blob: 458f7b280f9d00164ab11f55307154668b2f0117 [file] [log] [blame]
Sunny Goyal0b0847b2018-03-14 12:30:11 -07001/*
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
17package com.android.launcher3;
18
19import android.app.ActivityOptions;
20import android.content.ActivityNotFoundException;
21import android.content.Context;
22import android.content.ContextWrapper;
23import android.content.Intent;
24import android.graphics.Rect;
25import android.os.Bundle;
26import android.os.Process;
27import android.os.StrictMode;
28import android.os.UserHandle;
29import android.util.Log;
30import android.view.ActionMode;
31import android.view.View;
32import android.widget.Toast;
33
34import com.android.launcher3.LauncherSettings.Favorites;
35import com.android.launcher3.badge.BadgeInfo;
36import com.android.launcher3.compat.LauncherAppsCompat;
37import com.android.launcher3.shortcuts.DeepShortcutManager;
38import com.android.launcher3.views.BaseDragLayer;
39
40/**
41 * Extension of BaseActivity allowing support for drag-n-drop
42 */
43public abstract class BaseDraggingActivity extends BaseActivity {
44
45 private static final String TAG = "BaseDraggingActivity";
46
47 // The Intent extra that defines whether to ignore the launch animation
48 private static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
49 "com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
50
51 // When starting an action mode, setting this tag will cause the action mode to be cancelled
52 // automatically when user interacts with the launcher.
53 public static final Object AUTO_CANCEL_ACTION_MODE = new Object();
54
55 private ActionMode mCurrentActionMode;
56 protected boolean mIsSafeModeEnabled;
57
Sunny Goyal9d69c8d2018-03-19 13:41:31 -070058 private OnStartCallback mOnStartCallback;
59
Sunny Goyal0b0847b2018-03-14 12:30:11 -070060 @Override
61 protected void onCreate(Bundle savedInstanceState) {
62 super.onCreate(savedInstanceState);
63 mIsSafeModeEnabled = getPackageManager().isSafeMode();
64 }
65
66 @Override
67 public void onActionModeStarted(ActionMode mode) {
68 super.onActionModeStarted(mode);
69 mCurrentActionMode = mode;
70 }
71
72 @Override
73 public void onActionModeFinished(ActionMode mode) {
74 super.onActionModeFinished(mode);
75 mCurrentActionMode = null;
76 }
77
78 public boolean finishAutoCancelActionMode() {
79 if (mCurrentActionMode != null && AUTO_CANCEL_ACTION_MODE == mCurrentActionMode.getTag()) {
80 mCurrentActionMode.finish();
81 return true;
82 }
83 return false;
84 }
85
86 public abstract BaseDragLayer getDragLayer();
87
88 public abstract <T extends View> T getOverviewPanel();
89
Sunny Goyal9d69c8d2018-03-19 13:41:31 -070090 public abstract View getRootView();
91
Sunny Goyal0b0847b2018-03-14 12:30:11 -070092 public abstract BadgeInfo getBadgeInfoForItem(ItemInfo info);
93
94 public abstract void invalidateParent(ItemInfo info);
95
96 public static BaseDraggingActivity fromContext(Context context) {
97 if (context instanceof BaseDraggingActivity) {
98 return (BaseDraggingActivity) context;
99 }
100 return ((BaseDraggingActivity) ((ContextWrapper) context).getBaseContext());
101 }
102
103 public Rect getViewBounds(View v) {
104 int[] pos = new int[2];
105 v.getLocationOnScreen(pos);
106 return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight());
107 }
108
109 public final Bundle getActivityLaunchOptionsAsBundle(View v, boolean useDefaultLaunchOptions) {
110 ActivityOptions activityOptions = getActivityLaunchOptions(v, useDefaultLaunchOptions);
111 return activityOptions == null ? null : activityOptions.toBundle();
112 }
113
114 public abstract ActivityOptions getActivityLaunchOptions(
115 View v, boolean useDefaultLaunchOptions);
116
117 public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
118 if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
119 Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
120 return false;
121 }
122
123 // Only launch using the new animation if the shortcut has not opted out (this is a
124 // private contract between launcher and may be ignored in the future).
125 boolean useLaunchAnimation = (v != null) &&
126 !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
127 Bundle optsBundle = useLaunchAnimation
128 ? getActivityLaunchOptionsAsBundle(v, isInMultiWindowModeCompat())
129 : null;
130
131 UserHandle user = item == null ? null : item.user;
132
133 // Prepare intent
134 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
135 if (v != null) {
136 intent.setSourceBounds(getViewBounds(v));
137 }
138 try {
139 boolean isShortcut = Utilities.ATLEAST_MARSHMALLOW
140 && (item instanceof ShortcutInfo)
141 && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
142 || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
143 && !((ShortcutInfo) item).isPromise();
144 if (isShortcut) {
145 // Shortcuts need some special checks due to legacy reasons.
146 startShortcutIntentSafely(intent, optsBundle, item);
147 } else if (user == null || user.equals(Process.myUserHandle())) {
148 // Could be launching some bookkeeping activity
149 startActivity(intent, optsBundle);
150 } else {
151 LauncherAppsCompat.getInstance(this).startActivityForProfile(
152 intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
153 }
154 getUserEventDispatcher().logAppLaunch(v, intent);
155 return true;
156 } catch (ActivityNotFoundException|SecurityException e) {
157 Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
158 Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
159 }
160 return false;
161 }
162
163 private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
164 try {
165 StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
166 try {
167 // Temporarily disable deathPenalty on all default checks. For eg, shortcuts
168 // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure
169 // is enabled by default on NYC.
170 StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
171 .penaltyLog().build());
172
173 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
174 String id = ((ShortcutInfo) info).getDeepShortcutId();
175 String packageName = intent.getPackage();
176 DeepShortcutManager.getInstance(this).startShortcut(
177 packageName, id, intent.getSourceBounds(), optsBundle, info.user);
178 } else {
179 // Could be launching some bookkeeping activity
180 startActivity(intent, optsBundle);
181 }
182 } finally {
183 StrictMode.setVmPolicy(oldPolicy);
184 }
185 } catch (SecurityException e) {
186 if (!onErrorStartingShortcut(intent, info)) {
187 throw e;
188 }
189 }
190 }
191
192 protected boolean onErrorStartingShortcut(Intent intent, ItemInfo info) {
193 return false;
194 }
Sunny Goyal9d69c8d2018-03-19 13:41:31 -0700195
196 @Override
197 protected void onStart() {
198 super.onStart();
199
200 if (mOnStartCallback != null) {
201 mOnStartCallback.onActivityStart(this);
202 mOnStartCallback = null;
203 }
204 }
205
206 public <T extends BaseDraggingActivity> void setOnStartCallback(OnStartCallback<T> callback) {
207 mOnStartCallback = callback;
208 }
209
210 /**
211 * Callback for listening for onStart
212 */
213 public interface OnStartCallback<T extends BaseDraggingActivity> {
214
215 void onActivityStart(T activity);
216 }
Sunny Goyal0b0847b2018-03-14 12:30:11 -0700217}