• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 package com.android.documentsui.inspector;
17 
18 import static android.provider.DocumentsContract.Document.FLAG_SUPPORTS_SETTINGS;
19 import static com.android.internal.util.Preconditions.checkArgument;
20 
21 import android.app.Activity;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.pm.PackageManager;
25 import android.graphics.drawable.Drawable;
26 import android.net.Uri;
27 import android.provider.DocumentsContract;
28 import android.support.annotation.Nullable;
29 import android.support.annotation.VisibleForTesting;
30 import android.view.View;
31 import android.view.View.OnClickListener;
32 import com.android.documentsui.DocumentsApplication;
33 import com.android.documentsui.ProviderExecutor;
34 import com.android.documentsui.R;
35 import com.android.documentsui.base.DocumentInfo;
36 import com.android.documentsui.base.Lookup;
37 import com.android.documentsui.inspector.actions.Action;
38 import com.android.documentsui.inspector.actions.ClearDefaultAppAction;
39 import com.android.documentsui.inspector.actions.ShowInProviderAction;
40 import com.android.documentsui.roots.ProvidersAccess;
41 import com.android.documentsui.ui.Snackbars;
42 import java.util.concurrent.Executor;
43 import java.util.function.Consumer;
44 /**
45  * A controller that coordinates retrieving document information and sending it to the view.
46  */
47 public final class InspectorController {
48 
49     private final Loader mLoader;
50     private final Consumer<DocumentInfo> mHeader;
51     private final DetailsDisplay mDetails;
52     private final ActionDisplay mShowProvider;
53     private final ActionDisplay mAppDefaults;
54     private final Consumer<DocumentInfo> mDebugView;
55     private final boolean mShowDebug;
56     private final Context mContext;
57     private final PackageManager mPackageManager;
58     private final ProvidersAccess mProviders;
59     private final Runnable mShowSnackbar;
60     private final Lookup<String, Executor> mExecutors;
61 
62     /**
63      * InspectorControllerTest relies on this controller.
64      */
65     @VisibleForTesting
InspectorController(Context context, Loader loader, PackageManager pm, ProvidersAccess providers, boolean showDebug, Consumer<DocumentInfo> header, DetailsDisplay details, ActionDisplay showProvider, ActionDisplay appDefaults, Consumer<DocumentInfo> debugView, Lookup<String, Executor> executors, Runnable showSnackbar)66     public InspectorController(Context context, Loader loader, PackageManager pm,
67             ProvidersAccess providers, boolean showDebug, Consumer<DocumentInfo> header,
68             DetailsDisplay details, ActionDisplay showProvider, ActionDisplay appDefaults,
69             Consumer<DocumentInfo> debugView, Lookup<String, Executor> executors,
70             Runnable showSnackbar) {
71 
72         checkArgument(context != null);
73         checkArgument(loader != null);
74         checkArgument(pm != null);
75         checkArgument(providers != null);
76         checkArgument(header != null);
77         checkArgument(details != null);
78         checkArgument(showProvider != null);
79         checkArgument(appDefaults != null);
80         checkArgument(debugView != null);
81         checkArgument(showSnackbar != null);
82         checkArgument(executors != null);
83 
84         mContext = context;
85         mLoader = loader;
86         mPackageManager = pm;
87         mShowDebug = showDebug;
88         mProviders = providers;
89         mHeader = header;
90         mDetails = details;
91         mShowProvider = showProvider;
92         mAppDefaults = appDefaults;
93         mDebugView = debugView;
94         mExecutors = executors;
95         mShowSnackbar = showSnackbar;
96     }
97 
InspectorController(Activity activity, Loader loader, View layout, boolean showDebug)98     public InspectorController(Activity activity, Loader loader, View layout, boolean showDebug) {
99 
100         this(activity,
101                 loader,
102                 activity.getPackageManager(),
103                 DocumentsApplication.getProvidersCache (activity),
104                 showDebug,
105                 (HeaderView) layout.findViewById(R.id.inspector_header_view),
106                 (DetailsView) layout.findViewById(R.id.inspector_details_view),
107                 (ActionDisplay) layout.findViewById(R.id.inspector_show_in_provider_view),
108                 (ActionDisplay) layout.findViewById(R.id.inspector_app_defaults_view),
109                 (DebugView) layout.findViewById(R.id.inspector_debug_view),
110                 ProviderExecutor::forAuthority,
111                 () -> {
112                     // using a runnable to support unit testing this feature.
113                     Snackbars.showInspectorError(activity);
114                 }
115         );
116         if (showDebug) {
117             layout.findViewById(R.id.inspector_debug_view).setVisibility(View.VISIBLE);
118         }
119     }
120 
reset()121     public void reset() {
122         mLoader.reset();
123     }
124 
loadInfo(Uri uri)125     public void loadInfo(Uri uri) {
126         mLoader.loadDocInfo(uri, this::updateView);
127     }
128 
129     /**
130      * Updates the view with documentInfo.
131      */
132     @Nullable
updateView(@ullable DocumentInfo docInfo)133     public void updateView(@Nullable DocumentInfo docInfo) {
134 
135         if (docInfo == null) {
136             mShowSnackbar.run();
137         } else {
138             mHeader.accept(docInfo);
139             mDetails.accept(docInfo);
140 
141             if (docInfo.isDirectory()) {
142                 mLoader.loadDirCount(docInfo, this::displayChildCount);
143             } else {
144 
145                 mShowProvider.setVisible(docInfo.isSettingsSupported());
146                 if (docInfo.isSettingsSupported()) {
147                     Action showProviderAction =
148                         new ShowInProviderAction(mContext, mPackageManager, docInfo, mProviders);
149                     mShowProvider.init(
150                             showProviderAction,
151                             (view) -> {
152                                 showInProvider(docInfo.derivedUri);
153                             });
154                 }
155 
156                 Action defaultAction =
157                         new ClearDefaultAppAction(mContext, mPackageManager, docInfo);
158 
159                 mAppDefaults.setVisible(defaultAction.canPerformAction());
160                 if (defaultAction.canPerformAction()) {
161                     mAppDefaults.init(
162                             defaultAction,
163                             (View) -> {
164                                 clearDefaultApp(defaultAction.getPackageName());
165                             });
166                 }
167             }
168 
169             if (mShowDebug) {
170                 mDebugView.accept(docInfo);
171             }
172         }
173     }
174 
175     /**
176      * Displays a directory's information to the view.
177      *
178      * @param count - number of items in the directory.
179      */
displayChildCount(Integer count)180     private void displayChildCount(Integer count) {
181         mDetails.setChildrenCount(count);
182     }
183 
184     /**
185      * Shows the selected document in it's content provider.
186      *
187      * @param DocumentInfo whose flag FLAG_SUPPORTS_SETTINGS is set.
188      */
showInProvider(Uri uri)189     public void showInProvider(Uri uri) {
190 
191         Intent intent = new Intent(DocumentsContract.ACTION_DOCUMENT_SETTINGS);
192         intent.setPackage(mProviders.getPackageName(uri.getAuthority()));
193         intent.addCategory(Intent.CATEGORY_DEFAULT);
194         intent.setData(uri);
195         mContext.startActivity(intent);
196     }
197 
198     /**
199      * Clears the default app that's opens that file type.
200      *
201      * @param packageName of the preferred app.
202      */
clearDefaultApp(String packageName)203     public void clearDefaultApp(String packageName) {
204         assert packageName != null;
205         mPackageManager.clearPackagePreferredActivities(packageName);
206 
207         mAppDefaults.setAppIcon(null);
208         mAppDefaults.setAppName(mContext.getString(R.string.handler_app_not_selected));
209         mAppDefaults.showAction(false);
210     }
211 
212     /**
213      * Interface for loading document metadata.
214      */
215     public interface Loader {
216 
217         /**
218          * Starts the Asynchronous process of loading file data.
219          *
220          * @param uri - A content uri to query metadata from.
221          * @param callback - Function to be called when the loader has finished loading metadata. A
222          * DocumentInfo will be sent to this method. DocumentInfo may be null.
223          */
loadDocInfo(Uri uri, Consumer<DocumentInfo> callback)224         void loadDocInfo(Uri uri, Consumer<DocumentInfo> callback);
225 
226         /**
227          * Loads a folders item count.
228          * @param directory - a documentInfo thats a directory.
229          * @param callback - Function to be called when the loader has finished loading the number
230          * of children.
231          */
loadDirCount(DocumentInfo directory, Consumer<Integer> callback)232         void loadDirCount(DocumentInfo directory, Consumer<Integer> callback);
233 
234         /**
235          * Deletes all loader id's when android lifecycle ends.
236          */
reset()237         void reset();
238     }
239 
240     /**
241      * This interface is for unit testing.
242      */
243     public interface ActionDisplay {
244 
245         /**
246          * Initializes the view based on the action.
247          * @param action - ClearDefaultAppAction or ShowInProviderAction
248          * @param listener - listener for when the action is pressed.
249          */
init(Action action, OnClickListener listener)250         void init(Action action, OnClickListener listener);
251 
252         /**
253          * Makes the action visible.
254          */
setVisible(boolean visible)255         void setVisible(boolean visible);
256 
setActionHeader(String header)257         void setActionHeader(String header);
258 
setAppIcon(Drawable icon)259         void setAppIcon(Drawable icon);
260 
setAppName(String name)261         void setAppName(String name);
262 
showAction(boolean visible)263         void showAction(boolean visible);
264     }
265 
266     /**
267      * Provides details about a file.
268      */
269     public interface DetailsDisplay {
270 
accept(DocumentInfo info)271         void accept(DocumentInfo info);
272 
setChildrenCount(int count)273         void setChildrenCount(int count);
274     }
275 }