Home | History | Annotate | Download | only in launcher3
      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 android.app.ActivityOptions;
     20 import android.content.ActivityNotFoundException;
     21 import android.content.Context;
     22 import android.content.ContextWrapper;
     23 import android.content.Intent;
     24 import android.graphics.Rect;
     25 import android.os.Bundle;
     26 import android.os.Process;
     27 import android.os.StrictMode;
     28 import android.os.UserHandle;
     29 import android.util.Log;
     30 import android.view.ActionMode;
     31 import android.view.Surface;
     32 import android.view.View;
     33 import android.widget.Toast;
     34 
     35 import com.android.launcher3.LauncherSettings.Favorites;
     36 import com.android.launcher3.badge.BadgeInfo;
     37 import com.android.launcher3.compat.LauncherAppsCompat;
     38 import com.android.launcher3.uioverrides.DisplayRotationListener;
     39 import com.android.launcher3.uioverrides.WallpaperColorInfo;
     40 import com.android.launcher3.shortcuts.DeepShortcutManager;
     41 import com.android.launcher3.views.BaseDragLayer;
     42 
     43 /**
     44  * Extension of BaseActivity allowing support for drag-n-drop
     45  */
     46 public abstract class BaseDraggingActivity extends BaseActivity
     47         implements WallpaperColorInfo.OnChangeListener {
     48 
     49     private static final String TAG = "BaseDraggingActivity";
     50 
     51     // The Intent extra that defines whether to ignore the launch animation
     52     public static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
     53             "com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
     54 
     55     // When starting an action mode, setting this tag will cause the action mode to be cancelled
     56     // automatically when user interacts with the launcher.
     57     public static final Object AUTO_CANCEL_ACTION_MODE = new Object();
     58 
     59     private ActionMode mCurrentActionMode;
     60     protected boolean mIsSafeModeEnabled;
     61 
     62     private OnStartCallback mOnStartCallback;
     63 
     64     private int mThemeRes = R.style.LauncherTheme;
     65 
     66     private DisplayRotationListener mRotationListener;
     67 
     68     @Override
     69     protected void onCreate(Bundle savedInstanceState) {
     70         super.onCreate(savedInstanceState);
     71         mIsSafeModeEnabled = getPackageManager().isSafeMode();
     72         mRotationListener = new DisplayRotationListener(this, this::onDeviceRotationChanged);
     73 
     74         // Update theme
     75         WallpaperColorInfo wallpaperColorInfo = WallpaperColorInfo.getInstance(this);
     76         wallpaperColorInfo.addOnChangeListener(this);
     77         int themeRes = getThemeRes(wallpaperColorInfo);
     78         if (themeRes != mThemeRes) {
     79             mThemeRes = themeRes;
     80             setTheme(themeRes);
     81         }
     82     }
     83 
     84     @Override
     85     public void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo) {
     86         if (mThemeRes != getThemeRes(wallpaperColorInfo)) {
     87             recreate();
     88         }
     89     }
     90 
     91     protected int getThemeRes(WallpaperColorInfo wallpaperColorInfo) {
     92         if (wallpaperColorInfo.isDark()) {
     93             return wallpaperColorInfo.supportsDarkText() ?
     94                     R.style.LauncherThemeDark_DarKText : R.style.LauncherThemeDark;
     95         } else {
     96             return wallpaperColorInfo.supportsDarkText() ?
     97                     R.style.LauncherTheme_DarkText : R.style.LauncherTheme;
     98         }
     99     }
    100 
    101     @Override
    102     public void onActionModeStarted(ActionMode mode) {
    103         super.onActionModeStarted(mode);
    104         mCurrentActionMode = mode;
    105     }
    106 
    107     @Override
    108     public void onActionModeFinished(ActionMode mode) {
    109         super.onActionModeFinished(mode);
    110         mCurrentActionMode = null;
    111     }
    112 
    113     public boolean finishAutoCancelActionMode() {
    114         if (mCurrentActionMode != null && AUTO_CANCEL_ACTION_MODE == mCurrentActionMode.getTag()) {
    115             mCurrentActionMode.finish();
    116             return true;
    117         }
    118         return false;
    119     }
    120 
    121     public abstract BaseDragLayer getDragLayer();
    122 
    123     public abstract <T extends View> T getOverviewPanel();
    124 
    125     public abstract View getRootView();
    126 
    127     public abstract BadgeInfo getBadgeInfoForItem(ItemInfo info);
    128 
    129     public abstract void invalidateParent(ItemInfo info);
    130 
    131     public static BaseDraggingActivity fromContext(Context context) {
    132         if (context instanceof BaseDraggingActivity) {
    133             return (BaseDraggingActivity) context;
    134         }
    135         return ((BaseDraggingActivity) ((ContextWrapper) context).getBaseContext());
    136     }
    137 
    138     public Rect getViewBounds(View v) {
    139         int[] pos = new int[2];
    140         v.getLocationOnScreen(pos);
    141         return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight());
    142     }
    143 
    144     public final Bundle getActivityLaunchOptionsAsBundle(View v) {
    145         ActivityOptions activityOptions = getActivityLaunchOptions(v);
    146         return activityOptions == null ? null : activityOptions.toBundle();
    147     }
    148 
    149     public abstract ActivityOptions getActivityLaunchOptions(View v);
    150 
    151     public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
    152         if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
    153             Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
    154             return false;
    155         }
    156 
    157         // Only launch using the new animation if the shortcut has not opted out (this is a
    158         // private contract between launcher and may be ignored in the future).
    159         boolean useLaunchAnimation = (v != null) &&
    160                 !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
    161         Bundle optsBundle = useLaunchAnimation
    162                 ? getActivityLaunchOptionsAsBundle(v)
    163                 : null;
    164 
    165         UserHandle user = item == null ? null : item.user;
    166 
    167         // Prepare intent
    168         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    169         if (v != null) {
    170             intent.setSourceBounds(getViewBounds(v));
    171         }
    172         try {
    173             boolean isShortcut = Utilities.ATLEAST_MARSHMALLOW
    174                     && (item instanceof ShortcutInfo)
    175                     && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
    176                     || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
    177                     && !((ShortcutInfo) item).isPromise();
    178             if (isShortcut) {
    179                 // Shortcuts need some special checks due to legacy reasons.
    180                 startShortcutIntentSafely(intent, optsBundle, item);
    181             } else if (user == null || user.equals(Process.myUserHandle())) {
    182                 // Could be launching some bookkeeping activity
    183                 startActivity(intent, optsBundle);
    184             } else {
    185                 LauncherAppsCompat.getInstance(this).startActivityForProfile(
    186                         intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
    187             }
    188             getUserEventDispatcher().logAppLaunch(v, intent);
    189             return true;
    190         } catch (ActivityNotFoundException|SecurityException e) {
    191             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
    192             Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
    193         }
    194         return false;
    195     }
    196 
    197     private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
    198         try {
    199             StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
    200             try {
    201                 // Temporarily disable deathPenalty on all default checks. For eg, shortcuts
    202                 // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure
    203                 // is enabled by default on NYC.
    204                 StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
    205                         .penaltyLog().build());
    206 
    207                 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
    208                     String id = ((ShortcutInfo) info).getDeepShortcutId();
    209                     String packageName = intent.getPackage();
    210                     DeepShortcutManager.getInstance(this).startShortcut(
    211                             packageName, id, intent.getSourceBounds(), optsBundle, info.user);
    212                 } else {
    213                     // Could be launching some bookkeeping activity
    214                     startActivity(intent, optsBundle);
    215                 }
    216             } finally {
    217                 StrictMode.setVmPolicy(oldPolicy);
    218             }
    219         } catch (SecurityException e) {
    220             if (!onErrorStartingShortcut(intent, info)) {
    221                 throw e;
    222             }
    223         }
    224     }
    225 
    226     protected boolean onErrorStartingShortcut(Intent intent, ItemInfo info) {
    227         return false;
    228     }
    229 
    230     @Override
    231     protected void onStart() {
    232         super.onStart();
    233 
    234         if (mOnStartCallback != null) {
    235             mOnStartCallback.onActivityStart(this);
    236             mOnStartCallback = null;
    237         }
    238     }
    239 
    240     @Override
    241     protected void onDestroy() {
    242         super.onDestroy();
    243         WallpaperColorInfo.getInstance(this).removeOnChangeListener(this);
    244         mRotationListener.disable();
    245     }
    246 
    247     public <T extends BaseDraggingActivity> void setOnStartCallback(OnStartCallback<T> callback) {
    248         mOnStartCallback = callback;
    249     }
    250 
    251     protected void onDeviceProfileInitiated() {
    252         if (mDeviceProfile.isVerticalBarLayout()) {
    253             mRotationListener.enable();
    254             mDeviceProfile.updateIsSeascape(getWindowManager());
    255         } else {
    256             mRotationListener.disable();
    257         }
    258     }
    259 
    260     private void onDeviceRotationChanged() {
    261         if (mDeviceProfile.updateIsSeascape(getWindowManager())) {
    262             reapplyUi();
    263         }
    264     }
    265 
    266     protected abstract void reapplyUi();
    267 
    268     /**
    269      * Callback for listening for onStart
    270      */
    271     public interface OnStartCallback<T extends BaseDraggingActivity> {
    272 
    273         void onActivityStart(T activity);
    274     }
    275 }
    276