1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.documentsui;
18 
19 import static com.android.documentsui.Shared.DEBUG;
20 
21 import android.app.Activity;
22 import android.app.ActivityManager;
23 import android.app.ActivityManager.AppTask;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.net.Uri;
27 import android.os.Bundle;
28 import android.provider.DocumentsContract;
29 import android.support.annotation.Nullable;
30 import android.util.Log;
31 
32 import java.util.List;
33 
34 /**
35  * Provides FilesActivity task grouping support. This allows multiple FilesActivities to be
36  * launched (a behavior imparted by way of {@code documentLaunchMode="intoExisting"} and
37  * our use of pseudo document {@link Uri}s. This also lets us move an existing task
38  * to the foreground when a suitable task exists.
39  *
40  * Requires that {@code documentLaunchMode="intoExisting"} be set on target activity.
41  *
42  */
43 public class LauncherActivity extends Activity {
44 
45     private static final String LAUNCH_CONTROL_AUTHORITY = "com.android.documentsui.launchControl";
46     private static final String TAG = "LauncherActivity";
47 
48     // Array of boolean extras that should be copied when creating new launch intents.
49     // Missing intents will be ignored.
50     private static final String[] PERSISTENT_BOOLEAN_EXTRAS = {
51         DocumentsContract.EXTRA_SHOW_FILESIZE,
52         DocumentsContract.EXTRA_SHOW_ADVANCED,
53         DocumentsContract.EXTRA_FANCY_FEATURES,
54         Shared.EXTRA_PRODUCTIVITY_MODE
55     };
56 
57     @Override
onCreate(Bundle savedInstanceState)58     protected void onCreate(Bundle savedInstanceState) {
59         super.onCreate(savedInstanceState);
60 
61         ActivityManager activities = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
62 
63         Intent intent = findTask(activities);
64         if (intent != null) {
65             restoreTask(intent);
66         } else {
67             startTask();
68         }
69 
70         finish();
71     }
72 
findTask(ActivityManager activities)73     private @Nullable Intent findTask(ActivityManager activities) {
74         List<AppTask> tasks = activities.getAppTasks();
75         for (AppTask task : tasks) {
76             Intent intent = task.getTaskInfo().baseIntent;
77             Uri uri = intent.getData();
78             if (isLaunchUri(uri)) {
79                 return intent;
80             }
81         }
82         return null;
83     }
84 
startTask()85     private void startTask() {
86         Intent intent = createLaunchIntent(this);
87 
88         // Forward any flags from the original intent.
89         intent.setFlags(getIntent().getFlags());
90         if (DEBUG) Log.d(TAG, "Starting new task > " + intent.getData());
91         startActivity(intent);
92     }
93 
restoreTask(Intent intent)94     private void restoreTask(Intent intent) {
95         if (DEBUG) Log.d(TAG, "Restoring existing task > " + intent.getData());
96         // TODO: This doesn't appear to restore a task once it has stopped running.
97         startActivity(intent);
98     }
99 
createLaunchIntent(Activity activity)100     static final Intent createLaunchIntent(Activity activity) {
101         Intent intent = new Intent(activity, FilesActivity.class);
102         intent.setData(buildLaunchUri());
103 
104         // Relay any config overrides bits present in the original intent.
105         Intent original = activity.getIntent();
106         if (original != null) {
107             copyExtras(original, intent);
108             if (original.hasExtra(Intent.EXTRA_TITLE)) {
109                 intent.putExtra(
110                         Intent.EXTRA_TITLE,
111                         original.getStringExtra(Intent.EXTRA_TITLE));
112             }
113         }
114         return intent;
115     }
116 
copyExtras(Intent src, Intent dest)117     private static void copyExtras(Intent src, Intent dest) {
118         for (String extra : PERSISTENT_BOOLEAN_EXTRAS) {
119             if (src.hasExtra(extra)) {
120                 dest.putExtra(extra, src.getBooleanExtra(extra, false));
121             }
122         }
123     }
124 
buildLaunchUri()125     private static Uri buildLaunchUri() {
126         return new Uri.Builder()
127                 .authority(LAUNCH_CONTROL_AUTHORITY)
128                 .fragment(String.valueOf(System.currentTimeMillis()))
129                 .build();
130     }
131 
isLaunchUri(@ullable Uri uri)132     static boolean isLaunchUri(@Nullable Uri uri) {
133         boolean result = uri != null && LAUNCH_CONTROL_AUTHORITY.equals(uri.getAuthority());
134         return result;
135     }
136 }
137