Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Launcher3: Add Circle To Search #469

Open
wants to merge 1 commit into
base: 15.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AndroidManifest-common.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
<uses-permission android:name="android.permission.INJECT_EVENTS" />
<uses-permission android:name="android.permission.REGISTER_STATS_PULL_ATOM"/>
<uses-permission android:name="android.permission.ACCESS_CONTEXTUAL_SEARCH"/>

<!-- AppLock -->
<uses-permission android:name="android.permission.MANAGE_APP_LOCK" />
Expand Down
1 change: 1 addition & 0 deletions privapp_whitelist_com.android.launcher3-ext.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@
<permission name="android.permission.REGISTER_STATS_PULL_ATOM"/>
<permission name="com.android.alarm.permission.SET_ALARM"/>
<permission name="com.crdroid.permission.START_FREEFORM"/>
<permission name="android.permission.ACCESS_CONTEXTUAL_SEARCH"/>
</privapp-permissions>
</permissions>
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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);
}

/**
Expand All @@ -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;
}
}

/**
Expand All @@ -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);
}
}
146 changes: 139 additions & 7 deletions quickstep/src/com/android/quickstep/util/AssistUtils.java
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -15,31 +16,162 @@
*/
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();
}

/**
* @return {@code true} if the override was handled, i.e. an assist surface was shown or the
* 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);
}
}
4 changes: 4 additions & 0 deletions res/values/cr_strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -423,4 +423,8 @@
<!-- Force monochrome icons -->
<string name="force_monochrome_icons_title">Force themed icons</string>
<string name="force_monochrome_icons_summary">Generate monochromatic icons, if not supported by the app (requires re-toggling of themed icons)</string>

<!-- Circle to Search -->
<string name="pref_allow_cts_title">Circle to Search</string>
<string name="pref_allow_cts_summary">Touch and hold the Home button or the navigation handle to search using the content on your screen.</string>
</resources>
7 changes: 7 additions & 0 deletions res/xml/launcher_misc_preferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@
android:min="0"
settings:units="px"
android:defaultValue="23" />

<SwitchPreferenceCompat
android:key="pref_allow_cts"
android:title="@string/pref_allow_cts_title"
android:summary="@string/pref_allow_cts_summary"
android:defaultValue="@*android:bool/config_searchAllEntrypointsEnabledDefault"
launcher:iconSpaceReserved="false" />

<androidx.preference.PreferenceScreen
android:persistent="false"
Expand Down
29 changes: 28 additions & 1 deletion src/com/android/launcher3/settings/SettingsMisc.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartScreenCallback;
import androidx.preference.PreferenceGroup.PreferencePositionCallback;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreferenceCompat;
import androidx.recyclerview.widget.RecyclerView;

import com.android.launcher3.BuildConfig;
Expand All @@ -61,6 +62,8 @@
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.SettingsCache;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.AssistUtils;

import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;

Expand Down Expand Up @@ -89,6 +92,11 @@ public class SettingsMisc extends CollapsingToolbarBaseActivity
public static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";

public static final String KEY_TRUST_APPS = "pref_trust_apps";
private static final String CTS_KEY = "pref_allow_cts";
private static boolean mContextualSearchDefValue;
private static boolean mCtsEnabled;

private static Context mContext;

@Override
protected void onCreate(Bundle savedInstanceState) {
Expand Down Expand Up @@ -121,16 +129,24 @@ protected void onCreate(Bundle savedInstanceState) {
// Display the fragment as the main content.
fm.beginTransaction().replace(com.android.settingslib.collapsingtoolbar.R.id.content_frame, f).commit();
}
mContext = getApplicationContext();
mContextualSearchDefValue = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_searchAllEntrypointsEnabledDefault);
LauncherPrefs.getPrefs(getApplicationContext()).registerOnSharedPreferenceChangeListener(this);
}

@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
switch (key) {
case DeviceProfile.KEY_PHONE_TASKBAR:
case Utilities.KEY_BLUR_DEPTH:
LauncherAppState.INSTANCE.executeIfCreated(app -> 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;
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down