1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.documentsui;
18 
19 import android.app.Activity;
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.net.Uri;
25 
26 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
27 
28 import com.android.documentsui.AbstractActionHandler.CommonAddons;
29 import com.android.documentsui.base.DocumentInfo;
30 import com.android.documentsui.base.PairedTask;
31 import com.android.documentsui.base.RootInfo;
32 import com.android.documentsui.base.State;
33 import com.android.documentsui.dirlist.AnimationView;
34 import com.android.documentsui.queries.SearchViewManager;
35 import com.android.documentsui.roots.ProvidersAccess;
36 
37 import java.util.Collection;
38 
39 /**
40  * Monitors roots change and refresh the page when necessary.
41  */
42 final class RootsMonitor<T extends Activity & CommonAddons> {
43 
44     private final LocalBroadcastManager mManager;
45     private final BroadcastReceiver mReceiver;
46 
RootsMonitor( final T activity, final ActionHandler actions, final ProvidersAccess providers, final DocumentsAccess docs, final State state, final SearchViewManager searchMgr, final Runnable actionModeFinisher)47     RootsMonitor(
48             final T activity,
49             final ActionHandler actions,
50             final ProvidersAccess providers,
51             final DocumentsAccess docs,
52             final State state,
53             final SearchViewManager searchMgr,
54             final Runnable actionModeFinisher) {
55         mManager = LocalBroadcastManager.getInstance(activity);
56 
57         mReceiver = new BroadcastReceiver() {
58             @Override
59             public void onReceive(Context context, Intent intent) {
60                 new HandleRootsChangedTask<>(
61                         activity,
62                         actions,
63                         providers,
64                         docs,
65                         state,
66                         searchMgr,
67                         actionModeFinisher).execute(activity.getCurrentRoot());
68             }
69         };
70     }
71 
start()72     void start() {
73         mManager.registerReceiver(mReceiver, new IntentFilter(ProvidersAccess.BROADCAST_ACTION));
74     }
75 
stop()76     void stop() {
77         mManager.unregisterReceiver(mReceiver);
78     }
79 
80     private static class HandleRootsChangedTask<T extends Activity & CommonAddons>
81             extends PairedTask<T, RootInfo, RootInfo> {
82         private final ActionHandler mActions;
83         private final ProvidersAccess mProviders;
84         private final DocumentsAccess mDocs;
85         private final State mState;
86         private final SearchViewManager mSearchMgr;
87         private final Runnable mActionModeFinisher;
88 
89         private RootInfo mCurrentRoot;
90         private DocumentInfo mDefaultRootDocument;
91 
HandleRootsChangedTask( T activity, ActionHandler actions, ProvidersAccess providers, DocumentsAccess docs, State state, SearchViewManager searchMgr, Runnable actionModeFinisher)92         private HandleRootsChangedTask(
93                 T activity,
94                 ActionHandler actions,
95                 ProvidersAccess providers,
96                 DocumentsAccess docs,
97                 State state,
98                 SearchViewManager searchMgr,
99                 Runnable actionModeFinisher) {
100             super(activity);
101             mActions = actions;
102             mProviders = providers;
103             mDocs = docs;
104             mState = state;
105             mSearchMgr = searchMgr;
106             mActionModeFinisher = actionModeFinisher;
107         }
108 
109         @Override
run(RootInfo... roots)110         protected RootInfo run(RootInfo... roots) {
111             assert (roots.length == 1);
112             mCurrentRoot = roots[0];
113             final Collection<RootInfo> cachedRoots = mProviders.getRootsBlocking();
114             for (final RootInfo root : cachedRoots) {
115                 if (root.getUri().equals(mCurrentRoot.getUri())) {
116                     // We don't need to change the current root as the current root was not removed.
117                     return null;
118                 }
119             }
120 
121             // Choose the default root.
122             final RootInfo defaultRoot = mProviders.getDefaultRootBlocking(mState);
123             assert (defaultRoot != null);
124             if (!defaultRoot.isRecents()) {
125                 mDefaultRootDocument = mDocs.getRootDocument(defaultRoot);
126             }
127             return defaultRoot;
128         }
129 
130         @Override
finish(RootInfo defaultRoot)131         protected void finish(RootInfo defaultRoot) {
132             if (defaultRoot == null) {
133                 return;
134             }
135 
136             // If the activity has been launched for the specific root and it is removed, finish the
137             // activity.
138             final Uri uri = mOwner.getIntent().getData();
139             if (uri != null && uri.equals(mCurrentRoot.getUri())) {
140                 mOwner.finishAndRemoveTask();
141                 return;
142             }
143 
144             // Clean action mode before changing root.
145             mActionModeFinisher.run();
146 
147             // Clear entire backstack and start in new root.
148             mState.stack.changeRoot(defaultRoot);
149             mSearchMgr.update(mState.stack);
150 
151             if (defaultRoot.isRecents()) {
152                 mOwner.refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
153             } else {
154                 mActions.openContainerDocument(mDefaultRootDocument);
155             }
156         }
157     }
158 }
159