Launcher3: Add Google Lens scan button to recents

And a squash of:

Launcher3: Restart launcher on changing google search/lens app

Launcher3: Make lens available without lens package

* Google search package is capable of lens functionality. Who knew? :)

idoybh (YAAP): Keep clear all rightmost

Co-authored-by: Pranav Vashi <neobuddy89@gmail.com>
Co-authored-by: hundeva <hundeva@gmail.com>
Change-Id: I8ba82c0d33b704d4a9d4fd20ab6552d054caa8eb
Signed-off-by: Pranav <npv12@iitbbs.ac.in>
diff --git a/.gitignore b/.gitignore
index 694b40c..85bd93a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,4 @@
 build/
 gradlew*
 .DS_Store
+.settings
diff --git a/quickstep/res/drawable/ic_lens.xml b/quickstep/res/drawable/ic_lens.xml
new file mode 100644
index 0000000..e372e83
--- /dev/null
+++ b/quickstep/res/drawable/ic_lens.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector android:height="24.0dip" android:width="24.0dip" android:viewportWidth="24.0" android:viewportHeight="24.0"
+  xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#ff000000" android:pathData="M8.4445,5.7778C6.9689,5.7778 5.7778,6.9689 5.7778,8.4445L5.7778,10.2222L4,10.2222L4,8.4445C4,5.9911 5.9911,4 8.4445,4L10.2222,4v1.7778zM17.3333,17.3333m-1.7778,0a1.7778,1.7778 0,1 1,3.5555 0,1.7778 1.7778,0 1,1 -3.5555,0M12,12m-3.1111,0a3.1111,3.1111 0,1 1,6.2222 0,3.1111 3.1111,0 1,1 -6.2222,0M8.4444,18.2222c-1.4755,0 -2.6667,-1.1911 -2.6667,-2.6667L5.7777,13.7778L4,13.7778v1.7778C4,18.0089 5.9911,20 8.4445,20L10.2222,20L10.2222,18.2222ZM15.5555,5.7778c1.4755,0 2.6667,1.1911 2.6667,2.6667L18.2222,10.2222L20,10.2222L20,8.4445C20,5.9911 18.0089,4 15.5555,4h-1.7778v1.7778z" />
+</vector>
\ No newline at end of file
diff --git a/quickstep/res/layout/overview_actions_container.xml b/quickstep/res/layout/overview_actions_container.xml
index fcd2e54..36b37e5 100644
--- a/quickstep/res/layout/overview_actions_container.xml
+++ b/quickstep/res/layout/overview_actions_container.xml
@@ -45,6 +45,22 @@
             android:theme="@style/ThemeControlHighlightWorkspaceColor"
             android:visibility="gone" />
 
+        <Button
+            android:drawableStart="@drawable/ic_lens"
+            style="@style/OverviewActionButton"
+            android:id="@+id/action_lens"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/action_lens"
+            android:theme="@style/ThemeControlHighlightWorkspaceColor"
+            android:visibility="gone" />
+
+        <Space
+            android:id="@+id/lens_space"
+            android:layout_width="0dp"
+            android:layout_height="1dp"
+            android:layout_weight="1"
+            android:visibility="gone" />
     </LinearLayout>
 
     <!-- Currently, the only "group action button" is this save app pair button. If more are added,
@@ -59,4 +75,4 @@
         android:layout_gravity="bottom|center_horizontal"
         android:visibility="gone" />
 
-</com.android.quickstep.views.OverviewActionsView>
\ No newline at end of file
+</com.android.quickstep.views.OverviewActionsView>
diff --git a/quickstep/src/com/android/quickstep/ImageActionsApi.java b/quickstep/src/com/android/quickstep/ImageActionsApi.java
index 2273806..b7acd29 100644
--- a/quickstep/src/com/android/quickstep/ImageActionsApi.java
+++ b/quickstep/src/com/android/quickstep/ImageActionsApi.java
@@ -31,6 +31,7 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.util.Log;
+import android.view.View;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
@@ -108,6 +109,11 @@
         ImageActionUtils.startShareActivity(mContext, mBitmapSupplier, crop, null, TAG);
     }
 
+    @UiThread
+    public void startLensActivity() {
+        ImageActionUtils.startLensActivity(mContext, mBitmapSupplier, null, null, TAG);
+    }
+
     /**
      * @param screenshot       to be saved to the media store.
      * @param screenshotBounds the location of where the bitmap was laid out on the screen in
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index b183ae3..128a3c1 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -331,6 +331,14 @@
             public void onSaveAppPair() {
                 endLiveTileMode(TaskOverlay.this::saveAppPair);
             }
+
+            public void onLens() {
+                if (mIsAllowedByPolicy) {
+                    endLiveTileMode(() -> mImageApi.startLensActivity());
+                } else {
+                    showBlockedByPolicyMessage();
+                }
+            }
         }
     }
 
@@ -347,5 +355,7 @@
 
         /** User wants to save an app pair with current group of apps. */
         void onSaveAppPair();
+
+        void onLens();
     }
 }
diff --git a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
index 3a1c99b..b39c93e 100644
--- a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
+++ b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
@@ -45,12 +45,14 @@
 import android.util.Log;
 import android.view.View;
 
+import androidx.annotation.UiThread;
 import androidx.annotation.WorkerThread;
 import androidx.core.content.FileProvider;
 
 import com.android.internal.app.ChooserActivity;
 import com.android.internal.util.ScreenshotRequest;
 import com.android.launcher3.BuildConfig;
+import com.android.launcher3.Utilities;
 import com.android.quickstep.SystemUiProxy;
 import com.android.systemui.shared.recents.model.Task;
 
@@ -159,6 +161,19 @@
         });
     }
 
+    @UiThread
+    public static void startLensActivity(Context context, Supplier<Bitmap> bitmapSupplier,
+            Rect crop, Intent intent, String tag) {
+        if (bitmapSupplier.get() == null) {
+            Log.e(tag, "No snapshot available, not starting share.");
+            return;
+        }
+
+        UI_HELPER_EXECUTOR.execute(() -> persistBitmapAndStartActivity(context,
+                bitmapSupplier.get(), crop, intent, ImageActionUtils::getLensIntentForImageUri,
+                tag));
+    }
+
     /**
      * Starts activity based on given intent created from image uri.
      */
@@ -299,6 +314,24 @@
         return new Intent[]{Intent.createChooser(intent, null).addFlags(FLAG_ACTIVITY_NEW_TASK)};
     }
 
+    @WorkerThread
+    private static Intent[] getLensIntentForImageUri(Uri uri, Intent intent) {
+        if (intent == null) {
+            intent = new Intent();
+        }
+        ClipData clipdata = new ClipData(new ClipDescription("content",
+                new String[]{"image/png"}),
+                new ClipData.Item(uri));
+        intent.setAction(Intent.ACTION_SEND)
+                .setComponent(new ComponentName(Utilities.GSA_PACKAGE, Utilities.LENS_SHARE_ACTIVITY))
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                .addFlags(FLAG_GRANT_READ_URI_PERMISSION)
+                .setType("image/png")
+                .putExtra(Intent.EXTRA_STREAM, uri)
+                .setClipData(clipdata);
+        return new Intent[]{Intent.createChooser(intent, null).addFlags(FLAG_ACTIVITY_NEW_TASK)};
+    }
+
     private static void clearOldCacheFiles(Context context) {
         THREAD_POOL_EXECUTOR.execute(() -> {
             File parent = new File(context.getCacheDir(), SUB_FOLDER);
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index d9468c7..4285787 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -33,6 +33,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Flags;
 import com.android.launcher3.Insettable;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.AnimatedFloat;
 import com.android.launcher3.util.DisplayController;
@@ -181,6 +182,13 @@
         mSplitButton = findViewById(R.id.action_split);
         mSplitButton.setOnClickListener(this);
         mSaveAppPairButton.setOnClickListener(this);
+
+        if (Utilities.isGSAEnabled(getContext())) {
+            View lens = findViewById(R.id.action_lens);
+            findViewById(R.id.action_lens).setOnClickListener(this);
+            lens.setVisibility(VISIBLE);
+            findViewById(R.id.lens_space).setVisibility(VISIBLE);
+        }
     }
 
     /**
@@ -204,6 +212,8 @@
             mCallbacks.onSplit();
         } else if (id == R.id.action_save_app_pair) {
             mCallbacks.onSaveAppPair();
+        } else if (id == R.id.action_lens) {
+            mCallbacks.onLens();
         }
     }
 
diff --git a/res/values/leaf_strings.xml b/res/values/leaf_strings.xml
new file mode 100644
index 0000000..d0c8f5c
--- /dev/null
+++ b/res/values/leaf_strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2022 The LeafOS 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.
+*/
+-->
+<resources>
+    <!-- Recents -->
+    <string name="action_lens">Lens</string>
+</resources>
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 19a3002..fb737da 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -24,6 +24,7 @@
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
 import android.annotation.SuppressLint;
 import android.app.ActivityManager;
@@ -31,6 +32,7 @@
 import android.app.Person;
 import android.app.WallpaperManager;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
 import android.content.pm.ShortcutInfo;
@@ -138,6 +140,8 @@
     public static final boolean ATLEAST_V = Build.VERSION.SDK_INT
             >= VERSION_CODES.VANILLA_ICE_CREAM;
 
+    private static final long WAIT_BEFORE_RESTART = 250;
+
     /**
      * Set on a motion event dispatched from the nav bar. See {@link MotionEvent#setEdgeFlags(int)}.
      */
@@ -162,6 +166,9 @@
     @IntDef({TRANSLATE_UP, TRANSLATE_DOWN, TRANSLATE_LEFT, TRANSLATE_RIGHT})
     public @interface AdjustmentDirection{}
 
+    public static final String GSA_PACKAGE = "com.google.android.googlequicksearchbox";
+    public static final String LENS_SHARE_ACTIVITY = "com.google.android.apps.search.lens.LensShareEntryPointActivity";
+
     /**
      * Returns true if theme is dark.
      */
@@ -864,4 +871,22 @@
         }
         return null;
     }
+
+    public static void restart(final Context context) {
+        MODEL_EXECUTOR.execute(() -> {
+            try {
+                Thread.sleep(WAIT_BEFORE_RESTART);
+            } catch (Exception ignored) {
+            }
+            android.os.Process.killProcess(android.os.Process.myPid());
+        });
+    }
+
+    public static boolean isGSAEnabled(Context context) {
+        try {
+            return context.getPackageManager().getApplicationInfo(GSA_PACKAGE, 0).enabled;
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 079987b..8754ade 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -39,6 +39,7 @@
 import com.android.launcher3.LauncherModel.ModelUpdateTask;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.logging.FileLog;
@@ -117,6 +118,7 @@
                 : ItemInfoMatcher.ofPackages(packageSet, mUser);
         final HashSet<ComponentName> removedComponents = new HashSet<>();
         final HashMap<String, List<LauncherActivityInfo>> activitiesLists = new HashMap<>();
+        boolean needsRestart = false;
         if (DEBUG) {
             Log.d(TAG, "Package updated: mOp=" + getOpString()
                     + " packages=" + Arrays.toString(packages));
@@ -124,6 +126,9 @@
         switch (mOp) {
             case OP_ADD: {
                 for (int i = 0; i < N; i++) {
+                    if (isTargetPackage(packages[i])) {
+                        needsRestart = true;
+                    }
                     iconCache.updateIconsForPkg(packages[i], mUser);
                     if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
                         if (DEBUG) {
@@ -158,6 +163,9 @@
             case OP_REMOVE: {
                 for (int i = 0; i < N; i++) {
                     iconCache.removeIconsForPkg(packages[i], mUser);
+                    if (isTargetPackage(packages[i])) {
+                        needsRestart = true;
+                    }
                 }
                 // Fall through
             }
@@ -175,6 +183,11 @@
                 flagOp = FlagOp.NO_OP.setFlag(
                         WorkspaceItemInfo.FLAG_DISABLED_SUSPENDED, mOp == OP_SUSPEND);
                 appsList.updateDisabledFlags(matcher, flagOp);
+                for (int i = 0; i < N; i++) {
+                    if (isTargetPackage(packages[i])) {
+                        needsRestart = true;
+                    }
+                }
                 break;
             case OP_USER_AVAILABILITY_CHANGE: {
                 UserManagerState ums = new UserManagerState();
@@ -428,6 +441,10 @@
             }
             taskController.bindUpdatedWidgets(dataModel);
         }
+
+        if (needsRestart) {
+            Utilities.restart(context);
+        }
     }
 
     /**
@@ -464,5 +481,9 @@
             case OP_USER_AVAILABILITY_CHANGE -> "USER_AVAILABILITY_CHANGE";
             default -> "UNKNOWN";
         };
+   }
+
+    private boolean isTargetPackage(String packageName) {
+        return packageName.equals(Utilities.GSA_PACKAGE);
     }
 }