Merge "Removed bugreport intents from protected list." into nyc-dev
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 8717353..837ceb6 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -268,154 +268,155 @@
return mClassLoader;
}
- if (mIncludeCode && !mPackageName.equals("android")) {
- // Avoid the binder call when the package is the current application package.
- // The activity manager will perform ensure that dexopt is performed before
- // spinning up the process.
- if (!Objects.equals(mPackageName, ActivityThread.currentPackageName())) {
- final String isa = VMRuntime.getRuntime().vmInstructionSet();
- try {
- ActivityThread.getPackageManager().notifyPackageUse(mPackageName);
- } catch (RemoteException re) {
- // Ignored.
- }
- }
-
- final List<String> zipPaths = new ArrayList<>();
- final List<String> apkPaths = new ArrayList<>();
- final List<String> libPaths = new ArrayList<>();
-
- if (mRegisterPackage) {
- try {
- ActivityManagerNative.getDefault().addPackageDependency(mPackageName);
- } catch (RemoteException e) {
- }
- }
-
- zipPaths.add(mAppDir);
- if (mSplitAppDirs != null) {
- Collections.addAll(zipPaths, mSplitAppDirs);
- }
-
- libPaths.add(mLibDir);
-
- /*
- * The following is a bit of a hack to inject
- * instrumentation into the system: If the app
- * being started matches one of the instrumentation names,
- * then we combine both the "instrumentation" and
- * "instrumented" app into the path, along with the
- * concatenation of both apps' shared library lists.
- */
-
- String instrumentationPackageName = mActivityThread.mInstrumentationPackageName;
- String instrumentationAppDir = mActivityThread.mInstrumentationAppDir;
- String[] instrumentationSplitAppDirs = mActivityThread.mInstrumentationSplitAppDirs;
- String instrumentationLibDir = mActivityThread.mInstrumentationLibDir;
-
- String instrumentedAppDir = mActivityThread.mInstrumentedAppDir;
- String[] instrumentedSplitAppDirs = mActivityThread.mInstrumentedSplitAppDirs;
- String instrumentedLibDir = mActivityThread.mInstrumentedLibDir;
- String[] instrumentationLibs = null;
-
- if (mAppDir.equals(instrumentationAppDir)
- || mAppDir.equals(instrumentedAppDir)) {
- zipPaths.clear();
- zipPaths.add(instrumentationAppDir);
- if (instrumentationSplitAppDirs != null) {
- Collections.addAll(zipPaths, instrumentationSplitAppDirs);
- }
- zipPaths.add(instrumentedAppDir);
- if (instrumentedSplitAppDirs != null) {
- Collections.addAll(zipPaths, instrumentedSplitAppDirs);
- }
-
- libPaths.clear();
- libPaths.add(instrumentationLibDir);
- libPaths.add(instrumentedLibDir);
-
- if (!instrumentedAppDir.equals(instrumentationAppDir)) {
- instrumentationLibs = getLibrariesFor(instrumentationPackageName);
- }
- }
-
- apkPaths.addAll(zipPaths);
-
- if (mSharedLibraries != null) {
- for (String lib : mSharedLibraries) {
- if (!zipPaths.contains(lib)) {
- zipPaths.add(0, lib);
- }
- }
- }
-
- if (instrumentationLibs != null) {
- for (String lib : instrumentationLibs) {
- if (!zipPaths.contains(lib)) {
- zipPaths.add(0, lib);
- }
- }
- }
-
- final String zip = TextUtils.join(File.pathSeparator, zipPaths);
-
- // Add path to libraries in apk for current abi
- if (mApplicationInfo.primaryCpuAbi != null) {
- for (String apk : apkPaths) {
- libPaths.add(apk + "!/lib/" + mApplicationInfo.primaryCpuAbi);
- }
- }
-
- String libraryPermittedPath = mDataDir;
- boolean isBundledApp = false;
-
- if (mApplicationInfo.isSystemApp()) {
- isBundledApp = true;
- // Add path to system libraries to libPaths;
- // Access to system libs should be limited
- // to bundled applications; this is why updated
- // system apps are not included.
- libPaths.add(System.getProperty("java.library.path"));
-
- // This is necessary to grant bundled apps access to
- // libraries located in subdirectories of /system/lib
- libraryPermittedPath += File.pathSeparator +
- System.getProperty("java.library.path");
- }
- // DO NOT SHIP: this is a workaround for apps loading native libraries
- // provided by 3rd party apps using absolute path instead of corresponding
- // classloader; see http://b/26954419 for example.
- if (mApplicationInfo.targetSdkVersion <= 23) {
- libraryPermittedPath += File.pathSeparator + "/data/app";
- }
- // -----------------------------------------------------------------------------
-
- final String librarySearchPath = TextUtils.join(File.pathSeparator, libPaths);
-
- /*
- * With all the combination done (if necessary, actually
- * create the class loader.
- */
-
- if (ActivityThread.localLOGV)
- Slog.v(ActivityThread.TAG, "Class path: " + zip +
- ", JNI path: " + librarySearchPath);
-
- // Temporarily disable logging of disk reads on the Looper thread
- // as this is early and necessary.
- StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
-
- mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, isBundledApp,
- librarySearchPath, libraryPermittedPath, mBaseClassLoader);
-
- StrictMode.setThreadPolicy(oldPolicy);
- } else {
+ if (mPackageName.equals("android")) {
if (mBaseClassLoader == null) {
mClassLoader = ClassLoader.getSystemClassLoader();
} else {
mClassLoader = mBaseClassLoader;
}
+ return mClassLoader;
}
+
+ // Avoid the binder call when the package is the current application package.
+ // The activity manager will perform ensure that dexopt is performed before
+ // spinning up the process.
+ if (!Objects.equals(mPackageName, ActivityThread.currentPackageName())) {
+ final String isa = VMRuntime.getRuntime().vmInstructionSet();
+ try {
+ ActivityThread.getPackageManager().notifyPackageUse(mPackageName);
+ } catch (RemoteException re) {
+ // Ignored.
+ }
+ }
+
+ final List<String> zipPaths = new ArrayList<>();
+ final List<String> apkPaths = new ArrayList<>();
+ final List<String> libPaths = new ArrayList<>();
+
+ if (mRegisterPackage) {
+ try {
+ ActivityManagerNative.getDefault().addPackageDependency(mPackageName);
+ } catch (RemoteException e) {
+ }
+ }
+
+ zipPaths.add(mAppDir);
+ if (mSplitAppDirs != null) {
+ Collections.addAll(zipPaths, mSplitAppDirs);
+ }
+
+ libPaths.add(mLibDir);
+
+ /*
+ * The following is a bit of a hack to inject
+ * instrumentation into the system: If the app
+ * being started matches one of the instrumentation names,
+ * then we combine both the "instrumentation" and
+ * "instrumented" app into the path, along with the
+ * concatenation of both apps' shared library lists.
+ */
+
+ String instrumentationPackageName = mActivityThread.mInstrumentationPackageName;
+ String instrumentationAppDir = mActivityThread.mInstrumentationAppDir;
+ String[] instrumentationSplitAppDirs = mActivityThread.mInstrumentationSplitAppDirs;
+ String instrumentationLibDir = mActivityThread.mInstrumentationLibDir;
+
+ String instrumentedAppDir = mActivityThread.mInstrumentedAppDir;
+ String[] instrumentedSplitAppDirs = mActivityThread.mInstrumentedSplitAppDirs;
+ String instrumentedLibDir = mActivityThread.mInstrumentedLibDir;
+ String[] instrumentationLibs = null;
+
+ if (mAppDir.equals(instrumentationAppDir)
+ || mAppDir.equals(instrumentedAppDir)) {
+ zipPaths.clear();
+ zipPaths.add(instrumentationAppDir);
+ if (instrumentationSplitAppDirs != null) {
+ Collections.addAll(zipPaths, instrumentationSplitAppDirs);
+ }
+ zipPaths.add(instrumentedAppDir);
+ if (instrumentedSplitAppDirs != null) {
+ Collections.addAll(zipPaths, instrumentedSplitAppDirs);
+ }
+
+ libPaths.clear();
+ libPaths.add(instrumentationLibDir);
+ libPaths.add(instrumentedLibDir);
+
+ if (!instrumentedAppDir.equals(instrumentationAppDir)) {
+ instrumentationLibs = getLibrariesFor(instrumentationPackageName);
+ }
+ }
+
+ apkPaths.addAll(zipPaths);
+
+ if (mSharedLibraries != null) {
+ for (String lib : mSharedLibraries) {
+ if (!zipPaths.contains(lib)) {
+ zipPaths.add(0, lib);
+ }
+ }
+ }
+
+ if (instrumentationLibs != null) {
+ for (String lib : instrumentationLibs) {
+ if (!zipPaths.contains(lib)) {
+ zipPaths.add(0, lib);
+ }
+ }
+ }
+
+ final String zip = mIncludeCode ? TextUtils.join(File.pathSeparator, zipPaths) : "";
+
+ // Add path to libraries in apk for current abi
+ if (mApplicationInfo.primaryCpuAbi != null) {
+ for (String apk : apkPaths) {
+ libPaths.add(apk + "!/lib/" + mApplicationInfo.primaryCpuAbi);
+ }
+ }
+
+ String libraryPermittedPath = mDataDir;
+ boolean isBundledApp = false;
+
+ if (mApplicationInfo.isSystemApp()) {
+ isBundledApp = true;
+ // Add path to system libraries to libPaths;
+ // Access to system libs should be limited
+ // to bundled applications; this is why updated
+ // system apps are not included.
+ libPaths.add(System.getProperty("java.library.path"));
+
+ // This is necessary to grant bundled apps access to
+ // libraries located in subdirectories of /system/lib
+ libraryPermittedPath += File.pathSeparator +
+ System.getProperty("java.library.path");
+ }
+ // DO NOT SHIP: this is a workaround for apps loading native libraries
+ // provided by 3rd party apps using absolute path instead of corresponding
+ // classloader; see http://b/26954419 for example.
+ if (mApplicationInfo.targetSdkVersion <= 23) {
+ libraryPermittedPath += File.pathSeparator + "/data/app";
+ }
+ // -----------------------------------------------------------------------------
+
+ final String librarySearchPath = TextUtils.join(File.pathSeparator, libPaths);
+
+ /*
+ * With all the combination done (if necessary, actually
+ * create the class loader.
+ */
+
+ if (ActivityThread.localLOGV)
+ Slog.v(ActivityThread.TAG, "Class path: " + zip +
+ ", JNI path: " + librarySearchPath);
+
+ // Temporarily disable logging of disk reads on the Looper thread
+ // as this is early and necessary.
+ StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+
+ mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, isBundledApp,
+ librarySearchPath, libraryPermittedPath, mBaseClassLoader);
+
+ StrictMode.setThreadPolicy(oldPolicy);
return mClassLoader;
}
}
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index aa14cb5..3c7f48b 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -166,7 +166,8 @@
String path = classLoader.findLibrary(libname);
if (path == null) {
- throw new IllegalArgumentException("Unable to find native library: " + libname);
+ throw new IllegalArgumentException("Unable to find native library " + libname +
+ " using classloader: " + classLoader.toString());
}
byte[] nativeSavedState = savedInstanceState != null
diff --git a/core/java/android/security/net/config/ResourceCertificateSource.java b/core/java/android/security/net/config/ResourceCertificateSource.java
index 8803c4b..22fbee2 100644
--- a/core/java/android/security/net/config/ResourceCertificateSource.java
+++ b/core/java/android/security/net/config/ResourceCertificateSource.java
@@ -44,7 +44,7 @@
public ResourceCertificateSource(int resourceId, Context context) {
mResourceId = resourceId;
- mContext = context.getApplicationContext();
+ mContext = context;
}
private void ensureInitialized() {
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 294edb6..bd71e0d 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -160,6 +160,10 @@
}
if (!mStagingRequests.empty()) {
+ // No interpolator was set, use the default
+ if (mPlayState == PlayState::NotStarted && !mInterpolator) {
+ mInterpolator.reset(Interpolator::createDefaultInterpolator());
+ }
// Keep track of the play state and play time before they are changed when
// staging requests are resolved.
nsecs_t currentPlayTime = mPlayTime;
@@ -222,10 +226,6 @@
// Set to 0 so that the animate() basically instantly finishes
mStartTime = 0;
}
- // No interpolator was set, use the default
- if (!mInterpolator) {
- mInterpolator.reset(Interpolator::createDefaultInterpolator());
- }
if (mDuration < 0 || mDuration > 50000) {
ALOGW("Your duration is strange and confusing: %" PRId64, mDuration);
}
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 58e7709..637e06e 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.documentsui">
+ <uses-permission android:name="android.permission.GET_APP_GRANTED_URI_PERMISSIONS" />
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
<uses-permission android:name="android.permission.REMOVE_TASKS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index b67a6915..a34dd55 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -23,9 +23,11 @@
import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_SIDE;
import static com.android.internal.util.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
import android.app.Activity;
import android.app.Fragment;
+import android.app.FragmentManager;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -46,7 +48,7 @@
import android.view.MenuItem;
import android.widget.Spinner;
-import com.android.documentsui.SearchManager.SearchManagerListener;
+import com.android.documentsui.SearchViewManager.SearchManagerListener;
import com.android.documentsui.State.ViewMode;
import com.android.documentsui.dirlist.DirectoryFragment;
import com.android.documentsui.dirlist.Model;
@@ -64,14 +66,12 @@
public abstract class BaseActivity extends Activity
implements SearchManagerListener, NavigationView.Environment {
- static final String EXTRA_STATE = "state";
-
// See comments where this const is referenced for details.
private static final int DRAWER_NO_FIDDLE_DELAY = 1500;
State mState;
RootsCache mRoots;
- SearchManager mSearchManager;
+ SearchViewManager mSearchManager;
DrawerController mDrawer;
NavigationView mNavigator;
@@ -121,7 +121,7 @@
}
});
- mSearchManager = new SearchManager(this);
+ mSearchManager = new SearchViewManager(this, icicle);
DocumentsToolbar toolbar = (DocumentsToolbar) findViewById(R.id.toolbar);
setActionBar(toolbar);
@@ -141,6 +141,7 @@
boolean showMenu = super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.activity, menu);
+ mNavigator.update();
mSearchManager.install((DocumentsToolbar) findViewById(R.id.toolbar));
return showMenu;
@@ -188,7 +189,7 @@
private State getState(@Nullable Bundle icicle) {
if (icicle != null) {
- State state = icicle.<State>getParcelable(EXTRA_STATE);
+ State state = icicle.<State>getParcelable(Shared.EXTRA_STATE);
if (DEBUG) Log.d(mTag, "Recovered existing state object: " + state);
return state;
}
@@ -224,6 +225,9 @@
}
void onRootPicked(RootInfo root) {
+ // Clicking on the current root removes search
+ mSearchManager.cancelSearch();
+
// Skip refreshing if root nor directory didn't change
if (root.equals(getCurrentRoot()) && mState.stack.size() == 1) {
return;
@@ -233,7 +237,6 @@
// Clear entire backstack and start in new root
mState.onRootChanged(root);
- mSearchManager.update(root);
// Recents is always in memory, so we just load it directly.
// Otherwise we delegate loading data from disk to a task
@@ -370,18 +373,18 @@
* e.g. The current directory name displayed on the action bar won't get updated.
*/
@Override
- public void onSearchChanged() {
- refreshDirectory(ANIM_NONE);
+ public void onSearchChanged(@Nullable String query) {
+ // We should not get here if root is not searchable
+ checkState(canSearchRoot());
+ reloadSearch(query);
}
- /**
- * Called when search query changed.
- * Updates the state object.
- * @param query - New query
- */
- @Override
- public void onSearchQueryChanged(String query) {
- mState.currentSearch = query;
+ private void reloadSearch(String query) {
+ FragmentManager fm = getFragmentManager();
+ RootInfo root = getCurrentRoot();
+ DocumentInfo cwd = getCurrentDirectory();
+
+ DirectoryFragment.reloadSearch(fm, root, cwd, query);
}
final List<String> getExcludedAuthorities() {
@@ -486,7 +489,8 @@
@Override
protected void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
- state.putParcelable(EXTRA_STATE, mState);
+ state.putParcelable(Shared.EXTRA_STATE, mState);
+ mSearchManager.onSaveInstanceState(state);
}
@Override
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
index b0542b9..13b7b14 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
@@ -53,19 +53,22 @@
private final RootInfo mRoot;
private final Uri mUri;
private final int mUserSortOrder;
+ private final boolean mSearchMode;
private DocumentInfo mDoc;
private CancellationSignal mSignal;
private DirectoryResult mResult;
+
public DirectoryLoader(Context context, int type, RootInfo root, DocumentInfo doc, Uri uri,
- int userSortOrder) {
+ int userSortOrder, boolean inSearchMode) {
super(context, ProviderExecutor.forAuthority(root.authority));
mType = type;
mRoot = root;
mUri = uri;
mUserSortOrder = userSortOrder;
mDoc = doc;
+ mSearchMode = inSearchMode;
}
@Override
@@ -83,7 +86,7 @@
final DirectoryResult result = new DirectoryResult();
// Use default document when searching
- if (mType == DirectoryFragment.TYPE_SEARCH) {
+ if (mSearchMode) {
final Uri docUri = DocumentsContract.buildDocumentUri(
mRoot.authority, mRoot.documentId);
try {
@@ -106,7 +109,7 @@
}
// Search always uses ranking from provider
- if (mType == DirectoryFragment.TYPE_SEARCH) {
+ if (mSearchMode) {
result.sortOrder = State.SORT_ORDER_UNKNOWN;
}
@@ -127,7 +130,7 @@
cursor = new RootCursorWrapper(mUri.getAuthority(), mRoot.rootId, cursor, -1);
- if (mType == DirectoryFragment.TYPE_SEARCH) {
+ if (mSearchMode) {
// Filter directories out of search results, for now
cursor = new FilteringCursorWrapper(cursor, null, SEARCH_REJECT_MIMES);
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index ec7dde9..9812495 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -290,13 +290,8 @@
mState.derivedMode = visualMimes ? State.MODE_GRID : State.MODE_LIST;
}
} else {
- if (mSearchManager.isSearching()) {
- // Ongoing search
- DirectoryFragment.showSearch(fm, root, mState.currentSearch, anim);
- } else {
// Normal boring directory
DirectoryFragment.showDirectory(fm, root, cwd, anim);
- }
}
// Forget any replacement target
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java
index f7a45e2..9609dee 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java
@@ -16,8 +16,10 @@
package com.android.documentsui;
+import static com.android.documentsui.Shared.DEBUG;
import static com.android.documentsui.State.ACTION_MANAGE;
import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
+import static com.android.internal.util.Preconditions.checkState;
import android.app.Activity;
import android.app.Fragment;
@@ -119,17 +121,14 @@
final RootInfo root = getCurrentRoot();
final DocumentInfo cwd = getCurrentDirectory();
+ if (DEBUG) checkState(!mSearchManager.isSearching());
+
// If started in manage roots mode, there has to be a cwd (i.e. the root dir of the managed
// root).
Preconditions.checkNotNull(cwd);
- if (mState.currentSearch != null) {
- // Ongoing search
- DirectoryFragment.showSearch(fm, root, mState.currentSearch, anim);
- } else {
- // Normal boring directory
- DirectoryFragment.showDirectory(fm, root, cwd, anim);
- }
+ // Normal boring directory
+ DirectoryFragment.showDirectory(fm, root, cwd, anim);
}
@Override
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index c0faba3..b8b50e4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -82,7 +82,6 @@
if (mState.restored) {
if (DEBUG) Log.d(TAG, "Stack already resolved for uri: " + intent.getData());
- refreshCurrentRootAndDirectory(ANIM_NONE);
} else if (!mState.stack.isEmpty()) {
// If a non-empty stack is present in our state it was read (presumably)
// from EXTRA_STACK intent extra. In this case, we'll skip other means of
@@ -248,16 +247,13 @@
final RootInfo root = getCurrentRoot();
final DocumentInfo cwd = getCurrentDirectory();
+ if (DEBUG) checkState(!mSearchManager.isSearching());
+
if (cwd == null) {
DirectoryFragment.showRecentsOpen(fm, anim);
} else {
- if (mState.currentSearch != null) {
- // Ongoing search
- DirectoryFragment.showSearch(fm, root, mState.currentSearch, anim);
- } else {
- // Normal boring directory
- DirectoryFragment.showDirectory(fm, root, cwd, anim);
- }
+ // Normal boring directory
+ DirectoryFragment.showDirectory(fm, root, cwd, anim);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java b/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java
index 025faea..27d6797 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java
@@ -22,6 +22,7 @@
import static com.android.documentsui.Shared.DEBUG;
import android.app.Activity;
+import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
@@ -33,6 +34,7 @@
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
+import android.content.UriPermission;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
@@ -62,6 +64,8 @@
private static final String EXTRA_APP_LABEL = "com.android.documentsui.APP_LABEL";
private static final String EXTRA_VOLUME_LABEL = "com.android.documentsui.VOLUME_LABEL";
+ private ContentProviderClient mExternalStorageClient;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -98,12 +102,20 @@
}
}
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mExternalStorageClient != null) {
+ mExternalStorageClient.close();
+ }
+ }
+
/**
* Validates the given path (volume + directory) and display the appropriate dialog asking the
* user to grant access to it.
*/
- private static boolean showFragment(Activity activity, int userId, StorageVolume storageVolume,
- String directoryName) {
+ private static boolean showFragment(OpenExternalDirectoryActivity activity, int userId,
+ StorageVolume storageVolume, String directoryName) {
if (DEBUG)
Log.d(TAG, "showFragment() for volume " + storageVolume.dump() + ", directory "
+ directoryName + ", and user " + userId);
@@ -129,7 +141,7 @@
return false;
}
- // Gets volume label and converted path
+ // Gets volume label and converted path.
String volumeLabel = null;
final List<VolumeInfo> volumes = sm.getVolumes();
if (DEBUG) Log.d(TAG, "Number of volumes: " + volumes.size());
@@ -143,6 +155,15 @@
break;
}
}
+
+ // Checks if the user has granted the permission already.
+ final Intent intent = getIntentForExistingPermission(activity, file);
+ if (intent != null) {
+ activity.setResult(RESULT_OK, intent);
+ activity.finish();
+ return true;
+ }
+
if (volumeLabel == null) {
Log.e(TAG, "Could not get volume for " + file);
return false;
@@ -196,8 +217,7 @@
return volume.isVisibleForWrite(userId) && root.equals(path);
}
- private static Intent createGrantedUriPermissionsIntent(ContentProviderClient provider,
- File file) {
+ private static Uri getGrantedUriPermission(ContentProviderClient provider, File file) {
// Calls ExternalStorageProvider to get the doc id for the file
final Bundle bundle;
try {
@@ -218,8 +238,17 @@
Log.e(TAG, "Could not get URI for doc id " + docId);
return null;
}
-
if (DEBUG) Log.d(TAG, "URI for " + file + ": " + uri);
+ return uri;
+ }
+
+ private static Intent createGrantedUriPermissionsIntent(ContentProviderClient provider,
+ File file) {
+ final Uri uri = getGrantedUriPermission(provider, file);
+ return createGrantedUriPermissionsIntent(uri);
+ }
+
+ private static Intent createGrantedUriPermissionsIntent(Uri uri) {
final Intent intent = new Intent();
intent.setData(uri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
@@ -229,13 +258,31 @@
return intent;
}
+ private static Intent getIntentForExistingPermission(OpenExternalDirectoryActivity activity,
+ File file) {
+ final String packageName = activity.getCallingPackage();
+ final Uri grantedUri = getGrantedUriPermission(activity.getExternalStorageClient(), file);
+ if (DEBUG)
+ Log.d(TAG, "checking if " + packageName + " already has permission for " + grantedUri);
+ final ActivityManager am =
+ (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
+ for (UriPermission uriPermission : am.getGrantedUriPermissions(packageName).getList()) {
+ final Uri uri = uriPermission.getUri();
+ if (uri.equals(grantedUri)) {
+ if (DEBUG) Log.d(TAG, packageName + " already has permission: " + uriPermission);
+ return createGrantedUriPermissionsIntent(uri);
+ }
+ }
+ if (DEBUG) Log.d(TAG, packageName + " does not have permission for " + grantedUri);
+ return null;
+ }
+
public static class OpenExternalDirectoryDialogFragment extends DialogFragment {
private File mFile;
private String mVolumeLabel;
private String mAppLabel;
- private ContentProviderClient mExternalStorageClient;
- private ContentResolver mResolver;
+ private OpenExternalDirectoryActivity mActivity;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -245,16 +292,8 @@
mFile = new File(args.getString(EXTRA_FILE));
mVolumeLabel = args.getString(EXTRA_VOLUME_LABEL);
mAppLabel = args.getString(EXTRA_APP_LABEL);
- mResolver = getContext().getContentResolver();
}
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- if (mExternalStorageClient != null) {
- mExternalStorageClient.close();
- }
+ mActivity = (OpenExternalDirectoryActivity) getActivity();
}
@Override
@@ -267,8 +306,8 @@
public void onClick(DialogInterface dialog, int which) {
Intent intent = null;
if (which == DialogInterface.BUTTON_POSITIVE) {
- intent = createGrantedUriPermissionsIntent(getExternalStorageClient(),
- mFile);
+ intent = createGrantedUriPermissionsIntent(
+ mActivity.getExternalStorageClient(), mFile);
}
if (which == DialogInterface.BUTTON_NEGATIVE || intent == null) {
activity.setResult(RESULT_CANCELED);
@@ -297,13 +336,13 @@
activity.setResult(RESULT_CANCELED);
activity.finish();
}
+ }
- private synchronized ContentProviderClient getExternalStorageClient() {
- if (mExternalStorageClient == null) {
- mExternalStorageClient =
- mResolver.acquireContentProviderClient(EXTERNAL_STORAGE_AUTH);
- }
- return mExternalStorageClient;
+ private synchronized ContentProviderClient getExternalStorageClient() {
+ if (mExternalStorageClient == null) {
+ mExternalStorageClient =
+ getContentResolver().acquireContentProviderClient(EXTERNAL_STORAGE_AUTH);
}
+ return mExternalStorageClient;
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SearchManager.java b/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
similarity index 78%
rename from packages/DocumentsUI/src/com/android/documentsui/SearchManager.java
rename to packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
index 69f54c7..0496862 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SearchManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
@@ -16,6 +16,8 @@
package com.android.documentsui;
+import android.annotation.Nullable;
+import android.os.Bundle;
import android.provider.DocumentsContract.Root;
import android.text.TextUtils;
import android.util.Log;
@@ -31,28 +33,27 @@
/**
* Manages searching UI behavior.
*/
-final class SearchManager implements
+final class SearchViewManager implements
SearchView.OnCloseListener, OnQueryTextListener, OnClickListener, OnFocusChangeListener {
public interface SearchManagerListener {
- void onSearchChanged();
-
- void onSearchQueryChanged(String query);
+ void onSearchChanged(@Nullable String query);
}
public static final String TAG = "SearchManger";
private SearchManagerListener mListener;
- private String currentSearch;
private boolean mSearchExpanded;
+ private String mCurrentSearch;
private boolean mIgnoreNextClose;
private DocumentsToolbar mActionBar;
private MenuItem mMenu;
private SearchView mView;
- public SearchManager(SearchManagerListener listener) {
+ public SearchViewManager(SearchManagerListener listener, @Nullable Bundle savedState) {
mListener = listener;
+ mCurrentSearch = savedState != null ? savedState.getString(Shared.EXTRA_QUERY) : null;
}
public void setSearchMangerListener(SearchManagerListener listener) {
@@ -69,6 +70,8 @@
mView.setOnCloseListener(this);
mView.setOnSearchClickListener(this);
mView.setOnQueryTextFocusChangeListener(this);
+
+ restoreSearch();
}
/**
@@ -80,12 +83,12 @@
return;
}
- if (currentSearch != null) {
+ if (mCurrentSearch != null) {
mMenu.expandActionView();
mView.setIconified(false);
mView.clearFocus();
- mView.setQuery(currentSearch, false);
+ mView.setQuery(mCurrentSearch, false);
} else {
mView.clearFocus();
if (!mView.isIconified()) {
@@ -108,13 +111,11 @@
return;
}
- mMenu.setVisible(visible);
if (!visible) {
- currentSearch = null;
- if (mListener != null) {
- mListener.onSearchQueryChanged(currentSearch);
- }
+ mCurrentSearch = null;
}
+
+ mMenu.setVisible(visible);
}
/**
@@ -133,8 +134,23 @@
return false;
}
+ private void restoreSearch() {
+ if (isSearching()) {
+ onSearchExpanded();
+ mView.setIconified(false);
+ mView.setQuery(mCurrentSearch, false);
+ mView.clearFocus();
+ }
+ }
+
+ private void onSearchExpanded() {
+ mSearchExpanded = true;
+ mView.setBackgroundColor(
+ mView.getResources().getColor(R.color.menu_search_background, null));
+ }
+
boolean isSearching() {
- return currentSearch != null;
+ return mCurrentSearch != null;
}
boolean isExpanded() {
@@ -142,6 +158,14 @@
}
/**
+ * Called when owning activity is saving state to be used to restore state during creation.
+ * @param state Bundle to save state too
+ */
+ public void onSaveInstanceState(Bundle state) {
+ state.putString(Shared.EXTRA_QUERY, mCurrentSearch);
+ }
+
+ /**
* Clears the search. Clears the SearchView background color. Triggers refreshing of the
* directory content.
* @return True if the default behavior of clearing/dismissing SearchView should be overridden.
@@ -159,11 +183,10 @@
mView.getResources().getColor(android.R.color.transparent, null));
// Refresh the directory if a search was done
- if (currentSearch != null) {
- currentSearch = null;
+ if (mCurrentSearch != null) {
+ mCurrentSearch = null;
if (mListener != null) {
- mListener.onSearchQueryChanged(currentSearch);
- mListener.onSearchChanged();
+ mListener.onSearchChanged(mCurrentSearch);
}
}
return false;
@@ -176,18 +199,15 @@
*/
@Override
public void onClick(View v) {
- mSearchExpanded = true;
- mView.setBackgroundColor(
- mView.getResources().getColor(R.color.menu_search_background, null));
+ onSearchExpanded();
}
@Override
public boolean onQueryTextSubmit(String query) {
- currentSearch = query;
+ mCurrentSearch = query;
mView.clearFocus();
if (mListener != null) {
- mListener.onSearchQueryChanged(currentSearch);
- mListener.onSearchChanged();
+ mListener.onSearchChanged(mCurrentSearch);
}
return true;
}
@@ -195,7 +215,7 @@
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
- if (currentSearch == null) {
+ if (mCurrentSearch == null) {
mView.setIconified(true);
} else if (TextUtils.isEmpty(mView.getQuery())) {
cancelSearch();
@@ -207,4 +227,9 @@
public boolean onQueryTextChange(String newText) {
return false;
}
+
+ String getCurrentSearch() {
+ return mCurrentSearch;
+ }
+
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
index b90a119..a288fe8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Shared.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
@@ -37,10 +37,50 @@
* specifies if the destination directory needs to create new directory or not.
*/
public static final String EXTRA_DIRECTORY_COPY = "com.android.documentsui.DIRECTORY_COPY";
+ public static final String EXTRA_STACK = "com.android.documentsui.STACK";
+
+ /**
+ * Extra flag used to store query of type String in the bundle.
+ */
+ public static final String EXTRA_QUERY = "query";
+
+ /**
+ * Extra flag used to store state of type State in the bundle.
+ */
+ public static final String EXTRA_STATE = "state";
+
+ /**
+ * Extra flag used to store type of DirectoryFragment's type ResultType type in the bundle.
+ */
+ public static final String EXTRA_TYPE = "type";
+
+ /**
+ * Extra flag used to store root of type RootInfo in the bundle.
+ */
+ public static final String EXTRA_ROOT = "root";
+
+ /**
+ * Extra flag used to store document of DocumentInfo type in the bundle.
+ */
+ public static final String EXTRA_DOC = "document";
+
+ /**
+ * Extra flag used to store DirectoryFragment's selection of Selection type in the bundle.
+ */
+ public static final String EXTRA_SELECTION = "selection";
+
+ /**
+ * Extra flag used to store DirectoryFragment's search mode of boolean type in the bundle.
+ */
+ public static final String EXTRA_SEARCH_MODE = "searchMode";
+
+ /**
+ * Extra flag used to store DirectoryFragment's ignore state of boolean type in the bundle.
+ */
+ public static final String EXTRA_IGNORE_STATE = "ignoreState";
public static final boolean DEBUG = true;
public static final String TAG = "Documents";
- public static final String EXTRA_STACK = "com.android.documentsui.STACK";
/**
diff --git a/packages/DocumentsUI/src/com/android/documentsui/State.java b/packages/DocumentsUI/src/com/android/documentsui/State.java
index 0948ab1..2ecbdf6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/State.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/State.java
@@ -105,9 +105,6 @@
private boolean mInitialRootChanged;
private boolean mInitialDocChanged;
- /** Currently active search, overriding any stack. */
- public String currentSearch;
-
/** Instance state for every shown directory */
public HashMap<String, SparseArray<Parcelable>> dirState = new HashMap<>();
@@ -186,7 +183,6 @@
out.writeInt(showAdvanced ? 1 : 0);
out.writeInt(restored ? 1 : 0);
DurableUtils.writeToParcel(out, stack);
- out.writeString(currentSearch);
out.writeMap(dirState);
out.writeParcelable(selectedDocuments, 0);
out.writeList(selectedDocumentsForCopy);
@@ -217,7 +213,6 @@
state.showAdvanced = in.readInt() != 0;
state.restored = in.readInt() != 0;
DurableUtils.readFromParcel(in, state.stack);
- state.currentSearch = in.readString();
in.readMap(state.dirState, loader);
state.selectedDocuments = in.readParcelable(loader);
in.readList(state.selectedDocumentsForCopy, loader);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 4583dec..669eb71 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -113,19 +113,26 @@
/**
* Display the documents inside a single directory.
*/
-public class DirectoryFragment extends Fragment implements DocumentsAdapter.Environment {
+public class DirectoryFragment extends Fragment
+ implements DocumentsAdapter.Environment, LoaderCallbacks<DirectoryResult> {
@IntDef(flag = true, value = {
TYPE_NORMAL,
- TYPE_SEARCH,
TYPE_RECENT_OPEN
})
@Retention(RetentionPolicy.SOURCE)
public @interface ResultType {}
public static final int TYPE_NORMAL = 1;
- public static final int TYPE_SEARCH = 2;
- public static final int TYPE_RECENT_OPEN = 3;
+ public static final int TYPE_RECENT_OPEN = 2;
+ @IntDef(flag = true, value = {
+ ANIM_NONE,
+ ANIM_SIDE,
+ ANIM_LEAVE,
+ ANIM_ENTER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AnimationType {}
public static final int ANIM_NONE = 1;
public static final int ANIM_SIDE = 2;
public static final int ANIM_LEAVE = 3;
@@ -146,12 +153,6 @@
private static final int DELETE_JOB_DELAY = 5500;
private static final int EMPTY_REVEAL_DURATION = 250;
- private static final String EXTRA_TYPE = "type";
- private static final String EXTRA_ROOT = "root";
- private static final String EXTRA_DOC = "doc";
- private static final String EXTRA_QUERY = "query";
- private static final String EXTRA_IGNORE_STATE = "ignoreState";
-
private Model mModel;
private MultiSelectManager mSelectionManager;
private Model.UpdateListener mModelUpdateListener = new ModelUpdateListener();
@@ -164,12 +165,10 @@
private RecyclerView mRecView;
private ListeningGestureDetector mGestureDetector;
- private @ResultType int mType = TYPE_NORMAL;
private String mStateKey;
private int mLastSortOrder = SORT_ORDER_UNKNOWN;
private DocumentsAdapter mAdapter;
- private LoaderCallbacks<DirectoryResult> mCallbacks;
private FragmentTuner mTuner;
private DocumentClipper mClipper;
private GridLayoutManager mLayout;
@@ -178,6 +177,14 @@
private MessageBar mMessageBar;
private View mProgressBar;
+ // Directory fragment state is defined by: root, document, query, type, selection
+ private @ResultType int mType = TYPE_NORMAL;
+ private RootInfo mRoot;
+ private DocumentInfo mDocument;
+ private String mQuery = null;
+ private Selection mSelection = null;
+ private boolean mSearchMode = false;
+
@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@@ -226,9 +233,16 @@
final Context context = getActivity();
final State state = getDisplayState();
- final RootInfo root = getArguments().getParcelable(EXTRA_ROOT);
- final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC);
- mStateKey = buildStateKey(root, doc);
+ // Read arguments when object created for the first time.
+ // Restore state if fragment recreated.
+ Bundle args = savedInstanceState == null ? getArguments() : savedInstanceState;
+ mRoot = args.getParcelable(Shared.EXTRA_ROOT);
+ mDocument = args.getParcelable(Shared.EXTRA_DOC);
+ mStateKey = buildStateKey(mRoot, mDocument);
+ mQuery = args.getString(Shared.EXTRA_QUERY);
+ mType = args.getInt(Shared.EXTRA_TYPE);
+ mSelection = args.getParcelable(Shared.EXTRA_SELECTION);
+ mSearchMode = args.getBoolean(Shared.EXTRA_SEARCH_MODE);
mIconHelper = new IconHelper(context, MODE_GRID);
@@ -248,13 +262,6 @@
mRecView.addOnItemTouchListener(mGestureDetector);
- // final here because we'll manually bump the listener iwhen we had an initial selection,
- // but only after the model is fully loaded.
- final SelectionModeListener selectionListener = new SelectionModeListener();
- final Selection initialSelection = state.selectedDocuments.hasDirectoryKey(mStateKey)
- ? state.selectedDocuments
- : null;
-
// TODO: instead of inserting the view into the constructor, extract listener-creation code
// and set the listener on the view after the fact. Then the view doesn't need to be passed
// into the selection manager.
@@ -264,9 +271,9 @@
state.allowMultiple
? MultiSelectManager.MODE_MULTIPLE
: MultiSelectManager.MODE_SINGLE,
- initialSelection);
+ null);
- mSelectionManager.addCallback(selectionListener);
+ mSelectionManager.addCallback(new SelectionModeListener());
mModel = new Model();
mModel.addUpdateListener(mAdapter);
@@ -275,8 +282,6 @@
// Make sure this is done after the RecyclerView is set up.
mFocusManager = new FocusManager(context, mRecView, mModel);
- mType = getArguments().getInt(EXTRA_TYPE);
-
mTuner = FragmentTuner.pick(getContext(), state);
mClipper = new DocumentClipper(context);
@@ -286,7 +291,7 @@
hideGridTitles = MimePredicate.mimeMatches(
MimePredicate.VISUAL_MIMES, state.acceptMimes);
} else {
- hideGridTitles = (doc != null) && doc.isGridTitlesHidden();
+ hideGridTitles = (mDocument != null) && mDocument.isGridTitlesHidden();
}
GridDocumentHolder.setHideTitles(hideGridTitles);
@@ -295,86 +300,20 @@
boolean svelte = am.isLowRamDevice() && (mType == TYPE_RECENT_OPEN);
mIconHelper.setThumbnailsEnabled(!svelte);
- mCallbacks = new LoaderCallbacks<DirectoryResult>() {
- @Override
- public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) {
- final String query = getArguments().getString(EXTRA_QUERY);
-
- Uri contentsUri;
- switch (mType) {
- case TYPE_NORMAL:
- contentsUri = DocumentsContract.buildChildDocumentsUri(
- doc.authority, doc.documentId);
- if (state.action == ACTION_MANAGE) {
- contentsUri = DocumentsContract.setManageMode(contentsUri);
- }
- return new DirectoryLoader(
- context, mType, root, doc, contentsUri, state.userSortOrder);
- case TYPE_SEARCH:
- contentsUri = DocumentsContract.buildSearchDocumentsUri(
- root.authority, root.rootId, query);
- if (state.action == ACTION_MANAGE) {
- contentsUri = DocumentsContract.setManageMode(contentsUri);
- }
- return new DirectoryLoader(
- context, mType, root, doc, contentsUri, state.userSortOrder);
- case TYPE_RECENT_OPEN:
- final RootsCache roots = DocumentsApplication.getRootsCache(context);
- return new RecentsLoader(context, roots, state);
- default:
- throw new IllegalStateException("Unknown type " + mType);
- }
- }
-
- @Override
- public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
- if (!isAdded()) return;
-
- mModel.update(result);
- state.derivedSortOrder = result.sortOrder;
-
- updateDisplayState();
-
- if (initialSelection != null) {
- selectionListener.onSelectionChanged();
- }
-
- // Restore any previous instance state
- final SparseArray<Parcelable> container = state.dirState.remove(mStateKey);
- if (container != null && !getArguments().getBoolean(EXTRA_IGNORE_STATE, false)) {
- getView().restoreHierarchyState(container);
- } else if (mLastSortOrder != state.derivedSortOrder) {
- // The derived sort order takes the user sort order into account, but applies
- // directory-specific defaults when the user doesn't explicitly set the sort
- // order. Scroll to the top if the sort order actually changed.
- mRecView.smoothScrollToPosition(0);
- }
-
- mLastSortOrder = state.derivedSortOrder;
-
- mTuner.onModelLoaded(mModel, mType);
- }
-
- @Override
- public void onLoaderReset(Loader<DirectoryResult> loader) {
- mModel.update(null);
- }
- };
-
// Kick off loader at least once
- getLoaderManager().restartLoader(LOADER_ID, null, mCallbacks);
+ getLoaderManager().restartLoader(LOADER_ID, null, this);
}
@Override
public void onSaveInstanceState(Bundle outState) {
- State state = getDisplayState();
- if (mSelectionManager.hasSelection()) {
- mSelectionManager.getSelection(state.selectedDocuments);
- state.selectedDocuments.setDirectoryKey(mStateKey);
- if (!state.selectedDocuments.isEmpty()) {
- if (DEBUG) Log.d(TAG, "Persisted selection: " + state.selectedDocuments);
- }
- }
+ super.onSaveInstanceState(outState);
+
+ outState.putInt(Shared.EXTRA_TYPE, mType);
+ outState.putParcelable(Shared.EXTRA_ROOT, mRoot);
+ outState.putParcelable(Shared.EXTRA_DOC, mDocument);
+ outState.putString(Shared.EXTRA_QUERY, mQuery);
+ outState.putParcelable(Shared.EXTRA_SELECTION, mSelectionManager.getSelection());
+ outState.putBoolean(Shared.EXTRA_SEARCH_MODE, mSearchMode);
}
@Override
@@ -449,7 +388,7 @@
public void onSortOrderChanged() {
// Sort order is implemented as a sorting wrapper around directory
// results. So when sort order changes, we force a reload of the directory.
- getLoaderManager().restartLoader(LOADER_ID, null, mCallbacks);
+ getLoaderManager().restartLoader(LOADER_ID, null, this);
}
public void onViewModeChanged() {
@@ -1342,7 +1281,7 @@
mProgressBar.setVisibility(model.isLoading() ? View.VISIBLE : View.GONE);
if (model.isEmpty()) {
- if (getDisplayState().currentSearch != null) {
+ if (mSearchMode) {
showNoResults(getDisplayState().stack.root);
} else {
showEmptyDirectory();
@@ -1468,32 +1407,50 @@
public static void showDirectory(
FragmentManager fm, RootInfo root, DocumentInfo doc, int anim) {
- show(fm, TYPE_NORMAL, root, doc, null, anim);
- }
-
- public static void showSearch(FragmentManager fm, RootInfo root, String query, int anim) {
- show(fm, TYPE_SEARCH, root, null, query, anim);
+ create(fm, TYPE_NORMAL, root, doc, null, anim);
}
public static void showRecentsOpen(FragmentManager fm, int anim) {
- show(fm, TYPE_RECENT_OPEN, null, null, null, anim);
+ create(fm, TYPE_RECENT_OPEN, null, null, null, anim);
}
- private static void show(FragmentManager fm, int type, RootInfo root, DocumentInfo doc,
+ public static void reloadSearch(FragmentManager fm, RootInfo root, DocumentInfo doc,
+ String query) {
+ DirectoryFragment df = get(fm);
+
+ df.mQuery = query;
+ df.mRoot = root;
+ df.mDocument = doc;
+ df.mSearchMode = query != null;
+ df.getLoaderManager().restartLoader(LOADER_ID, null, df);
+ }
+
+ public static void reload(FragmentManager fm, int type, RootInfo root, DocumentInfo doc,
+ String query) {
+ DirectoryFragment df = get(fm);
+ df.mType = type;
+ df.mQuery = query;
+ df.mRoot = root;
+ df.mDocument = doc;
+ df.mSearchMode = query != null;
+ df.getLoaderManager().restartLoader(LOADER_ID, null, df);
+ }
+
+ public static void create(FragmentManager fm, int type, RootInfo root, DocumentInfo doc,
String query, int anim) {
final Bundle args = new Bundle();
- args.putInt(EXTRA_TYPE, type);
- args.putParcelable(EXTRA_ROOT, root);
- args.putParcelable(EXTRA_DOC, doc);
- args.putString(EXTRA_QUERY, query);
+ args.putInt(Shared.EXTRA_TYPE, type);
+ args.putParcelable(Shared.EXTRA_ROOT, root);
+ args.putParcelable(Shared.EXTRA_DOC, doc);
+ args.putString(Shared.EXTRA_QUERY, query);
final FragmentTransaction ft = fm.beginTransaction();
switch (anim) {
case ANIM_SIDE:
- args.putBoolean(EXTRA_IGNORE_STATE, true);
+ args.putBoolean(Shared.EXTRA_IGNORE_STATE, true);
break;
case ANIM_ENTER:
- args.putBoolean(EXTRA_IGNORE_STATE, true);
+ args.putBoolean(Shared.EXTRA_IGNORE_STATE, true);
ft.setCustomAnimations(R.animator.dir_enter, R.animator.dir_frozen);
break;
case ANIM_LEAVE:
@@ -1504,7 +1461,7 @@
final DirectoryFragment fragment = new DirectoryFragment();
fragment.setArguments(args);
- ft.replace(R.id.container_directory, fragment);
+ ft.replace(getFragmentId(), fragment);
ft.commitAllowingStateLoss();
}
@@ -1518,9 +1475,77 @@
public static @Nullable DirectoryFragment get(FragmentManager fm) {
// TODO: deal with multiple directories shown at once
- Fragment fragment = fm.findFragmentById(R.id.container_directory);
+ Fragment fragment = fm.findFragmentById(getFragmentId());
return fragment instanceof DirectoryFragment
? (DirectoryFragment) fragment
: null;
}
-}
+
+ private static int getFragmentId() {
+ return R.id.container_directory;
+ }
+
+ @Override
+ public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) {
+ Context context = getActivity();
+ State state = getDisplayState();
+
+ Uri contentsUri;
+ switch (mType) {
+ case TYPE_NORMAL:
+ contentsUri = mSearchMode ? DocumentsContract.buildSearchDocumentsUri(
+ mRoot.authority, mRoot.rootId, mQuery)
+ : DocumentsContract.buildChildDocumentsUri(
+ mDocument.authority, mDocument.documentId);
+ if (state.action == ACTION_MANAGE) {
+ contentsUri = DocumentsContract.setManageMode(contentsUri);
+ }
+ return new DirectoryLoader(
+ context, mType, mRoot, mDocument, contentsUri, state.userSortOrder, mSearchMode);
+ case TYPE_RECENT_OPEN:
+ final RootsCache roots = DocumentsApplication.getRootsCache(context);
+ return new RecentsLoader(context, roots, state);
+ default:
+ throw new IllegalStateException("Unknown type " + mType);
+ }
+ }
+
+ @Override
+ public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
+ if (!isAdded()) return;
+
+ State state = getDisplayState();
+
+ mAdapter.notifyDataSetChanged();
+ mModel.update(result);
+
+ state.derivedSortOrder = result.sortOrder;
+
+ updateLayout(state.derivedMode);
+
+ if (mSelection != null) {
+ mSelectionManager.setItemsSelected(mSelection.toList(), true);
+ }
+
+ // Restore any previous instance state
+ final SparseArray<Parcelable> container = state.dirState.remove(mStateKey);
+ if (container != null && !getArguments().getBoolean(Shared.EXTRA_IGNORE_STATE, false)) {
+ getView().restoreHierarchyState(container);
+ } else if (mLastSortOrder != state.derivedSortOrder) {
+ // The derived sort order takes the user sort order into account, but applies
+ // directory-specific defaults when the user doesn't explicitly set the sort
+ // order. Scroll to the top if the sort order actually changed.
+ mRecView.smoothScrollToPosition(0);
+ }
+
+ mLastSortOrder = state.derivedSortOrder;
+
+ mTuner.onModelLoaded(mModel, mType, mSearchMode);
+
+ }
+
+ @Override
+ public void onLoaderReset(Loader<DirectoryResult> loader) {
+ mModel.update(null);
+ }
+ }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
index a9b0fd1..914f71e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
@@ -84,7 +84,7 @@
return MimePredicate.mimeMatches(mState.acceptMimes, docMimeType);
}
- abstract void onModelLoaded(Model model, @ResultType int resultType);
+ abstract void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch);
/**
* Provides support for Platform specific specializations of DirectoryFragment.
@@ -166,7 +166,7 @@
}
@Override
- void onModelLoaded(Model model, @ResultType int resultType) {
+ void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch) {
// When launched into empty recents, show drawer
if (resultType == DirectoryFragment.TYPE_RECENT_OPEN
&& model.isEmpty()
@@ -211,7 +211,7 @@
}
@Override
- void onModelLoaded(Model model, @ResultType int resultType) {}
+ void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch) {}
}
/**
@@ -248,11 +248,10 @@
}
@Override
- void onModelLoaded(Model model, @ResultType int resultType) {
+ void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch) {
if (DEBUG) Log.d(TAG, "Handling model loaded. Has Location shcnage: " + mState.initialLocationHasChanged());
// When launched into empty root, open drawer.
- if (model.isEmpty() && !mState.initialLocationHasChanged()
- && resultType != DirectoryFragment.TYPE_SEARCH) {
+ if (model.isEmpty() && !mState.initialLocationHasChanged() && !isSearch) {
if (DEBUG) Log.d(TAG, "Showing roots drawer cuz stuffs empty.");
// This noops on layouts without drawer, so no need to guard.
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
index c8b6f85..19268d7 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
@@ -687,7 +687,7 @@
* Returns an unordered array of selected positions (including any
* provisional selections current in effect).
*/
- private List<String> toList() {
+ public List<String> toList() {
ArrayList<String> selection = new ArrayList<String>(mSelection);
selection.addAll(mProvisionalSelection);
return selection;
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 681b39e..41cce74 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -18,11 +18,14 @@
import android.content.Context;
import android.util.Log;
+import android.view.View;
import android.view.ViewGroup;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarWindowManager;
@@ -66,4 +69,9 @@
ViewGroup container) {
return new KeyguardBouncer(context, callback, lockPatternUtils, windowManager, container);
}
+
+ public ScrimController createScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
+ View headsUpScrim, boolean scrimSrcEnabled) {
+ return new ScrimController(scrimBehind, scrimInFront, headsUpScrim, scrimSrcEnabled);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 1a0acbe..8381f18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -94,7 +94,7 @@
private TextView mClockView;
private View mReserveNotificationSpace;
private View mQsNavbarScrim;
- private NotificationsQuickSettingsContainer mNotificationContainerParent;
+ protected NotificationsQuickSettingsContainer mNotificationContainerParent;
protected NotificationStackScrollLayout mNotificationStackScroller;
private boolean mAnimateNextTopPaddingChange;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index d60ea20..2e1855c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -111,6 +111,7 @@
import com.android.systemui.Interpolators;
import com.android.systemui.Prefs;
import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.doze.DozeHost;
@@ -771,8 +772,8 @@
ScrimView scrimBehind = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_behind);
ScrimView scrimInFront = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_in_front);
View headsUpScrim = mStatusBarWindow.findViewById(R.id.heads_up_scrim);
- mScrimController = new ScrimController(scrimBehind, scrimInFront, headsUpScrim,
- mScrimSrcModeEnabled);
+ mScrimController = SystemUIFactory.getInstance().createScrimController(
+ scrimBehind, scrimInFront, headsUpScrim, mScrimSrcModeEnabled);
mHeadsUpManager.addListener(mScrimController);
mStackScroller.setScrimController(mScrimController);
mScrimController.setBackDropView(mBackdrop);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index f310c2c2..fe76ae7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -56,16 +56,16 @@
private static final int TAG_START_ALPHA = R.id.scrim_alpha_start;
private static final int TAG_END_ALPHA = R.id.scrim_alpha_end;
- private final ScrimView mScrimBehind;
+ protected final ScrimView mScrimBehind;
private final ScrimView mScrimInFront;
private final UnlockMethodCache mUnlockMethodCache;
private final View mHeadsUpScrim;
- private boolean mKeyguardShowing;
+ protected boolean mKeyguardShowing;
private float mFraction;
private boolean mDarkenWhileDragging;
- private boolean mBouncerShowing;
+ protected boolean mBouncerShowing;
private boolean mWakeAndUnlocking;
private boolean mAnimateChange;
private boolean mUpdatePending;
@@ -203,7 +203,7 @@
mUpdatePending = true;
}
- private void updateScrims() {
+ protected void updateScrims() {
if (mAnimateKeyguardFadingOut || mForceHideScrims) {
setScrimInFrontColor(0f);
setScrimBehindColor(0f);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index f078b53..8f9a224 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -3045,7 +3045,7 @@
disableClipOptimization();
}
handleDismissAllClipping();
- if (mCurrIconRow != null & mCurrIconRow.isVisible()) {
+ if (mCurrIconRow != null && mCurrIconRow.isVisible()) {
mCurrIconRow.getNotificationParent().animateTranslateNotification(0 /* left target */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
index fac6338..6b943e0 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -101,7 +101,26 @@
@Override
public void run() {
if (mState != STATE_NO_PIP) {
- // TODO: check whether PIP task is closed.
+ StackInfo stackInfo = null;
+ try {
+ stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ if (stackInfo == null) {
+ Log.w(TAG, "There is no pinned stack");
+ closeInternal(false);
+ return;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "getStackInfo failed", e);
+ return;
+ }
+ for (int i = stackInfo.taskIds.length - 1; i >= 0; --i) {
+ if (stackInfo.taskIds[i] == mPipTaskId) {
+ // PIP task is still alive.
+ return;
+ }
+ }
+ // PIP task doesn't exist anymore in PINNED_STACK.
+ closeInternal(true);
}
}
};
@@ -203,12 +222,18 @@
* Closes PIP (PIPed activity and PIP system UI).
*/
public void closePip() {
+ closeInternal(true);
+ }
+
+ private void closeInternal(boolean removePipStack) {
mState = STATE_NO_PIP;
mPipTaskId = TASK_ID_NO_PIP;
- try {
- mActivityManager.removeStack(PINNED_STACK_ID);
- } catch (RemoteException e) {
- Log.e(TAG, "removeStack failed", e);
+ if (removePipStack) {
+ try {
+ mActivityManager.removeStack(PINNED_STACK_ID);
+ } catch (RemoteException e) {
+ Log.e(TAG, "removeStack failed", e);
+ }
}
}
diff --git a/tools/layoutlib/bridge/src/android/view/ViewRootImpl_RunQueue_Delegate.java b/tools/layoutlib/bridge/src/android/view/ViewRootImpl_RunQueue_Delegate.java
deleted file mode 100644
index 51b42a6..0000000
--- a/tools/layoutlib/bridge/src/android/view/ViewRootImpl_RunQueue_Delegate.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate used to provide new implementation of a select few methods of
- * {@link ViewRootImpl.RunQueue}
- *
- * Through the layoutlib_create tool, the original methods of ViewRootImpl.RunQueue have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- */
-public class ViewRootImpl_RunQueue_Delegate {
-
- @LayoutlibDelegate
- /*package*/ static void postDelayed(ViewRootImpl.RunQueue thisQueue, Runnable action, long
- delayMillis) {
- // The actual RunQueue is never run and therefore never cleared. This method avoids
- // runnables to be added to the RunQueue so they do not leak resources.
- }
-}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 15c567f..8a23e4b 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -184,7 +184,6 @@
"android.view.View#getWindowToken",
"android.view.View#isInEditMode",
"android.view.ViewRootImpl#isInTouchMode",
- "android.view.ViewRootImpl$RunQueue#postDelayed",
"android.view.WindowManagerGlobal#getWindowManagerService",
"android.view.inputmethod.InputMethodManager#getInstance",
"android.view.MenuInflater#registerMenu",