OSDN Git Service

Add support for opening multiple windows.
authorSteve McKay <smckay@google.com>
Wed, 23 Sep 2015 22:44:24 +0000 (15:44 -0700)
committerSteve McKay <smckay@google.com>
Fri, 25 Sep 2015 21:46:14 +0000 (14:46 -0700)
Installs an intermediate activity that manages tasks.

Bug: 20562850
Change-Id: If2d0a125b0630c27af07666b0f6a712f58243e9f

packages/DocumentsUI/AndroidManifest.xml
packages/DocumentsUI/res/menu/activity.xml
packages/DocumentsUI/res/values/strings.xml
packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java [new file with mode: 0644]
packages/DocumentsUI/src/com/android/documentsui/State.java

index ac6f950..295cb80 100644 (file)
@@ -3,6 +3,7 @@
 
     <uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
     <uses-permission android:name="android.permission.REMOVE_TASKS" />
+    <uses-permission android:name="android.permission.REORDER_TASKS" />
 
     <application
         android:name=".DocumentsApplication"
@@ -49,8 +50,8 @@
         </activity>
 
         <activity
-            android:name=".FilesActivity"
-            android:theme="@style/FilesTheme"
+            android:name=".LauncherActivity"
+            android:theme="@android:style/Theme.NoDisplay"
             android:icon="@drawable/ic_files_app"
             android:label="@string/files_label"
             android:enabled="@bool/productivity_device">
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
+        </activity>
+
+        <activity
+            android:name=".FilesActivity"
+            android:theme="@style/FilesTheme"
+            android:icon="@drawable/ic_files_app"
+            android:label="@string/files_label"
+            android:documentLaunchMode="intoExisting">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
             <intent-filter>
                 <action android:name="android.provider.action.BROWSE_DOCUMENT_ROOT" />
                 <category android:name="android.intent.category.DEFAULT" />
index 7df152f..673a254 100644 (file)
@@ -26,6 +26,7 @@
         android:id="@+id/menu_create_dir"
         android:title="@string/menu_create_dir"
         android:icon="@drawable/ic_menu_new_folder"
+        android:alphabeticShortcut="e"
         android:showAsAction="always"
         android:visible="false" />
     <item
         android:icon="@drawable/ic_menu_view_list"
         android:showAsAction="never" />
     <item
+        android:id="@+id/menu_new_window"
+        android:title="@string/menu_new_window"
+        android:alphabeticShortcut="n"
+        android:showAsAction="never"
+        android:visible="false" />
+    <item
         android:id="@+id/menu_paste_from_clipboard"
         android:title="@string/menu_paste_from_clipboard"
         android:alphabeticShortcut="v"
         android:showAsAction="never"
         android:visible="false" />
+    <!-- Copy action is defined in mode_directory.xml -->
     <item
         android:id="@+id/menu_advanced"
         android:showAsAction="never"
index a12edf3..a4acb60 100644 (file)
@@ -54,6 +54,8 @@
     <!-- Menu item title that moves the selected documents [CHAR LIMIT=24] -->
     <string name="menu_move">Move to\u2026</string>
 
+    <!-- Menu item title that creates a new window in the activity [CHAR LIMIT=24] -->
+    <string name="menu_new_window">New window</string>
     <!-- Menu item title that copies the selected documents to clipboard [CHAR LIMIT=24] -->
     <string name="menu_copy_to_clipboard">Copy</string>
     <!-- Menu item title that pastes files from the clipboard [CHAR LIMIT=24] -->
index c8ec4dc..a6a45e5 100644 (file)
@@ -75,10 +75,11 @@ abstract class BaseActivity extends Activity {
     RootsCache mRoots;
     SearchManager mSearchManager;
     DrawerController mDrawer;
+    boolean mProductivityDevice;
 
+    private final String mTag;
     @LayoutRes
     private int mLayoutId;
-    private final String mTag;
     private DirectoryContainerView mDirectoryContainer;
 
     public abstract void onDocumentPicked(DocumentInfo doc, @Nullable DocumentContext siblings);
@@ -99,6 +100,7 @@ abstract class BaseActivity extends Activity {
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
+        mProductivityDevice = getResources().getBoolean(R.bool.productivity_device);
         mState = (icicle != null)
                 ? icicle.<State>getParcelable(EXTRA_STATE)
                         : buildState();
@@ -208,6 +210,7 @@ abstract class BaseActivity extends Activity {
             switch (item.getItemId()) {
                 case R.id.menu_advanced:
                 case R.id.menu_file_size:
+                case R.id.menu_new_window:
                     break;
                 default:
                     item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
index 7e9531b..e8d1088 100644 (file)
@@ -19,6 +19,7 @@ package com.android.documentsui;
 import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
 import static com.android.documentsui.Shared.DEBUG;
 import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkState;
 
 import android.app.Activity;
 import android.app.FragmentManager;
@@ -85,11 +86,12 @@ public class FilesActivity extends BaseActivity {
 
         RootsFragment.show(getFragmentManager(), null);
         if (!mState.restored) {
-            Uri rootUri = getIntent().getData();
+            Intent intent = getIntent();
+            Uri rootUri = intent.getData();
 
             // If we've got a specific root to display, restore that root using a dedicated
             // authority. That way a misbehaving provider won't result in an ANR.
-            if (rootUri != null) {
+            if (rootUri != null && !LauncherActivity.isLaunchUri(rootUri)) {
                 new RestoreRootTask(rootUri).executeOnExecutor(
                         ProviderExecutor.forAuthority(rootUri.getAuthority()));
             } else {
@@ -97,7 +99,6 @@ public class FilesActivity extends BaseActivity {
             }
 
             // Show a failure dialog if there was a failed operation.
-            final Intent intent = getIntent();
             final DocumentStack dstStack = intent.getParcelableExtra(CopyService.EXTRA_STACK);
             final int failure = intent.getIntExtra(CopyService.EXTRA_FAILURE, 0);
             final int transferMode = intent.getIntExtra(CopyService.EXTRA_TRANSFER_MODE,
@@ -207,16 +208,25 @@ public class FilesActivity extends BaseActivity {
     public boolean onPrepareOptionsMenu(Menu menu) {
         boolean shown = super.onPrepareOptionsMenu(menu);
 
-        final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard);
+        menu.findItem(R.id.menu_file_size).setVisible(true);
+        menu.findItem(R.id.menu_advanced).setVisible(true);
+
         final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
+        final MenuItem newWindow = menu.findItem(R.id.menu_new_window);
+        final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard);
 
         boolean canCreateDir = canCreateDirectory();
 
         createDir.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
         createDir.setVisible(canCreateDir);
+        createDir.setEnabled(canCreateDir);
+
+        newWindow.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+        newWindow.setVisible(mProductivityDevice);
+        newWindow.setEnabled(mProductivityDevice);
 
-        pasteFromCb.setVisible(true);
         pasteFromCb.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+        pasteFromCb.setVisible(true);
         pasteFromCb.setEnabled(mClipper.hasItemsToPaste());
 
         return shown;
@@ -224,12 +234,19 @@ public class FilesActivity extends BaseActivity {
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
-        final int id = item.getItemId();
-        if (id == R.id.menu_paste_from_clipboard) {
-            DirectoryFragment dir = DirectoryFragment.get(getFragmentManager());
-            dir = DirectoryFragment.get(getFragmentManager());
-            dir.pasteFromClipboard();
-            return true;
+        switch (item.getItemId()) {
+            case R.id.menu_create_dir:
+                checkState(canCreateDirectory());
+                showCreateDirectoryDialog();
+                return true;
+            case R.id.menu_new_window:
+                startActivity(LauncherActivity.createLaunchIntent(this));
+                return true;
+            case R.id.menu_paste_from_clipboard:
+                DirectoryFragment dir = DirectoryFragment.get(getFragmentManager());
+                dir = DirectoryFragment.get(getFragmentManager());
+                dir.pasteFromClipboard();
+                return true;
         }
 
         return super.onOptionsItemSelected(item);
@@ -317,19 +334,13 @@ public class FilesActivity extends BaseActivity {
                 dir = DirectoryFragment.get(getFragmentManager());
                 dir.selectAllFiles();
                 return true;
-            case KeyEvent.KEYCODE_N:
-                if (event.isShiftPressed() && canCreateDirectory()) {
-                    showCreateDirectoryDialog();
-                    return true;
-                }
             case KeyEvent.KEYCODE_C:
                 // TODO: Should be statically bound using alphabeticShortcut. See b/21330356.
                 dir = DirectoryFragment.get(getFragmentManager());
                 dir.copySelectedToClipboard();
-                // TODO: Cancel action mode in directory fragment.
         }
 
-        return super.onKeyUp(keyCode, event);
+        return super.onKeyShortcut(keyCode, event);
     }
 
     @Override
diff --git a/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java b/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java
new file mode 100644 (file)
index 0000000..c29937d
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 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 com.android.documentsui;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityManager.AppTask;
+import android.app.ActivityManager.RecentTaskInfo;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+
+import java.util.List;
+
+/**
+ * Provides FilesActivity task grouping support. This allows multiple FilesActivities to be
+ * launched (a behavior imparted by way of {@code documentLaunchMode="intoExisting"} and
+ * our use of pseudo document {@link Uri}s. This also lets us move an existing task
+ * to the foreground when a suitable task exists.
+ *
+ * Requires that {@code documentLaunchMode="intoExisting"} be set on target activity.
+ *
+ */
+public class LauncherActivity extends Activity {
+
+    public static final String LAUNCH_CONTROL_AUTHORITY = "com.android.documentsui.launchControl";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        ActivityManager activities = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+        List<AppTask> tasks = activities.getAppTasks();
+
+        AppTask raiseTask = null;
+        for (AppTask task : tasks) {
+            Uri taskUri = task.getTaskInfo().baseIntent.getData();
+            if (taskUri != null && isLaunchUri(taskUri)) {
+                raiseTask = task;
+            }
+        }
+
+        if (raiseTask == null) {
+            launchFilesTask();
+        } else {
+            raiseFilesTask(activities, raiseTask.getTaskInfo());
+        }
+
+        finish();
+    }
+
+    private void launchFilesTask() {
+        Intent intent = createLaunchIntent(this);
+        startActivity(intent);
+    }
+
+    private void raiseFilesTask(ActivityManager activities, RecentTaskInfo task) {
+        activities.moveTaskToFront(task.id, 0);
+    }
+
+    static Intent createLaunchIntent(Context context) {
+        Intent intent = new Intent(context, FilesActivity.class);
+        intent.setData(buildLaunchUri());
+        return intent;
+    }
+
+    private static Uri buildLaunchUri() {
+        return new Uri.Builder()
+                .authority(LAUNCH_CONTROL_AUTHORITY)
+                .fragment(String.valueOf(System.currentTimeMillis()))
+                .build();
+    }
+
+    static boolean isLaunchUri(@Nullable Uri uri) {
+        return uri != null && LAUNCH_CONTROL_AUTHORITY.equals(uri.getAuthority());
+    }
+}
index bbffad3..4306a0e 100644 (file)
@@ -44,14 +44,14 @@ public class State implements android.os.Parcelable {
     public int derivedSortOrder = SORT_ORDER_DISPLAY_NAME;
 
     public boolean allowMultiple;
-    public boolean forceSize ;
+    public boolean forceSize;
     public boolean showSize;
-    public boolean localOnly ;
-    public boolean forceAdvanced ;
-    public boolean showAdvanced ;
-    public boolean stackTouched ;
-    public boolean restored ;
-    public boolean directoryCopy ;
+    public boolean localOnly;
+    public boolean forceAdvanced;
+    public boolean showAdvanced;
+    public boolean stackTouched;
+    public boolean restored;
+    public boolean directoryCopy;
     /** Transfer mode for file copy/move operations. */
     public int transferMode;