• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.launcher3;
18 
19 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
20 import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
21 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
22 
23 import android.app.ActivityOptions;
24 import android.app.WallpaperColors;
25 import android.app.WallpaperManager;
26 import android.app.WallpaperManager.OnColorsChangedListener;
27 import android.content.ActivityNotFoundException;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.pm.LauncherApps;
31 import android.content.res.Configuration;
32 import android.graphics.Insets;
33 import android.graphics.Point;
34 import android.graphics.Rect;
35 import android.graphics.drawable.Drawable;
36 import android.os.Bundle;
37 import android.os.Process;
38 import android.os.StrictMode;
39 import android.os.UserHandle;
40 import android.util.Log;
41 import android.view.ActionMode;
42 import android.view.Display;
43 import android.view.View;
44 import android.view.View.OnClickListener;
45 import android.view.WindowInsets.Type;
46 import android.view.WindowMetrics;
47 import android.widget.Toast;
48 
49 import androidx.annotation.NonNull;
50 import androidx.annotation.Nullable;
51 
52 import com.android.launcher3.LauncherSettings.Favorites;
53 import com.android.launcher3.allapps.AllAppsContainerView;
54 import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider;
55 import com.android.launcher3.allapps.search.SearchAdapterProvider;
56 import com.android.launcher3.logging.InstanceId;
57 import com.android.launcher3.logging.InstanceIdSequence;
58 import com.android.launcher3.model.data.ItemInfo;
59 import com.android.launcher3.model.data.WorkspaceItemInfo;
60 import com.android.launcher3.touch.ItemClickHandler;
61 import com.android.launcher3.util.ActivityOptionsWrapper;
62 import com.android.launcher3.util.DisplayController;
63 import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
64 import com.android.launcher3.util.DisplayController.Info;
65 import com.android.launcher3.util.PackageManagerHelper;
66 import com.android.launcher3.util.RunnableList;
67 import com.android.launcher3.util.Themes;
68 import com.android.launcher3.util.TraceHelper;
69 import com.android.launcher3.util.WindowBounds;
70 
71 /**
72  * Extension of BaseActivity allowing support for drag-n-drop
73  */
74 @SuppressWarnings("NewApi")
75 public abstract class BaseDraggingActivity extends BaseActivity
76         implements OnColorsChangedListener, DisplayInfoChangeListener {
77 
78     private static final String TAG = "BaseDraggingActivity";
79 
80     // When starting an action mode, setting this tag will cause the action mode to be cancelled
81     // automatically when user interacts with the launcher.
82     public static final Object AUTO_CANCEL_ACTION_MODE = new Object();
83 
84     private ActionMode mCurrentActionMode;
85     protected boolean mIsSafeModeEnabled;
86 
87     private Runnable mOnStartCallback;
88     private RunnableList mOnResumeCallbacks = new RunnableList();
89 
90     private int mThemeRes = R.style.AppTheme;
91 
92     @Override
onCreate(Bundle savedInstanceState)93     protected void onCreate(Bundle savedInstanceState) {
94         super.onCreate(savedInstanceState);
95 
96         mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
97                 () -> getPackageManager().isSafeMode());
98         DisplayController.INSTANCE.get(this).addChangeListener(this);
99 
100         // Update theme
101         if (Utilities.ATLEAST_P) {
102             getSystemService(WallpaperManager.class)
103                     .addOnColorsChangedListener(this, MAIN_EXECUTOR.getHandler());
104         }
105         int themeRes = Themes.getActivityThemeRes(this);
106         if (themeRes != mThemeRes) {
107             mThemeRes = themeRes;
108             setTheme(themeRes);
109         }
110     }
111 
112     @Override
onResume()113     protected void onResume() {
114         super.onResume();
115         mOnResumeCallbacks.executeAllAndClear();
116     }
117 
addOnResumeCallback(Runnable callback)118     public void addOnResumeCallback(Runnable callback) {
119         mOnResumeCallbacks.add(callback);
120     }
121 
122     @Override
onColorsChanged(WallpaperColors wallpaperColors, int which)123     public void onColorsChanged(WallpaperColors wallpaperColors, int which) {
124         updateTheme();
125     }
126 
127     @Override
onConfigurationChanged(Configuration newConfig)128     public void onConfigurationChanged(Configuration newConfig) {
129         super.onConfigurationChanged(newConfig);
130         updateTheme();
131     }
132 
updateTheme()133     private void updateTheme() {
134         if (mThemeRes != Themes.getActivityThemeRes(this)) {
135             recreate();
136         }
137     }
138 
139     @Override
onActionModeStarted(ActionMode mode)140     public void onActionModeStarted(ActionMode mode) {
141         super.onActionModeStarted(mode);
142         mCurrentActionMode = mode;
143     }
144 
145     @Override
onActionModeFinished(ActionMode mode)146     public void onActionModeFinished(ActionMode mode) {
147         super.onActionModeFinished(mode);
148         mCurrentActionMode = null;
149     }
150 
151     @Override
finishAutoCancelActionMode()152     public boolean finishAutoCancelActionMode() {
153         if (mCurrentActionMode != null && AUTO_CANCEL_ACTION_MODE == mCurrentActionMode.getTag()) {
154             mCurrentActionMode.finish();
155             return true;
156         }
157         return false;
158     }
159 
getOverviewPanel()160     public abstract <T extends View> T getOverviewPanel();
161 
getRootView()162     public abstract View getRootView();
163 
returnToHomescreen()164     public void returnToHomescreen() {
165         // no-op
166     }
167 
getViewBounds(View v)168     public Rect getViewBounds(View v) {
169         int[] pos = new int[2];
170         v.getLocationOnScreen(pos);
171         return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight());
172     }
173 
174     @NonNull
getActivityLaunchOptions(View v, @Nullable ItemInfo item)175     public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
176         int left = 0, top = 0;
177         int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
178         if (v instanceof BubbleTextView) {
179             // Launch from center of icon, not entire view
180             Drawable icon = ((BubbleTextView) v).getIcon();
181             if (icon != null) {
182                 Rect bounds = icon.getBounds();
183                 left = (width - bounds.width()) / 2;
184                 top = v.getPaddingTop();
185                 width = bounds.width();
186                 height = bounds.height();
187             }
188         }
189         ActivityOptions options =
190                 ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
191         RunnableList callback = new RunnableList();
192         addOnResumeCallback(callback::executeAllAndDestroy);
193         return new ActivityOptionsWrapper(options, callback);
194     }
195 
startActivitySafely(View v, Intent intent, @Nullable ItemInfo item)196     public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item) {
197         if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
198             Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
199             return false;
200         }
201 
202         Bundle optsBundle = (v != null) ? getActivityLaunchOptions(v, item).toBundle() : null;
203         UserHandle user = item == null ? null : item.user;
204 
205         // Prepare intent
206         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
207         if (v != null) {
208             intent.setSourceBounds(getViewBounds(v));
209         }
210         try {
211             boolean isShortcut = (item instanceof WorkspaceItemInfo)
212                     && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
213                     || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
214                     && !((WorkspaceItemInfo) item).isPromise();
215             if (isShortcut) {
216                 // Shortcuts need some special checks due to legacy reasons.
217                 startShortcutIntentSafely(intent, optsBundle, item);
218             } else if (user == null || user.equals(Process.myUserHandle())) {
219                 // Could be launching some bookkeeping activity
220                 startActivity(intent, optsBundle);
221             } else {
222                 getSystemService(LauncherApps.class).startMainActivity(
223                         intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
224             }
225             if (item != null) {
226                 InstanceId instanceId = new InstanceIdSequence().newInstanceId();
227                 logAppLaunch(item, instanceId);
228             }
229             return true;
230         } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
231             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
232             Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
233         }
234         return false;
235     }
236 
logAppLaunch(ItemInfo info, InstanceId instanceId)237     protected void logAppLaunch(ItemInfo info, InstanceId instanceId) {
238         getStatsLogManager().logger().withItemInfo(info).withInstanceId(instanceId)
239                 .log(LAUNCHER_APP_LAUNCH_TAP);
240     }
241 
startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info)242     private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
243         try {
244             StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
245             try {
246                 // Temporarily disable deathPenalty on all default checks. For eg, shortcuts
247                 // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure
248                 // is enabled by default on NYC.
249                 StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
250                         .penaltyLog().build());
251 
252                 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
253                     String id = ((WorkspaceItemInfo) info).getDeepShortcutId();
254                     String packageName = intent.getPackage();
255                     startShortcut(packageName, id, intent.getSourceBounds(), optsBundle, info.user);
256                 } else {
257                     // Could be launching some bookkeeping activity
258                     startActivity(intent, optsBundle);
259                 }
260             } finally {
261                 StrictMode.setVmPolicy(oldPolicy);
262             }
263         } catch (SecurityException e) {
264             if (!onErrorStartingShortcut(intent, info)) {
265                 throw e;
266             }
267         }
268     }
269 
onErrorStartingShortcut(Intent intent, ItemInfo info)270     protected boolean onErrorStartingShortcut(Intent intent, ItemInfo info) {
271         return false;
272     }
273 
274     @Override
onStart()275     protected void onStart() {
276         super.onStart();
277 
278         if (mOnStartCallback != null) {
279             mOnStartCallback.run();
280             mOnStartCallback = null;
281         }
282     }
283 
284     @Override
onDestroy()285     protected void onDestroy() {
286         super.onDestroy();
287         if (Utilities.ATLEAST_P) {
288             getSystemService(WallpaperManager.class).removeOnColorsChangedListener(this);
289         }
290         DisplayController.INSTANCE.get(this).removeChangeListener(this);
291     }
292 
runOnceOnStart(Runnable action)293     public void runOnceOnStart(Runnable action) {
294         mOnStartCallback = action;
295     }
296 
clearRunOnceOnStartCallback()297     public void clearRunOnceOnStartCallback() {
298         mOnStartCallback = null;
299     }
300 
onDeviceProfileInitiated()301     protected void onDeviceProfileInitiated() {
302         if (mDeviceProfile.isVerticalBarLayout()) {
303             mDeviceProfile.updateIsSeascape(this);
304         }
305     }
306 
307     @Override
onDisplayInfoChanged(Context context, Info info, int flags)308     public void onDisplayInfoChanged(Context context, Info info, int flags) {
309         if ((flags & CHANGE_ROTATION) != 0 && mDeviceProfile.updateIsSeascape(this)) {
310             reapplyUi();
311         }
312     }
313 
getItemOnClickListener()314     public OnClickListener getItemOnClickListener() {
315         return ItemClickHandler.INSTANCE;
316     }
317 
reapplyUi()318     protected abstract void reapplyUi();
319 
getMultiWindowDisplaySize()320     protected WindowBounds getMultiWindowDisplaySize() {
321         if (Utilities.ATLEAST_R) {
322             WindowMetrics wm = getWindowManager().getCurrentWindowMetrics();
323 
324             Insets insets = wm.getWindowInsets().getInsets(Type.systemBars());
325             return new WindowBounds(wm.getBounds(),
326                     new Rect(insets.left, insets.top, insets.right, insets.bottom));
327         }
328         // Note: Calls to getSize() can't rely on our cached DefaultDisplay since it can return
329         // the app window size
330         Display display = getWindowManager().getDefaultDisplay();
331         Point mwSize = new Point();
332         display.getSize(mwSize);
333         return new WindowBounds(new Rect(0, 0, mwSize.x, mwSize.y), new Rect());
334     }
335 
336     /**
337      * Creates and returns {@link SearchAdapterProvider} for build variant specific search result
338      * views
339      */
createSearchAdapterProvider(AllAppsContainerView allapps)340     public SearchAdapterProvider createSearchAdapterProvider(AllAppsContainerView allapps) {
341         return new DefaultSearchAdapterProvider(this, allapps);
342     }
343 }
344