Merge "Use correct config.smallestScreenWidthDp when creating Resource in DeviceProfile" into tm-dev
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 6bb20fc..a6b8897 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -3872,9 +3872,10 @@
         float taskSplitScrollOffsetPrimary = 0f;
         float clearAllSplitScrollOffsetPrimar = 0f;
         if (isSplitPlaceholderFirstInGrid()) {
-            taskSplitScrollOffsetPrimary = mSplitPlaceholderSize;
+            taskSplitScrollOffsetPrimary = mIsRtl ? mSplitPlaceholderSize : -mSplitPlaceholderSize;
         } else if (isSplitPlaceholderLastInGrid()) {
-            clearAllSplitScrollOffsetPrimar = -mSplitPlaceholderSize;
+            clearAllSplitScrollOffsetPrimar =
+                    mIsRtl ? -mSplitPlaceholderSize : mSplitPlaceholderSize;
         }
 
         for (int i = 0; i < getTaskViewCount(); i++) {
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 42f7053..73d3e33 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_WIDGETS;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -25,12 +24,7 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
-import android.content.pm.LauncherApps;
 import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.util.Log;
 
 import androidx.annotation.IntDef;
 
@@ -39,7 +33,7 @@
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.ViewCache;
-import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.AppLauncher;
 import com.android.launcher3.views.ScrimView;
 
 import java.io.PrintWriter;
@@ -50,7 +44,7 @@
 /**
  * Launcher BaseActivity
  */
-public abstract class BaseActivity extends Activity implements ActivityContext,
+public abstract class BaseActivity extends Activity implements AppLauncher,
         DeviceProfileListenable {
 
     private static final String TAG = "BaseActivity";
@@ -314,22 +308,6 @@
         writer.println(prefix + "mForceInvisible: " + mForceInvisible);
     }
 
-    /**
-     * A wrapper around the platform method with Launcher specific checks
-     */
-    public void startShortcut(String packageName, String id, Rect sourceBounds,
-            Bundle startActivityOptions, UserHandle user) {
-        if (GO_DISABLE_WIDGETS) {
-            return;
-        }
-        try {
-            getSystemService(LauncherApps.class).startShortcut(packageName, id, sourceBounds,
-                    startActivityOptions, user);
-        } catch (SecurityException | IllegalStateException e) {
-            Log.e(TAG, "Failed to start shortcut", e);
-        }
-    }
-
     public static <T extends BaseActivity> T fromContext(Context context) {
         if (context instanceof BaseActivity) {
             return (T) context;
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index f53d01e..3af43c0 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -16,50 +16,33 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
 import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
-import android.app.ActivityOptions;
 import android.app.WallpaperColors;
 import android.app.WallpaperManager;
 import android.app.WallpaperManager.OnColorsChangedListener;
-import android.content.ActivityNotFoundException;
 import android.content.Context;
-import android.content.Intent;
-import android.content.pm.LauncherApps;
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
 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.Display;
 import android.view.View;
-import android.widget.Toast;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.allapps.ActivityAllAppsContainerView;
 import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider;
 import com.android.launcher3.allapps.search.SearchAdapterProvider;
-import com.android.launcher3.logging.InstanceId;
-import com.android.launcher3.logging.InstanceIdSequence;
-import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.util.ActivityOptionsWrapper;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
 import com.android.launcher3.util.DisplayController.Info;
-import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.TraceHelper;
@@ -162,112 +145,12 @@
         // no-op
     }
 
+    @Override
     @NonNull
     public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
-        int left = 0, top = 0;
-        int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
-        if (v instanceof BubbleTextView) {
-            // Launch from center of icon, not entire view
-            Drawable icon = ((BubbleTextView) v).getIcon();
-            if (icon != null) {
-                Rect bounds = icon.getBounds();
-                left = (width - bounds.width()) / 2;
-                top = v.getPaddingTop();
-                width = bounds.width();
-                height = bounds.height();
-            }
-        }
-        ActivityOptions options =
-                ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
-
-        options.setLaunchDisplayId(
-                (v != null && v.getDisplay() != null) ? v.getDisplay().getDisplayId()
-                        : Display.DEFAULT_DISPLAY);
-        RunnableList callback = new RunnableList();
-        addOnResumeCallback(callback::executeAllAndDestroy);
-        return new ActivityOptionsWrapper(options, callback);
-    }
-
-    public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item) {
-        if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
-            Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
-            return false;
-        }
-
-        Bundle optsBundle = (v != null) ? getActivityLaunchOptions(v, item).toBundle() : null;
-        UserHandle user = item == null ? null : item.user;
-
-        // Prepare intent
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        if (v != null) {
-            intent.setSourceBounds(Utilities.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);
-            } else if (user == null || user.equals(Process.myUserHandle())) {
-                // Could be launching some bookkeeping activity
-                startActivity(intent, optsBundle);
-            } else {
-                getSystemService(LauncherApps.class).startMainActivity(
-                        intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
-            }
-            if (item != null) {
-                InstanceId instanceId = new InstanceIdSequence().newInstanceId();
-                logAppLaunch(getStatsLogManager(), item, instanceId);
-            }
-            return true;
-        } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
-            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
-            Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
-        }
-        return false;
-    }
-
-    /**
-     * Creates and logs a new app launch event.
-     */
-    public void logAppLaunch(StatsLogManager statsLogManager, ItemInfo info,
-            InstanceId instanceId) {
-        statsLogManager.logger().withItemInfo(info).withInstanceId(instanceId)
-                .log(LAUNCHER_APP_LAUNCH_TAP);
-    }
-
-    private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
-        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();
-                    startShortcut(packageName, id, intent.getSourceBounds(), optsBundle, info.user);
-                } 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;
+        ActivityOptionsWrapper wrapper = super.getActivityLaunchOptions(v, item);
+        addOnResumeCallback(wrapper.onEndCallback::executeAllAndDestroy);
+        return wrapper;
     }
 
     @Override
@@ -337,4 +220,9 @@
             ActivityAllAppsContainerView<?> allApps) {
         return new DefaultSearchAdapterProvider(this);
     }
+
+    @Override
+    public boolean isAppBlockedForSafeMode() {
+        return mIsSafeModeEnabled;
+    }
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 5c406bd..2f9b563 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2065,7 +2065,7 @@
 
     @TargetApi(Build.VERSION_CODES.M)
     @Override
-    protected boolean onErrorStartingShortcut(Intent intent, ItemInfo info) {
+    public boolean onErrorStartingShortcut(Intent intent, ItemInfo info) {
         // Due to legacy reasons, direct call shortcuts require Launchers to have the
         // corresponding permission. Show the appropriate permission prompt if that
         // is the case.
diff --git a/src/com/android/launcher3/views/AppLauncher.java b/src/com/android/launcher3/views/AppLauncher.java
new file mode 100644
index 0000000..19e66ab
--- /dev/null
+++ b/src/com/android/launcher3/views/AppLauncher.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2022 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.views;
+
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
+import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_WIDGETS;
+
+import android.app.ActivityOptions;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.StrictMode;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.Display;
+import android.view.View;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.InstanceIdSequence;
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.util.ActivityOptionsWrapper;
+import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.RunnableList;
+
+/** An {@link ActivityContext} that can also launch app activities and shortcuts safely. */
+public interface AppLauncher extends ActivityContext {
+
+    String TAG = "AppLauncher";
+
+    /**
+     * Safely starts an activity.
+     *
+     * @param v View starting the activity.
+     * @param intent Base intent being launched.
+     * @param item Item associated with the view.
+     * @return {@code true} if the activity starts successfully.
+     */
+    default boolean startActivitySafely(
+            View v, Intent intent, @Nullable ItemInfo item) {
+
+        Context context = (Context) this;
+        if (isAppBlockedForSafeMode() && !PackageManagerHelper.isSystemApp(context, intent)) {
+            Toast.makeText(context, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
+            return false;
+        }
+
+        Bundle optsBundle = (v != null) ? getActivityLaunchOptions(v, item).toBundle() : null;
+        UserHandle user = item == null ? null : item.user;
+
+        // Prepare intent
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        if (v != null) {
+            intent.setSourceBounds(Utilities.getViewBounds(v));
+        }
+        try {
+            boolean isShortcut = (item instanceof WorkspaceItemInfo)
+                    && (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
+                    || item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)
+                    && !((WorkspaceItemInfo) item).isPromise();
+            if (isShortcut) {
+                // Shortcuts need some special checks due to legacy reasons.
+                startShortcutIntentSafely(intent, optsBundle, item);
+            } else if (user == null || user.equals(Process.myUserHandle())) {
+                // Could be launching some bookkeeping activity
+                context.startActivity(intent, optsBundle);
+            } else {
+                context.getSystemService(LauncherApps.class).startMainActivity(
+                        intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
+            }
+            if (item != null) {
+                InstanceId instanceId = new InstanceIdSequence().newInstanceId();
+                logAppLaunch(getStatsLogManager(), item, instanceId);
+            }
+            return true;
+        } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
+            Toast.makeText(context, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+            Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
+        }
+        return false;
+    }
+
+    /** Returns {@code true} if an app launch is blocked due to safe mode. */
+    default boolean isAppBlockedForSafeMode() {
+        return false;
+    }
+
+    /**
+     * Creates and logs a new app launch event.
+     */
+    default void logAppLaunch(StatsLogManager statsLogManager, ItemInfo info,
+            InstanceId instanceId) {
+        statsLogManager.logger().withItemInfo(info).withInstanceId(instanceId)
+                .log(LAUNCHER_APP_LAUNCH_TAP);
+    }
+
+    /**
+     * Returns launch options for an Activity.
+     *
+     * @param v View initiating a launch.
+     * @param item Item associated with the view.
+     */
+    default ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
+        int left = 0, top = 0;
+        int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
+        if (v instanceof BubbleTextView) {
+            // Launch from center of icon, not entire view
+            Drawable icon = ((BubbleTextView) v).getIcon();
+            if (icon != null) {
+                Rect bounds = icon.getBounds();
+                left = (width - bounds.width()) / 2;
+                top = v.getPaddingTop();
+                width = bounds.width();
+                height = bounds.height();
+            }
+        }
+        ActivityOptions options =
+                ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
+
+        options.setLaunchDisplayId(
+                (v != null && v.getDisplay() != null) ? v.getDisplay().getDisplayId()
+                        : Display.DEFAULT_DISPLAY);
+        RunnableList callback = new RunnableList();
+        return new ActivityOptionsWrapper(options, callback);
+    }
+
+    /**
+     * Safely launches an intent for a shortcut.
+     *
+     * @param intent Intent to start.
+     * @param optsBundle Optional launch arguments.
+     * @param info Shortcut information.
+     */
+    default void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
+        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();
+                    startShortcut(packageName, id, intent.getSourceBounds(), optsBundle, info.user);
+                } else {
+                    // Could be launching some bookkeeping activity
+                    ((Context) this).startActivity(intent, optsBundle);
+                }
+            } finally {
+                StrictMode.setVmPolicy(oldPolicy);
+            }
+        } catch (SecurityException e) {
+            if (!onErrorStartingShortcut(intent, info)) {
+                throw e;
+            }
+        }
+    }
+
+    /**
+     * A wrapper around the platform method with Launcher specific checks.
+     */
+    default void startShortcut(String packageName, String id, Rect sourceBounds,
+            Bundle startActivityOptions, UserHandle user) {
+        if (GO_DISABLE_WIDGETS) {
+            return;
+        }
+        try {
+            ((Context) this).getSystemService(LauncherApps.class).startShortcut(packageName, id,
+                    sourceBounds, startActivityOptions, user);
+        } catch (SecurityException | IllegalStateException e) {
+            Log.e(TAG, "Failed to start shortcut", e);
+        }
+    }
+
+    /**
+     * Invoked when a shortcut fails to launch.
+     * @param intent Shortcut intent that failed to start.
+     * @param info Shortcut information.
+     * @return {@code true} if the error is handled by this callback.
+     */
+    default boolean onErrorStartingShortcut(Intent intent, ItemInfo info) {
+        return false;
+    }
+}