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 }