diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index b5bd354567a..103594ad370 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -60,6 +60,7 @@
+
diff --git a/privapp_whitelist_com.android.launcher3-ext.xml b/privapp_whitelist_com.android.launcher3-ext.xml
index 99fad541464..9b957898cea 100644
--- a/privapp_whitelist_com.android.launcher3-ext.xml
+++ b/privapp_whitelist_com.android.launcher3-ext.xml
@@ -26,5 +26,6 @@
+
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java
index 1d00e533f5c..d72bc907526 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 Neoteric OS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,23 +17,42 @@
package com.android.quickstep.inputconsumers;
+import android.app.contextualsearch.ContextualSearchManager;
import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.VibrationEffect;
+import android.view.ViewConfiguration;
import androidx.annotation.Nullable;
-import com.android.launcher3.R;
-import com.android.launcher3.util.ResourceBasedOverride;
+import com.android.launcher3.util.VibratorWrapper;
import com.android.quickstep.NavHandle;
+import com.android.quickstep.util.AssistUtils;
/**
* Class for extending nav handle long press behavior
*/
-public class NavHandleLongPressHandler implements ResourceBasedOverride {
+public class NavHandleLongPressHandler {
+
+ private Context mContext;
+ private AssistUtils mAssistUtils;
+
+ private static final VibrationEffect EFFECT_HEAVY_CLICK =
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK);
+ private static final VibrationEffect EFFECT_TICK =
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK);
+
+ private static final Handler mHandler = new Handler(Looper.getMainLooper());
+
+ public NavHandleLongPressHandler(Context context) {
+ mContext = context;
+ mAssistUtils = new AssistUtils(mContext);
+ }
/** Creates NavHandleLongPressHandler as specified by overrides */
public static NavHandleLongPressHandler newInstance(Context context) {
- return Overrides.getObject(NavHandleLongPressHandler.class, context,
- R.string.nav_handle_long_press_handler_class);
+ return new NavHandleLongPressHandler(context);
}
/**
@@ -47,7 +67,23 @@ public static NavHandleLongPressHandler newInstance(Context context) {
* @param navHandle to handle this long press
*/
public @Nullable Runnable getLongPressRunnable(NavHandle navHandle) {
- return null;
+ if (mAssistUtils.canDoContextualSearch()) {
+ VibratorWrapper.INSTANCE.get(mContext).vibrate(EFFECT_TICK);
+ navHandle.animateNavBarLongPress(true, true, 200L);
+ return new Runnable() {
+ @Override
+ public final void run() {
+ mHandler.postDelayed(() -> {
+ if (mAssistUtils.invokeContextualSearch(
+ ContextualSearchManager.ENTRYPOINT_LONG_PRESS_NAV_HANDLE)) {
+ VibratorWrapper.INSTANCE.get(mContext).vibrate(EFFECT_HEAVY_CLICK);
+ }
+ }, ViewConfiguration.getLongPressTimeout());
+ }
+ };
+ } else {
+ return null;
+ }
}
/**
@@ -64,5 +100,7 @@ public void onTouchStarted(NavHandle navHandle) {}
* @param navHandle to handle the animation for this touch
* @param reason why the touch ended
*/
- public void onTouchFinished(NavHandle navHandle, String reason) {}
+ public void onTouchFinished(NavHandle navHandle, String reason) {
+ navHandle.animateNavBarLongPress(false, true, 200L);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/AssistUtils.java b/quickstep/src/com/android/quickstep/util/AssistUtils.java
index 11b6ea7d213..8a39000aeab 100644
--- a/quickstep/src/com/android/quickstep/util/AssistUtils.java
+++ b/quickstep/src/com/android/quickstep/util/AssistUtils.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 Neoteric OS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,24 +16,91 @@
*/
package com.android.quickstep.util;
+import android.app.contextualsearch.ContextualSearchManager;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
-import com.android.launcher3.R;
-import com.android.launcher3.util.ResourceBasedOverride;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.SafeCloseable;
+import com.android.launcher3.util.SettingsCache;
+import com.android.launcher3.util.SimpleBroadcastReceiver;
+import com.android.quickstep.SystemUiProxy;
+
+import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS;
+
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
/** Utilities to work with Assistant functionality. */
-public class AssistUtils implements ResourceBasedOverride {
+public class AssistUtils implements SettingsCache.OnChangeListener, SafeCloseable {
+
+ private final String TAG = "AssistUtils";
+ private boolean DEBUG = false;
+
+ private Context mContext;
+ private ContextualSearchManager mContextualSearchManager;
+ private String mContextualSearchPkg;
+ private int mContextualSearchDefValue;
+ private Intent mCtsPkgIntent;
+
+ private final SimpleBroadcastReceiver mContextualSearchPkgReceiver =
+ new SimpleBroadcastReceiver(this::onPkgStateChanged);
+
+ private final long KEYGUARD_SHOWING_SYSUI_FLAGS = SYSUI_STATE_BOUNCER_SHOWING |
+ SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
+ private final long SHADE_EXPANDED_SYSUI_FLAGS = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED |
+ SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
- public AssistUtils() {}
+ private final int mCtsPkgQueryFlags = PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_ALL;
+
+ public AssistUtils(Context context) {
+ mContext = context;
+ mContextualSearchManager = (ContextualSearchManager) mContext.getSystemService(Context.CONTEXTUAL_SEARCH_SERVICE);
+ mContextualSearchPkg = mContext.getResources()
+ .getString(com.android.internal.R.string.config_defaultContextualSearchPackageName);
+ mContextualSearchDefValue = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_searchAllEntrypointsEnabledDefault) ? 1 : 0;
+ mCtsPkgIntent = new Intent(ContextualSearchManager.ACTION_LAUNCH_CONTEXTUAL_SEARCH)
+ .setPackage(mContextualSearchPkg);
+
+ // Register package actions
+ UI_HELPER_EXECUTOR.execute(() -> {
+ mContextualSearchPkgReceiver.registerPkgActions(mContext, mContextualSearchPkg,
+ Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_CHANGED,
+ Intent.ACTION_PACKAGE_REMOVED);
+ });
+
+ // Register settings cache listener
+ Uri contextualSearchUri = Settings.Secure.getUriFor(Settings.Secure.SEARCH_ALL_ENTRYPOINTS_ENABLED);
+ SettingsCache.INSTANCE.get(mContext).register(contextualSearchUri, this);
+ }
/** Creates AssistUtils as specified by overrides */
public static AssistUtils newInstance(Context context) {
- return Overrides.getObject(AssistUtils.class, context, R.string.assist_utils_class);
+ return new AssistUtils(context);
}
/** @return Array of AssistUtils.INVOCATION_TYPE_* that we want to handle instead of SysUI. */
public int[] getSysUiAssistOverrideInvocationTypes() {
- return new int[0];
+ if (mContextualSearchManager == null || !isContextualSearchIntentAvailable() ||
+ Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.SEARCH_ALL_ENTRYPOINTS_ENABLED, mContextualSearchDefValue) == 0) {
+ return new int[0];
+ }
+ IntArray invocationTypes = new IntArray();
+ invocationTypes.add(INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
+ return invocationTypes.toArray();
}
/**
@@ -40,6 +108,70 @@ public int[] getSysUiAssistOverrideInvocationTypes() {
* request should be ignored. {@code false} means the caller should start assist another way.
*/
public boolean tryStartAssistOverride(int invocationType) {
- return false;
+ return invocationType == INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS &&
+ invokeContextualSearch(ContextualSearchManager.ENTRYPOINT_LONG_PRESS_HOME);
+ }
+
+ public boolean invokeContextualSearch(int invocationType) {
+ if (!canDoContextualSearch()) {
+ return false;
+ }
+ if (DEBUG) Log.d(TAG, "invokeContextualSearch: Contextual Search should start now");
+ mContextualSearchManager.startContextualSearch(invocationType);
+ return true;
+ }
+
+ public boolean canDoContextualSearch() {
+ if (Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.SEARCH_ALL_ENTRYPOINTS_ENABLED, mContextualSearchDefValue) == 0) {
+ if (DEBUG) Log.d(TAG, "Contextual Search invocation failed: CTS setting disabled");
+ return false;
+ }
+
+ if (mContextualSearchManager == null) {
+ if (DEBUG) Log.d(TAG, "Contextual Search invocation failed: no ContextualSearchManager");
+ return false;
+ }
+
+ boolean isNotificationShadeShowing = (SystemUiProxy.INSTANCE.get(mContext)
+ .getLastSystemUiStateFlags() & SHADE_EXPANDED_SYSUI_FLAGS) != 0;
+ if (isNotificationShadeShowing) {
+ if (DEBUG) Log.d(TAG, "Contextual Search invocation failed: notification shade");
+ return false;
+ }
+
+ boolean isKeyguardShowing = (SystemUiProxy.INSTANCE.get(mContext)
+ .getLastSystemUiStateFlags() & KEYGUARD_SHOWING_SYSUI_FLAGS) != 0;
+ if (isKeyguardShowing) {
+ if (DEBUG) Log.d(TAG, "Contextual Search invocation failed: keyguard");
+ return false;
+ }
+
+ if (!isContextualSearchIntentAvailable()) {
+ if (DEBUG) Log.d(TAG, "Contextual Search invocation failed: Contextual Search intent not found");
+ return false;
+ }
+
+ return true;
+ }
+
+ public boolean isContextualSearchIntentAvailable() {
+ return !mContext.getPackageManager().queryIntentActivities(mCtsPkgIntent, mCtsPkgQueryFlags).isEmpty();
+ }
+
+ private void onPkgStateChanged(Intent intent) {
+ SystemUiProxy.INSTANCE.get(mContext).setAssistantOverridesRequested(getSysUiAssistOverrideInvocationTypes());
+ }
+
+ @Override
+ public void onSettingsChanged(boolean isEnabled) {
+ SystemUiProxy.INSTANCE.get(mContext).setAssistantOverridesRequested(getSysUiAssistOverrideInvocationTypes());
+ }
+
+ @Override
+ public void close() {
+ mContextualSearchPkgReceiver.unregisterReceiverSafely(mContext);
+ SettingsCache.INSTANCE.get(mContext)
+ .unregister(Settings.Secure.getUriFor(Settings.Secure.SEARCH_ALL_ENTRYPOINTS_ENABLED), this);
}
}
diff --git a/res/values/cr_strings.xml b/res/values/cr_strings.xml
index 651ca599def..78e1c94a47f 100644
--- a/res/values/cr_strings.xml
+++ b/res/values/cr_strings.xml
@@ -423,4 +423,8 @@
Force themed icons
Generate monochromatic icons, if not supported by the app (requires re-toggling of themed icons)
+
+
+ Circle to Search
+ Touch and hold the Home button or the navigation handle to search using the content on your screen.
diff --git a/res/xml/launcher_misc_preferences.xml b/res/xml/launcher_misc_preferences.xml
index 53cd084defa..3ce4c19676b 100644
--- a/res/xml/launcher_misc_preferences.xml
+++ b/res/xml/launcher_misc_preferences.xml
@@ -48,6 +48,13 @@
android:min="0"
settings:units="px"
android:defaultValue="23" />
+
+
app.setNeedsRestart());
break;
+ case CTS_KEY:
+ mCtsEnabled = LauncherPrefs.getPrefs(mContext).getBoolean(CTS_KEY, mContextualSearchDefValue);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.SEARCH_ALL_ENTRYPOINTS_ENABLED, mCtsEnabled ? 1 : 0);
+ break;
default:
break;
}
@@ -189,6 +205,8 @@ public static class MiscSettingsFragment extends PreferenceFragmentCompat implem
private String mHighLightKey;
private boolean mPreferenceHighlighted = false;
+ private SwitchPreferenceCompat mCtsPref;
+
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
if (BuildConfig.IS_DEBUG_DEVICE) {
@@ -223,6 +241,15 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
if (getActivity() != null && !TextUtils.isEmpty(getPreferenceScreen().getTitle())) {
getActivity().setTitle(getPreferenceScreen().getTitle());
}
+
+ mCtsPref = (SwitchPreferenceCompat) findPreference(CTS_KEY);
+ mCtsEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.SEARCH_ALL_ENTRYPOINTS_ENABLED, mContextualSearchDefValue ? 1 : 0) == 1;
+ if (!AssistUtils.newInstance(mContext).isContextualSearchIntentAvailable()) {
+ getPreferenceScreen().removePreference(mCtsPref);
+ } else {
+ mCtsPref.setChecked(mCtsEnabled);
+ }
}
@Override