content: /*
   * Copyright (C) 2018 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.launcher3;
  
  import android.app.ActivityOptions;
  import android.content.ActivityNotFoundException;
  import android.content.Intent;
  import android.content.res.Configuration;
  import android.graphics.Rect;
  import android.os.Bundle;
  import android.os.Process;
  import android.os.StrictMode;
  import android.os.UserHandle;
  import android.util.Log;
  import android.view.ActionMode;
  import android.view.View;
  
  import androidx.annotation.Nullable;
  
  import com.android.launcher3.LauncherSettings.Favorites;
  import com.android.launcher3.compat.LauncherAppsCompat;
  import com.android.launcher3.model.AppLaunchTracker;
  import com.android.launcher3.shortcuts.DeepShortcutManager;
  import com.android.launcher3.uioverrides.DisplayRotationListener;
  import com.android.launcher3.uioverrides.WallpaperColorInfo;
  import com.android.launcher3.util.PackageManagerHelper;
  import com.android.launcher3.util.Themes;
  
  import amirz.helpers.Settings;
  
  /**
   * Extension of BaseActivity allowing support for drag-n-drop
   */
  public abstract class BaseDraggingActivity extends BaseActivity
          implements WallpaperColorInfo.OnChangeListener {
  
      private static final String TAG = "BaseDraggingActivity";
  
      // When starting an action mode, setting this tag will cause the action mode to be cancelled
      // automatically when user interacts with the launcher.
      public static final Object AUTO_CANCEL_ACTION_MODE = new Object();
  
      private ActionMode mCurrentActionMode;
      protected boolean mIsSafeModeEnabled;
  
      private OnStartCallback mOnStartCallback;
  
      private int mThemeRes = R.style.AppTheme;
  
      private DisplayRotationListener mRotationListener;
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          mIsSafeModeEnabled = getPackageManager().isSafeMode();
          mRotationListener = new DisplayRotationListener(this, this::onDeviceRotationChanged);
  
          // Update theme
          WallpaperColorInfo wallpaperColorInfo = WallpaperColorInfo.getInstance(this);
          wallpaperColorInfo.addOnChangeListener(this);
          int themeRes = Themes.getActivityThemeRes(this);
          if (themeRes != mThemeRes) {
              mThemeRes = themeRes;
              setTheme(themeRes);
          }
      }
  
      @Override
      public void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo) {
          updateTheme();
      }
  
      @Override
      public void onConfigurationChanged(Configuration newConfig) {
          super.onConfigurationChanged(newConfig);
          updateTheme();
      }
  
      private void updateTheme() {
          if (mThemeRes != Themes.getActivityThemeRes(this)) {
              recreate();
          }
      }
  
      @Override
      public void onActionModeStarted(ActionMode mode) {
          super.onActionModeStarted(mode);
          mCurrentActionMode = mode;
      }
  
      @Override
      public void onActionModeFinished(ActionMode mode) {
          super.onActionModeFinished(mode);
          mCurrentActionMode = null;
      }
  
      @Override
      public boolean finishAutoCancelActionMode() {
          if (mCurrentActionMode != null && AUTO_CANCEL_ACTION_MODE == mCurrentActionMode.getTag()) {
              mCurrentActionMode.finish();
              return true;
          }
          return false;
      }
  
      public abstract <T extends View> T getOverviewPanel();
  
      public abstract View getRootView();
  
      public void returnToHomescreen() {
          // no-op
      }
  
      public Rect getViewBounds(View v) {
          int[] pos = new int[2];
          v.getLocationOnScreen(pos);
          return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight());
      }
  
      public final Bundle getActivityLaunchOptionsAsBundle(View v) {
          ActivityOptions activityOptions = getActivityLaunchOptions(v);
          return activityOptions == null ? null : activityOptions.toBundle();
      }
  
      public abstract ActivityOptions getActivityLaunchOptions(View v);
  
      public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item,
              @Nullable String sourceContainer) {
          if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
              Settings.showSnackBar(this, R.string.safemode_shortcut_error);
              return false;
          }
  
          Bundle optsBundle = (v != null) ? getActivityLaunchOptionsAsBundle(v) : null;
          UserHandle user = item == null ? null : item.user;
  
          // Prepare intent
          intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
          if (v != null) {
              intent.setSourceBounds(getViewBounds(v));
          }
          try {
              boolean isShortcut = (item instanceof WorkspaceItemInfo)
                      && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
                      || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
                      && !((WorkspaceItemInfo) item).isPromise();
              if (isShortcut) {
                  // Shortcuts need some special checks due to legacy reasons.
                  startShortcutIntentSafely(intent, optsBundle, item, sourceContainer);
              } else if (user == null || user.equals(Process.myUserHandle())) {
                  // Could be launching some bookkeeping activity
                  startActivity(intent, optsBundle);
                  AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(),
                          Process.myUserHandle(), sourceContainer);
              } else {
                  LauncherAppsCompat.getInstance(this).startActivityForProfile(
                          intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
                  AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(), user,
                          sourceContainer);
              }
              getUserEventDispatcher().logAppLaunch(v, intent);
              getStatsLogManager().logAppLaunch(v, intent);
              return true;
          } catch (NullPointerException|ActivityNotFoundException|SecurityException e) {
              Settings.showSnackBar(this, R.string.activity_not_found);
              Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
          }
          return false;
      }
  
      private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info,
              @Nullable String sourceContainer) {
          try {
              StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
              try {
                  // Temporarily disable deathPenalty on all default checks. For eg, shortcuts
                  // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure
                  // is enabled by default on NYC.
                  StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
                          .penaltyLog().build());
  
                  if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
                      String id = ((WorkspaceItemInfo) info).getDeepShortcutId();
                      String packageName = intent.getPackage();
                      DeepShortcutManager.getInstance(this).startShortcut(
                              packageName, id, intent.getSourceBounds(), optsBundle, info.user);
                      AppLaunchTracker.INSTANCE.get(this).onStartShortcut(packageName, id, info.user,
                              sourceContainer);
                  } else {
                      // Could be launching some bookkeeping activity
                      startActivity(intent, optsBundle);
                  }
              } finally {
                  StrictMode.setVmPolicy(oldPolicy);
              }
          } catch (SecurityException e) {
              if (!onErrorStartingShortcut(intent, info)) {
                  throw e;
              }
          }
      }
  
      protected boolean onErrorStartingShortcut(Intent intent, ItemInfo info) {
          return false;
      }
  
      @Override
      protected void onStart() {
          super.onStart();
  
          if (mOnStartCallback != null) {
              mOnStartCallback.onActivityStart(this);
              mOnStartCallback = null;
          }
      }
  
      @Override
      protected void onDestroy() {
          super.onDestroy();
          WallpaperColorInfo.getInstance(this).removeOnChangeListener(this);
          mRotationListener.disable();
      }
  
      public <T extends BaseDraggingActivity> void setOnStartCallback(OnStartCallback<T> callback) {
          mOnStartCallback = callback;
      }
  
      protected void onDeviceProfileInitiated() {
          if (mDeviceProfile.isVerticalBarLayout()) {
              mRotationListener.enable();
              mDeviceProfile.updateIsSeascape(this);
          } else {
              mRotationListener.disable();
          }
      }
  
      private void onDeviceRotationChanged() {
          if (mDeviceProfile.updateIsSeascape(this)) {
              reapplyUi();
          }
      }
  
      protected abstract void reapplyUi();
  
      /**
       * Callback for listening for onStart
       */
      public interface OnStartCallback<T extends BaseDraggingActivity> {
  
          void onActivityStart(T activity);
      }
  }
  