Merge "Prevent crash when display is already removed by the time we get displayAdded" into ub-launcher3-master
diff --git a/go/AndroidManifest.xml b/go/AndroidManifest.xml
index f84a82e..f36439d 100644
--- a/go/AndroidManifest.xml
+++ b/go/AndroidManifest.xml
@@ -46,6 +46,12 @@
tools:node="replace" >
</activity>
+ <service
+ android:name="com.android.launcher3.notification.NotificationListener"
+ android:label="@string/notification_dots_service_title"
+ android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
+ android:enabled="false"
+ tools:node="replace" />
</application>
</manifest>
diff --git a/go/src/com/android/launcher3/model/WidgetsModel.java b/go/src/com/android/launcher3/model/WidgetsModel.java
index 3b3dc01..89b3831 100644
--- a/go/src/com/android/launcher3/model/WidgetsModel.java
+++ b/go/src/com/android/launcher3/model/WidgetsModel.java
@@ -41,6 +41,7 @@
// True is the widget support is disabled.
public static final boolean GO_DISABLE_WIDGETS = true;
+ public static final boolean GO_DISABLE_NOTIFICATION_DOTS = true;
private static final ArrayList<WidgetListRowEntry> EMPTY_WIDGET_LIST = new ArrayList<>();
diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
index cc7b712..810f4e3 100644
--- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java
+++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
@@ -43,9 +43,13 @@
import android.util.Log;
import androidx.annotation.MainThread;
+import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherProvider;
+import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.ItemInfo;
@@ -74,6 +78,9 @@
private static final int MSG_PACKAGE_REMOVED = 2;
private static final int MSG_FULL_REFRESH = 3;
+ private static final int UNKNOWN_MINIMAL_DEVICE_STATE = 0;
+ private static final int IN_MINIMAL_DEVICE = 2;
+
// Welbeing contract
private static final String PATH_ACTIONS = "actions";
private static final String PATH_MINIMAL_DEVICE = "minimal_device";
@@ -84,6 +91,8 @@
private static final String EXTRA_MAX_NUM_ACTIONS_SHOWN = "max_num_actions_shown";
private static final String EXTRA_PACKAGES = "packages";
private static final String EXTRA_SUCCESS = "success";
+ private static final String EXTRA_MINIMAL_DEVICE_STATE = "minimal_device_state";
+ private static final String DB_NAME_MINIMAL_DEVICE = "minimal.db";
public static final MainThreadInitializedObject<WellbeingModel> INSTANCE =
new MainThreadInitializedObject<>(WellbeingModel::new);
@@ -121,11 +130,12 @@
updateWellbeingData();
} else if (uri.getPath().contains(PATH_MINIMAL_DEVICE)) {
// Wellbeing reports that minimal device state or config is changed.
- updateLauncherModel();
+ updateLauncherModel(context);
}
}
};
- FeatureFlags.ENABLE_MINIMAL_DEVICE.addChangeListener(mContext, this::updateLauncherModel);
+ FeatureFlags.ENABLE_MINIMAL_DEVICE.addChangeListener(mContext, () ->
+ updateLauncherModel(context));
if (!TextUtils.isEmpty(mWellbeingProviderPkg)) {
context.registerReceiver(
@@ -170,7 +180,6 @@
Log.e(TAG, "Failed to register content observer for " + actionsUri + ": " + e);
if (mIsInTest) throw new RuntimeException(e);
}
-
updateWellbeingData();
}
@@ -208,10 +217,34 @@
mWorkerHandler.sendEmptyMessage(MSG_FULL_REFRESH);
}
- private void updateLauncherModel() {
- if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) return;
+ private void updateLauncherModel(@NonNull final Context context) {
+ if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) {
+ reloadLauncherInNormalMode(context);
+ return;
+ }
+ runWithMinimalDeviceConfigs((bundle) -> {
+ if (bundle.getInt(EXTRA_MINIMAL_DEVICE_STATE, UNKNOWN_MINIMAL_DEVICE_STATE)
+ == IN_MINIMAL_DEVICE) {
+ reloadLauncherInMinimalMode(context);
+ } else {
+ reloadLauncherInNormalMode(context);
+ }
+ });
+ }
- // TODO: init Launcher in minimal device / normal mode
+ private void reloadLauncherInNormalMode(@NonNull final Context context) {
+ LauncherSettings.Settings.call(context.getContentResolver(),
+ LauncherSettings.Settings.METHOD_SWITCH_DATABASE,
+ InvariantDeviceProfile.INSTANCE.get(context).dbFile);
+ }
+
+ private void reloadLauncherInMinimalMode(@NonNull final Context context) {
+ final Bundle extras = new Bundle();
+ extras.putString(LauncherProvider.KEY_LAYOUT_PROVIDER_AUTHORITY,
+ mWellbeingProviderPkg + ".api");
+ LauncherSettings.Settings.call(context.getContentResolver(),
+ LauncherSettings.Settings.METHOD_SWITCH_DATABASE,
+ DB_NAME_MINIMAL_DEVICE, extras);
}
private Uri.Builder apiBuilder() {
@@ -225,6 +258,9 @@
*/
@WorkerThread
private void runWithMinimalDeviceConfigs(Consumer<Bundle> consumer) {
+ if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) {
+ return;
+ }
if (DEBUG || mIsInTest) {
Log.d(TAG, "runWithMinimalDeviceConfigs() called");
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 1b8e244..aad7e17 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.graphics.OverviewScrim.SCRIM_MULTIPLIER;
import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL;
@@ -70,6 +71,7 @@
getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim();
SCRIM_PROGRESS.set(scrim, state.getOverviewScrimAlpha(mLauncher));
+ SCRIM_MULTIPLIER.set(scrim, 1f);
getTaskModalnessProperty().set(mRecentsView, state.getOverviewModalness());
}
@@ -108,6 +110,8 @@
OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim();
setter.setFloat(scrim, SCRIM_PROGRESS, toState.getOverviewScrimAlpha(mLauncher),
config.getInterpolator(ANIM_OVERVIEW_SCRIM_FADE, LINEAR));
+ setter.setFloat(scrim, SCRIM_MULTIPLIER, 1f,
+ config.getInterpolator(ANIM_OVERVIEW_SCRIM_FADE, LINEAR));
setter.setFloat(
mRecentsView, getTaskModalnessProperty(),
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index 57fd11a..da3c485 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -66,8 +66,8 @@
SingleAxisSwipeDetector.Listener {
private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL_3;
- // How much of the overview scrim we can remove during the transition.
- private static final float OVERVIEW_TO_HOME_SCRIM_PROGRESS = 0.5f;
+ // The min amount of overview scrim we keep during the transition.
+ private static final float OVERVIEW_TO_HOME_SCRIM_MULTIPLIER = 0.5f;
private final Launcher mLauncher;
private final SingleAxisSwipeDetector mSwipeDetector;
@@ -163,11 +163,11 @@
RecentsView recentsView = mLauncher.getOverviewPanel();
AnimatorControllerWithResistance.createRecentsResistanceFromOverviewAnim(mLauncher,
builder);
- float endScrimAlpha = Utilities.mapRange(OVERVIEW_TO_HOME_SCRIM_PROGRESS,
- mStartState.getOverviewScrimAlpha(mLauncher),
- mEndState.getOverviewScrimAlpha(mLauncher));
+
builder.setFloat(mLauncher.getDragLayer().getOverviewScrim(),
- OverviewScrim.SCRIM_PROGRESS, endScrimAlpha, PULLBACK_INTERPOLATOR);
+ OverviewScrim.SCRIM_MULTIPLIER, OVERVIEW_TO_HOME_SCRIM_MULTIPLIER,
+ PULLBACK_INTERPOLATOR);
+
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
builder.addOnFrameCallback(recentsView::redrawLiveTile);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index 3586b4f..df6194d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -82,7 +82,15 @@
mDetector = new SingleAxisSwipeDetector(activity, this, dir);
}
- private boolean canInterceptTouch() {
+ private boolean canInterceptTouch(MotionEvent ev) {
+ if ((ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) != 0) {
+ // Don't intercept swipes on the nav bar, as user might be trying to go home
+ // during a task dismiss animation.
+ if (mCurrentAnimation != null) {
+ mCurrentAnimation.getAnimationPlayer().end();
+ }
+ return false;
+ }
if (mCurrentAnimation != null) {
mCurrentAnimation.forceFinishIfCloseToEnd();
}
@@ -118,7 +126,7 @@
clearState();
}
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mNoIntercept = !canInterceptTouch();
+ mNoIntercept = !canInterceptTouch(ev);
if (mNoIntercept) {
return false;
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 5ec6377..ba0a81f 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -659,17 +659,17 @@
runningComponent != null && runningComponent.equals(homeComponent);
}
- if (gestureState.getRunningTask() == null) {
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()
+ && gestureState.getActivityInterface().isInLiveTileMode()) {
+ return createOverviewInputConsumer(
+ previousGestureState, gestureState, event, forceOverviewInputConsumer);
+ } else if (gestureState.getRunningTask() == null) {
return mResetGestureInputConsumer;
} else if (previousGestureState.isRunningAnimationToLauncher()
|| gestureState.getActivityInterface().isResumed()
|| forceOverviewInputConsumer) {
return createOverviewInputConsumer(
previousGestureState, gestureState, event, forceOverviewInputConsumer);
- } else if (ENABLE_QUICKSTEP_LIVE_TILE.get()
- && gestureState.getActivityInterface().isInLiveTileMode()) {
- return createOverviewInputConsumer(
- previousGestureState, gestureState, event, forceOverviewInputConsumer);
} else if (mDeviceState.isGestureBlockedActivity(gestureState.getRunningTask())) {
return mResetGestureInputConsumer;
} else {
diff --git a/res/layout/search_result_play_item.xml b/res/layout/search_result_play_item.xml
new file mode 100644
index 0000000..4e82eaf
--- /dev/null
+++ b/res/layout/search_result_play_item.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<com.android.launcher3.views.SearchResultPlayItem xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="8dp"
+ android:orientation="horizontal">
+ <View
+ android:id="@+id/icon"
+ android:layout_width="@dimen/deep_shortcut_icon_size"
+ android:layout_height="@dimen/deep_shortcut_icon_size"
+ android:layout_gravity="start|center_vertical"
+ android:background="@drawable/ic_deepshortcut_placeholder" />
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start|center_vertical"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:padding="8dp">
+
+ <TextView
+ android:id="@+id/title_view"
+ style="@style/TextHeadline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAlignment="viewStart"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="16sp" />
+
+ <TextView
+ android:id="@+id/detail_0"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="?android:attr/textColorPrimary" />
+
+ <TextView
+ android:id="@+id/detail_1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="?android:attr/textColorPrimary"
+ android:visibility="gone" />
+
+ <TextView
+ android:id="@+id/detail_2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="?android:attr/textColorPrimary"
+ android:visibility="gone" />
+ </LinearLayout>
+ <Button
+ android:id="@+id/try_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start|center_vertical"
+ android:background="?android:attr/selectableItemBackground"
+ android:text="@string/search_action_try_now">
+ </Button>
+
+
+</com.android.launcher3.views.SearchResultPlayItem>
diff --git a/res/layout/search_section_title.xml b/res/layout/search_section_title.xml
index c39a641..9419015 100644
--- a/res/layout/search_section_title.xml
+++ b/res/layout/search_section_title.xml
@@ -13,11 +13,11 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/section_title"
- android:textSize="14sp"
- android:fontFamily="@style/TextHeadline"
- android:layout_width="wrap_content"
- android:textColor="?android:attr/textColorPrimary"
- android:padding="4dp"
- android:layout_height="wrap_content"/>
\ No newline at end of file
+<com.android.launcher3.views.SearchSectionHeaderView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/section_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="@style/TextHeadline"
+ android:padding="4dp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="14sp" />
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ef47eef..ad3e2b7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -70,6 +70,8 @@
<!--All apps Search-->
<!-- Section title for apps [CHAR_LIMIT=50] -->
<string name="search_corpus_apps">Apps</string>
+ <!-- try instant app action for play search result [CHAR_LIMIT=50 -->
+ <string name="search_action_try_now">Try Now</string>
<!-- Popup items -->
<!-- Text to display as the header above notifications. [CHAR_LIMIT=30] -->
diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index d64967b..02c6162 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -24,6 +24,7 @@
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.UiThreadHelper;
@@ -130,6 +131,10 @@
public void reset() {
if (!TextUtils.isEmpty(getText())) {
setText("");
+ } else {
+ if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
+ return;
+ }
}
if (isFocused()) {
View nextFocus = focusSearch(View.FOCUS_DOWN);
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index fdbbf4e..2973cf7 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -100,10 +100,12 @@
public static final int SCHEMA_VERSION = 28;
public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".settings";
+ public static final String KEY_LAYOUT_PROVIDER_AUTHORITY = "KEY_LAYOUT_PROVIDER_AUTHORITY";
static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
protected DatabaseHelper mOpenHelper;
+ protected String mProviderAuthority;
private long mLastRestoreTimestamp = 0L;
@@ -367,7 +369,8 @@
case LauncherSettings.Settings.METHOD_WAS_EMPTY_DB_CREATED : {
Bundle result = new Bundle();
result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
- Utilities.getPrefs(getContext()).getBoolean(EMPTY_DATABASE_CREATED, false));
+ Utilities.getPrefs(getContext()).getBoolean(
+ mOpenHelper.getKey(EMPTY_DATABASE_CREATED), false));
return result;
}
case LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS: {
@@ -437,6 +440,7 @@
getContext(), true /* forMigration */)));
return result;
}
+ return null;
}
case LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW: {
if (MULTI_DB_GRID_MIRATION_ALGO.get()) {
@@ -450,6 +454,23 @@
() -> mOpenHelper));
return result;
}
+ return null;
+ }
+ case LauncherSettings.Settings.METHOD_SWITCH_DATABASE: {
+ if (TextUtils.equals(arg, mOpenHelper.getDatabaseName())) return null;
+ final DatabaseHelper helper = mOpenHelper;
+ if (extras == null || !extras.containsKey(KEY_LAYOUT_PROVIDER_AUTHORITY)) {
+ mProviderAuthority = null;
+ } else {
+ mProviderAuthority = extras.getString(KEY_LAYOUT_PROVIDER_AUTHORITY);
+ }
+ mOpenHelper = DatabaseHelper.createDatabaseHelper(
+ getContext(), arg, false /* forMigration */);
+ helper.close();
+ LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+ if (app == null) return null;
+ app.getModel().forceReload();
+ return null;
}
}
return null;
@@ -492,7 +513,8 @@
}
private void clearFlagEmptyDbCreated() {
- Utilities.getPrefs(getContext()).edit().remove(EMPTY_DATABASE_CREATED).commit();
+ Utilities.getPrefs(getContext()).edit()
+ .remove(mOpenHelper.getKey(EMPTY_DATABASE_CREATED)).commit();
}
/**
@@ -505,7 +527,7 @@
synchronized private void loadDefaultFavoritesIfNecessary() {
SharedPreferences sp = Utilities.getPrefs(getContext());
- if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) {
+ if (sp.getBoolean(mOpenHelper.getKey(EMPTY_DATABASE_CREATED), false)) {
Log.d(TAG, "loading default workspace");
AppWidgetHost widgetHost = mOpenHelper.newLauncherWidgetHost();
@@ -553,8 +575,13 @@
*/
private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction(AppWidgetHost widgetHost) {
Context ctx = getContext();
- String authority = Settings.Secure.getString(ctx.getContentResolver(),
- "launcher3.layout.provider");
+ final String authority;
+ if (!TextUtils.isEmpty(mProviderAuthority)) {
+ authority = mProviderAuthority;
+ } else {
+ authority = Settings.Secure.getString(ctx.getContentResolver(),
+ "launcher3.layout.provider");
+ }
if (TextUtils.isEmpty(authority)) {
return null;
}
@@ -694,11 +721,25 @@
}
/**
+ * Re-composite given key in respect to database. If the current db is
+ * {@link LauncherFiles#LAUNCHER_DB}, return the key as-is. Otherwise append the db name to
+ * given key. e.g. consider key="EMPTY_DATABASE_CREATED", dbName="minimal.db", the returning
+ * string will be "EMPTY_DATABASE_CREATED@minimal.db".
+ */
+ String getKey(final String key) {
+ if (TextUtils.equals(getDatabaseName(), LauncherFiles.LAUNCHER_DB)) {
+ return key;
+ }
+ return key + "@" + getDatabaseName();
+ }
+
+ /**
* Overriden in tests.
*/
protected void onEmptyDbCreated() {
// Set the flag for empty DB
- Utilities.getPrefs(mContext).edit().putBoolean(EMPTY_DATABASE_CREATED, true).commit();
+ Utilities.getPrefs(mContext).edit().putBoolean(getKey(EMPTY_DATABASE_CREATED), true)
+ .commit();
}
public long getSerialNumberForUser(UserHandle user) {
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 5512654..58a418e 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -354,14 +354,20 @@
public static final String METHOD_PREP_FOR_PREVIEW = "prep_for_preview";
+ public static final String METHOD_SWITCH_DATABASE = "switch_database";
+
public static final String EXTRA_VALUE = "value";
public static Bundle call(ContentResolver cr, String method) {
- return call(cr, method, null);
+ return call(cr, method, null /* arg */);
}
public static Bundle call(ContentResolver cr, String method, String arg) {
- return cr.call(CONTENT_URI, method, arg, null);
+ return call(cr, method, arg, null /* extras */);
+ }
+
+ public static Bundle call(ContentResolver cr, String method, String arg, Bundle extras) {
+ return cr.call(CONTENT_URI, method, arg, extras);
}
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index af3722a..0684fe0 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
+import static com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
@@ -42,6 +44,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
+import androidx.core.os.BuildCompat;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -108,7 +111,7 @@
private final MultiValueAlpha mMultiValueAlpha;
- Rect mInsets = new Rect();
+ private Rect mInsets = new Rect();
public AllAppsContainerView(Context context) {
this(context, null);
@@ -204,6 +207,17 @@
mAH[AdapterHolder.WORK].applyPadding();
}
+ private void hideInput() {
+ if (!BuildCompat.isAtLeastR() || !FeatureFlags.ENABLE_DEVICE_SEARCH.get()) return;
+
+ WindowInsets insets = getRootWindowInsets();
+ if (insets == null) return;
+
+ if (insets.isVisible(WindowInsets.Type.ime())) {
+ getWindowInsetsController().hide(WindowInsets.Type.ime());
+ }
+ }
+
/**
* Returns whether the view itself will handle the touch event or not.
*/
@@ -219,9 +233,14 @@
}
if (rv.getScrollbar().getThumbOffsetY() >= 0 &&
mLauncher.getDragLayer().isEventOverView(rv.getScrollbar(), ev)) {
+ hideInput();
return false;
}
- return rv.shouldContainerScroll(ev, mLauncher.getDragLayer());
+ boolean shouldScroll = rv.shouldContainerScroll(ev, mLauncher.getDragLayer());
+ if (shouldScroll) {
+ hideInput();
+ }
+ return shouldScroll;
}
@Override
@@ -528,6 +547,25 @@
}
/**
+ * Handles selection on focused view and returns success
+ */
+ public boolean selectFocusedView(View v) {
+ ItemInfo itemInfo = getHighlightedItemInfo();
+ if (itemInfo != null) {
+ return mLauncher.startActivitySafely(v, itemInfo.getIntent(), itemInfo);
+ }
+ AdapterItem focusedItem = getActiveRecyclerView().getApps().getFocusedChild();
+ if (focusedItem instanceof AdapterItemWithPayload) {
+ Runnable onSelection = ((AdapterItemWithPayload) focusedItem).getSelectionHandler();
+ if (onSelection != null) {
+ onSelection.run();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Returns the ItemInfo of a view that is in focus, ready to be launched by an IME.
*/
public ItemInfo getHighlightedItemInfo() {
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 2cec797..c61f01f 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -40,10 +40,10 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.R;
-import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
+import com.android.launcher3.allapps.search.AllAppsSearchBarController.PayloadResultHandler;
+import com.android.launcher3.allapps.search.SearchSectionInfo;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.util.PackageManagerHelper;
-import com.android.launcher3.views.HeroSearchResultView;
import java.util.List;
@@ -71,6 +71,8 @@
public static final int VIEW_TYPE_SEARCH_HERO_APP = 1 << 6;
+ public static final int DETAIL_ROW_WITH_BUTTON = 1 << 7;
+
// Common view type masks
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
@@ -86,6 +88,108 @@
}
/**
+ * Info about a particular adapter item (can be either section or app)
+ */
+ public static class AdapterItem {
+ /** Common properties */
+ // The index of this adapter item in the list
+ public int position;
+ // The type of this item
+ public int viewType;
+
+ /** App-only properties */
+ // The section name of this app. Note that there can be multiple items with different
+ // sectionNames in the same section
+ public String sectionName = null;
+ // The row that this item shows up on
+ public int rowIndex;
+ // The index of this app in the row
+ public int rowAppIndex;
+ // The associated AppInfo for the app
+ public AppInfo appInfo = null;
+ // The index of this app not including sections
+ public int appIndex = -1;
+ // Search section associated to result
+ public SearchSectionInfo searchSectionInfo = null;
+
+ /**
+ * Factory method for AppIcon AdapterItem
+ */
+ public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo,
+ int appIndex) {
+ AdapterItem item = new AdapterItem();
+ item.viewType = VIEW_TYPE_ICON;
+ item.position = pos;
+ item.sectionName = sectionName;
+ item.appInfo = appInfo;
+ item.appIndex = appIndex;
+ return item;
+ }
+
+ /**
+ * Factory method for empty search results view
+ */
+ public static AdapterItem asEmptySearch(int pos) {
+ AdapterItem item = new AdapterItem();
+ item.viewType = VIEW_TYPE_EMPTY_SEARCH;
+ item.position = pos;
+ return item;
+ }
+
+ /**
+ * Factory method for a dividerView in AllAppsSearch
+ */
+ public static AdapterItem asAllAppsDivider(int pos) {
+ AdapterItem item = new AdapterItem();
+ item.viewType = VIEW_TYPE_ALL_APPS_DIVIDER;
+ item.position = pos;
+ return item;
+ }
+
+ /**
+ * Factory method for a market search button
+ */
+ public static AdapterItem asMarketSearch(int pos) {
+ AdapterItem item = new AdapterItem();
+ item.viewType = VIEW_TYPE_SEARCH_MARKET;
+ item.position = pos;
+ return item;
+ }
+
+ boolean isCountedForAccessibility() {
+ return viewType == VIEW_TYPE_ICON
+ || viewType == VIEW_TYPE_SEARCH_HERO_APP
+ || viewType == DETAIL_ROW_WITH_BUTTON;
+ }
+ }
+
+ /**
+ * Extension of AdapterItem that contains an extra payload specific to item
+ * @param <T> Play load Type
+ */
+ public static class AdapterItemWithPayload<T> extends AdapterItem {
+ private T mPayload;
+ private Runnable mSelectionHandler;
+
+ public AdapterItemWithPayload(T payload, int type) {
+ mPayload = payload;
+ viewType = type;
+ }
+
+ public void setSelectionHandler(Runnable runnable) {
+ mSelectionHandler = runnable;
+ }
+
+ public Runnable getSelectionHandler() {
+ return mSelectionHandler;
+ }
+
+ public T getPayload() {
+ return mPayload;
+ }
+ }
+
+ /**
* A subclass of GridLayoutManager that overrides accessibility values during app search.
*/
public class AppsGridLayoutManager extends GridLayoutManager {
@@ -286,6 +390,9 @@
case VIEW_TYPE_SEARCH_HERO_APP:
return new ViewHolder(mLayoutInflater.inflate(
R.layout.search_result_hero_app, parent, false));
+ case DETAIL_ROW_WITH_BUTTON:
+ return new ViewHolder(mLayoutInflater.inflate(
+ R.layout.search_result_play_item, parent, false));
default:
throw new RuntimeException("Unexpected view type");
}
@@ -315,15 +422,11 @@
}
break;
case VIEW_TYPE_SEARCH_CORPUS_TITLE:
- TextView titleView = (TextView) holder.itemView;
- titleView.setText(mApps.getAdapterItems().get(position).searchSectionInfo.getTitle(
- titleView.getContext()));
- break;
+ case DETAIL_ROW_WITH_BUTTON:
case VIEW_TYPE_SEARCH_HERO_APP:
- HeroSearchResultView heroView = (HeroSearchResultView) holder.itemView;
- heroView.prepareUsingAdapterItem(
- (AlphabeticalAppsList.HeroAppAdapterItem) mApps.getAdapterItems().get(
- position));
+ PayloadResultHandler payloadResultView = (PayloadResultHandler) holder.itemView;
+ payloadResultView.applyAdapterInfo(
+ (AdapterItemWithPayload) mApps.getAdapterItems().get(position));
break;
case VIEW_TYPE_ALL_APPS_DIVIDER:
// nothing to do
@@ -344,7 +447,7 @@
@Override
public int getItemViewType(int position) {
- AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position);
+ AdapterItem item = mApps.getAdapterItems().get(position);
return item.viewType;
}
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 640ef01..13a93ff 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -278,7 +278,7 @@
if (mApps == null) {
return;
}
- List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
+ List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
// Skip early if there are no items or we haven't been measured
if (items.isEmpty() || mNumAppsPerRow == 0) {
@@ -352,7 +352,7 @@
@Override
public int getCurrentScrollY() {
// Return early if there are no items or we haven't been measured
- List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
+ List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
if (items.isEmpty() || mNumAppsPerRow == 0 || getChildCount() == 0) {
return -1;
}
@@ -368,14 +368,14 @@
}
public int getCurrentScrollY(int position, int offset) {
- List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
- AlphabeticalAppsList.AdapterItem posItem = position < items.size() ?
- items.get(position) : null;
+ List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
+ AllAppsGridAdapter.AdapterItem posItem = position < items.size()
+ ? items.get(position) : null;
int y = mCachedScrollPositions.get(position, -1);
if (y < 0) {
y = 0;
for (int i = 0; i < position; i++) {
- AlphabeticalAppsList.AdapterItem item = items.get(i);
+ AllAppsGridAdapter.AdapterItem item = items.get(i);
if (AllAppsGridAdapter.isIconViewType(item.viewType)) {
// Break once we reach the desired row
if (posItem != null && posItem.viewType == item.viewType &&
diff --git a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java b/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java
index a168c06..6f29e11 100644
--- a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java
+++ b/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java
@@ -47,13 +47,13 @@
// Since views in the same section will follow each other, we can skip to a last view in
// a section to get the bounds of the section without having to iterate on every item.
int itemCount = parent.getChildCount();
- List<AlphabeticalAppsList.AdapterItem> adapterItems = mAppsView.getApps().getAdapterItems();
+ List<AllAppsGridAdapter.AdapterItem> adapterItems = mAppsView.getApps().getAdapterItems();
SectionDecorationHandler lastDecorationHandler = null;
int i = 0;
while (i < itemCount) {
View view = parent.getChildAt(i);
int position = parent.getChildAdapterPosition(view);
- AlphabeticalAppsList.AdapterItem adapterItem = adapterItems.get(position);
+ AllAppsGridAdapter.AdapterItem adapterItem = adapterItems.get(position);
if (adapterItem.searchSectionInfo != null) {
SearchSectionInfo sectionInfo = adapterItem.searchSectionInfo;
int endIndex = Math.min(i + sectionInfo.getPosEnd() - position, itemCount - 1);
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 7379dbed..8c059d5 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -19,10 +19,10 @@
import android.content.Context;
import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
import com.android.launcher3.allapps.search.SearchSectionInfo;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LabelComparator;
@@ -62,101 +62,6 @@
}
}
- /**
- * Info about a particular adapter item (can be either section or app)
- */
- public static class AdapterItem {
- /** Common properties */
- // The index of this adapter item in the list
- public int position;
- // The type of this item
- public int viewType;
-
- /** App-only properties */
- // The section name of this app. Note that there can be multiple items with different
- // sectionNames in the same section
- public String sectionName = null;
- // The row that this item shows up on
- public int rowIndex;
- // The index of this app in the row
- public int rowAppIndex;
- // The associated AppInfo for the app
- public AppInfo appInfo = null;
- // The index of this app not including sections
- public int appIndex = -1;
- // Search section associated to result
- public SearchSectionInfo searchSectionInfo = null;
-
- public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo,
- int appIndex) {
- AdapterItem item = new AdapterItem();
- item.viewType = AllAppsGridAdapter.VIEW_TYPE_ICON;
- item.position = pos;
- item.sectionName = sectionName;
- item.appInfo = appInfo;
- item.appIndex = appIndex;
- return item;
- }
-
- public static AdapterItem asEmptySearch(int pos) {
- AdapterItem item = new AdapterItem();
- item.viewType = AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH;
- item.position = pos;
- return item;
- }
-
- public static AdapterItem asAllAppsDivider(int pos) {
- AdapterItem item = new AdapterItem();
- item.viewType = AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER;
- item.position = pos;
- return item;
- }
-
- public static AdapterItem asMarketSearch(int pos) {
- AdapterItem item = new AdapterItem();
- item.viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET;
- item.position = pos;
- return item;
- }
-
- /**
- * Factory method for search section title AdapterItem
- */
- public static AdapterItem asSearchTitle(SearchSectionInfo sectionInfo, int pos) {
- AdapterItem item = new AdapterItem();
- item.viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_CORPUS_TITLE;
- item.position = pos;
- item.searchSectionInfo = sectionInfo;
- return item;
- }
-
- boolean isCountedForAccessibility() {
- return viewType == AllAppsGridAdapter.VIEW_TYPE_ICON
- || viewType == AllAppsGridAdapter.VIEW_TYPE_SEARCH_HERO_APP;
- }
- }
-
- /**
- * Extension of AdapterItem that contains shortcut workspace items
- */
- public static class HeroAppAdapterItem extends AdapterItem {
- private ArrayList<WorkspaceItemInfo> mShortcutInfos;
-
- public HeroAppAdapterItem(AppInfo info, ArrayList<WorkspaceItemInfo> shortcutInfos) {
- viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_HERO_APP;
- mShortcutInfos = shortcutInfos;
- appInfo = info;
- }
-
- /**
- * Returns list of shortcuts for appInfo
- */
- public ArrayList<WorkspaceItemInfo> getShortcutInfos() {
- return mShortcutInfos;
- }
-
- }
-
private final BaseDraggingActivity mLauncher;
@@ -396,7 +301,9 @@
adapterItem.position = i;
mAdapterItems.add(adapterItem);
if (adapterItem.searchSectionInfo != lastSection) {
- adapterItem.searchSectionInfo.setPosStart(i);
+ if (adapterItem.searchSectionInfo != null) {
+ adapterItem.searchSectionInfo.setPosStart(i);
+ }
if (lastSection != null) {
lastSection.setPosEnd(i - 1);
}
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index 0d87481..2e5ed3e 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -15,35 +15,26 @@
*/
package com.android.launcher3.allapps.search;
-import android.content.Context;
-import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
-import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnFocusChangeListener;
import android.view.inputmethod.EditorInfo;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
-import android.widget.Toast;
-
-import androidx.core.app.ActivityCompat;
-import androidx.core.content.ContextCompat;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AlphabeticalAppsList;
+import com.android.launcher3.allapps.AllAppsGridAdapter;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.systemui.plugins.AllAppsSearchPlugin;
-import com.android.systemui.plugins.PluginListener;
import java.util.ArrayList;
import java.util.List;
@@ -54,17 +45,14 @@
*/
public class AllAppsSearchBarController
implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener,
- OnFocusChangeListener, PluginListener<AllAppsSearchPlugin> {
+ OnFocusChangeListener {
- private static final String TAG = "AllAppsSearchBarContoller";
protected BaseDraggingActivity mLauncher;
protected Callbacks mCb;
protected ExtendedEditText mInput;
protected String mQuery;
protected SearchAlgorithm mSearchAlgorithm;
- private AllAppsSearchPlugin mPlugin;
- private Consumer mPlubinCb;
public void setVisibility(int visibility) {
mInput.setVisibility(visibility);
@@ -85,18 +73,13 @@
mInput.setOnBackKeyListener(this);
mInput.setOnFocusChangeListener(this);
mSearchAlgorithm = searchAlgorithm;
-
- PluginManagerWrapper.INSTANCE.get(launcher).addPluginListener(this,
- AllAppsSearchPlugin.class);
- mPlubinCb = secondaryCb;
}
@Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- if (mPlugin != null) {
- if (s.length() == 0) {
- mPlugin.startedTyping();
- }
+ public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
+ if (mSearchAlgorithm instanceof PluginWrapper) {
+ ((PluginWrapper) mSearchAlgorithm).runOnPluginIfConnected(
+ AllAppsSearchPlugin::startedTyping);
}
}
@@ -114,9 +97,6 @@
} else {
mSearchAlgorithm.cancel(false);
mSearchAlgorithm.doSearch(mQuery, mCb);
- if (mPlugin != null) {
- mPlugin.performSearch(mQuery, mPlubinCb);
- }
}
}
@@ -133,10 +113,8 @@
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
- ItemInfo info = Launcher.getLauncher(mLauncher).getAppsView()
- .getHighlightedItemInfo();
- if (info != null) {
- return mLauncher.startActivitySafely(v, info.getIntent(), info);
+ if (Launcher.getLauncher(mLauncher).getAppsView().selectFocusedView(v)) {
+ return true;
}
}
}
@@ -197,43 +175,14 @@
return mInput.isFocused();
}
- @Override
- public void onPluginConnected(AllAppsSearchPlugin allAppsSearchPlugin, Context context) {
- if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
- mPlugin = allAppsSearchPlugin;
- checkCallPermission();
- }
- }
-
/**
- * Check call permissions.
+ * A wrapper setup for running essential calls to plugin from search controller
*/
- public void checkCallPermission() {
- final String[] permission = {"android.permission.CALL_PHONE",
- "android.permission.READ_CONTACTS"};
- boolean request = false;
- for (String p : permission) {
- int permissionCheck = ContextCompat.checkSelfPermission(mLauncher, p);
- if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
- request = true;
- }
- }
-
- if (!request) return;
- boolean rationale = false;
- for (String p : permission) {
- if (mLauncher.shouldShowRequestPermissionRationale(p)) {
- rationale = true;
- }
- if (rationale) {
- Log.e(TAG, p + " Show rationale");
- Toast.makeText(mLauncher, "Requesting Permissions", Toast.LENGTH_SHORT).show();
- } else {
- ActivityCompat.requestPermissions(mLauncher, permission, 123);
- Log.e(TAG, p + " request permission");
- }
- }
-
+ public interface PluginWrapper {
+ /**
+ * executes call if plugin is connected
+ */
+ void runOnPluginIfConnected(Consumer<AllAppsSearchPlugin> plugin);
}
/**
@@ -246,11 +195,23 @@
*
* @param items sorted list of search result adapter items.
*/
- void onSearchResult(String query, ArrayList<AlphabeticalAppsList.AdapterItem> items);
+ void onSearchResult(String query, ArrayList<AllAppsGridAdapter.AdapterItem> items);
/**
* Called when the search results should be cleared.
*/
void clearSearchResult();
}
+
+ /**
+ * An interface for supporting dynamic search results
+ *
+ * @param <T> Type of payload
+ */
+ public interface PayloadResultHandler<T> {
+ /**
+ * Updates View using Adapter's payload
+ */
+ void applyAdapterInfo(AdapterItemWithPayload<T> adapterItemWithPayload);
+ }
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index e8a0d7a..6f183ee 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -42,6 +42,7 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsGridAdapter;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.allapps.AlphabeticalAppsList;
import com.android.launcher3.allapps.SearchUiManager;
@@ -172,7 +173,7 @@
}
@Override
- public void onSearchResult(String query, ArrayList<AlphabeticalAppsList.AdapterItem> items) {
+ public void onSearchResult(String query, ArrayList<AllAppsGridAdapter.AdapterItem> items) {
if (items != null) {
mApps.setSearchResults(items);
notifyResultChanged();
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java b/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java
index e67e897..fb3d953 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java
@@ -16,6 +16,7 @@
package com.android.launcher3.allapps.search;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS;
+import static com.android.launcher3.allapps.AllAppsGridAdapter.VIEW_TYPE_SEARCH_HERO_APP;
import android.content.Context;
import android.content.pm.ShortcutInfo;
@@ -23,9 +24,9 @@
import androidx.annotation.WorkerThread;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.allapps.AllAppsGridAdapter;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
import com.android.launcher3.allapps.AllAppsSectionDecorator.SectionDecorationHandler;
-import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
-import com.android.launcher3.allapps.AlphabeticalAppsList.HeroAppAdapterItem;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.AllAppsList;
import com.android.launcher3.model.BaseModelUpdateTask;
@@ -82,7 +83,7 @@
/**
* Returns MAX_SHORTCUTS_COUNT shortcuts from local cache
- * TODO: Shortcuts should be ranked based on relevancy
+ * TODO: Shortcuts should be ranked based on relevancy
*/
private ArrayList<WorkspaceItemInfo> getShortcutInfos(Context context, AppInfo appInfo) {
List<ShortcutInfo> shortcuts = new ShortcutRequest(context, appInfo.user)
@@ -126,7 +127,9 @@
//hero app
AppInfo appInfo = apps.get(i);
ArrayList<WorkspaceItemInfo> shortcuts = getShortcutInfos(context, appInfo);
- AdapterItem adapterItem = new HeroAppAdapterItem(appInfo, shortcuts);
+ AdapterItem adapterItem = new AllAppsGridAdapter.AdapterItemWithPayload(shortcuts,
+ VIEW_TYPE_SEARCH_HERO_APP);
+ adapterItem.appInfo = appInfo;
adapterItem.searchSectionInfo = mSearchSectionInfo;
adapterItems.add(adapterItem);
}
diff --git a/src/com/android/launcher3/allapps/search/SearchPipeline.java b/src/com/android/launcher3/allapps/search/SearchPipeline.java
index 3216740..545f0e3 100644
--- a/src/com/android/launcher3/allapps/search/SearchPipeline.java
+++ b/src/com/android/launcher3/allapps/search/SearchPipeline.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3.allapps.search;
-import com.android.launcher3.allapps.AlphabeticalAppsList;
+import com.android.launcher3.allapps.AllAppsGridAdapter;
import java.util.ArrayList;
import java.util.function.Consumer;
@@ -28,5 +28,5 @@
/**
* Perform query
*/
- void performSearch(String query, Consumer<ArrayList<AlphabeticalAppsList.AdapterItem>> cb);
+ void performSearch(String query, Consumer<ArrayList<AllAppsGridAdapter.AdapterItem>> cb);
}
diff --git a/src/com/android/launcher3/allapps/search/SearchSectionInfo.java b/src/com/android/launcher3/allapps/search/SearchSectionInfo.java
index dee0ffd..e026e84 100644
--- a/src/com/android/launcher3/allapps/search/SearchSectionInfo.java
+++ b/src/com/android/launcher3/allapps/search/SearchSectionInfo.java
@@ -15,8 +15,6 @@
*/
package com.android.launcher3.allapps.search;
-import android.content.Context;
-
import com.android.launcher3.allapps.AllAppsSectionDecorator.SectionDecorationHandler;
/**
@@ -24,7 +22,7 @@
*/
public class SearchSectionInfo {
- private final int mTitleResId;
+ private String mTitle;
private SectionDecorationHandler mDecorationHandler;
public int getPosStart() {
@@ -47,11 +45,11 @@
private int mPosEnd;
public SearchSectionInfo() {
- this(-1);
+ this(null);
}
- public SearchSectionInfo(int titleResId) {
- mTitleResId = titleResId;
+ public SearchSectionInfo(String title) {
+ mTitle = title;
}
public void setDecorationHandler(SectionDecorationHandler sectionDecorationHandler) {
@@ -66,10 +64,7 @@
/**
* Returns the section's title
*/
- public String getTitle(Context context) {
- if (mTitleResId == -1) {
- return "";
- }
- return context.getString(mTitleResId);
+ public String getTitle() {
+ return mTitle == null ? "" : mTitle;
}
}
diff --git a/src/com/android/launcher3/graphics/OverviewScrim.java b/src/com/android/launcher3/graphics/OverviewScrim.java
index 94acbfd..c0c3e5e 100644
--- a/src/com/android/launcher3/graphics/OverviewScrim.java
+++ b/src/com/android/launcher3/graphics/OverviewScrim.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.LauncherState.OVERVIEW;
import android.graphics.Rect;
+import android.util.FloatProperty;
import android.view.View;
import android.view.ViewGroup;
@@ -33,10 +34,25 @@
*/
public class OverviewScrim extends Scrim {
+ public static final FloatProperty<OverviewScrim> SCRIM_MULTIPLIER =
+ new FloatProperty<OverviewScrim>("scrimMultiplier") {
+ @Override
+ public Float get(OverviewScrim scrim) {
+ return scrim.mScrimMultiplier;
+ }
+
+ @Override
+ public void setValue(OverviewScrim scrim, float v) {
+ scrim.setScrimMultiplier(v);
+ }
+ };
+
private @NonNull View mStableScrimmedView;
// Might be higher up if mStableScrimmedView is invisible.
private @Nullable View mCurrentScrimmedView;
+ private float mScrimMultiplier = 1f;
+
public OverviewScrim(View view) {
super(view);
mStableScrimmedView = mCurrentScrimmedView = mLauncher.getOverviewPanel();
@@ -68,4 +84,16 @@
public @Nullable View getScrimmedView() {
return mCurrentScrimmedView;
}
+
+ private void setScrimMultiplier(float scrimMultiplier) {
+ if (Float.compare(mScrimMultiplier, scrimMultiplier) != 0) {
+ mScrimMultiplier = scrimMultiplier;
+ invalidate();
+ }
+ }
+
+ @Override
+ protected int getScrimAlpha() {
+ return Math.round(super.getScrimAlpha() * mScrimMultiplier);
+ }
}
diff --git a/src/com/android/launcher3/graphics/Scrim.java b/src/com/android/launcher3/graphics/Scrim.java
index f90962d..a151cba 100644
--- a/src/com/android/launcher3/graphics/Scrim.java
+++ b/src/com/android/launcher3/graphics/Scrim.java
@@ -61,7 +61,11 @@
}
public void draw(Canvas canvas) {
- canvas.drawColor(setColorAlphaBound(mScrimColor, mScrimAlpha));
+ canvas.drawColor(setColorAlphaBound(mScrimColor, getScrimAlpha()));
+ }
+
+ protected int getScrimAlpha() {
+ return mScrimAlpha;
}
private void setScrimProgress(float progress) {
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 7e45021..cf32514 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -297,8 +297,7 @@
.filter(wi -> wi.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
.map(ShortcutKey::fromItemInfo),
// Pending shortcuts
- ItemInstallQueue.INSTANCE.get(context).getPendingShortcuts()
- .stream().filter(si -> si.user.equals(user)))
+ ItemInstallQueue.INSTANCE.get(context).getPendingShortcuts(user))
.collect(groupingBy(ShortcutKey::getPackageName,
mapping(ShortcutKey::getId, Collectors.toSet())));
diff --git a/src/com/android/launcher3/model/ItemInstallQueue.java b/src/com/android/launcher3/model/ItemInstallQueue.java
index 6238db3..5e48a0f 100644
--- a/src/com/android/launcher3/model/ItemInstallQueue.java
+++ b/src/com/android/launcher3/model/ItemInstallQueue.java
@@ -21,7 +21,6 @@
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
-import static com.android.launcher3.LauncherSettings.Favorites.PROFILE_ID;
import static com.android.launcher3.model.data.AppInfo.makeLaunchIntent;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -30,11 +29,9 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
-import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
import android.util.Pair;
@@ -47,27 +44,19 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.Utilities;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.PersistedItemArray;
import com.android.launcher3.util.Preconditions;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.json.JSONStringer;
-
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Collection;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
-import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* Class to maintain a queue of pending items to be added to the workspace.
@@ -79,7 +68,6 @@
public static final int FLAG_DRAG_AND_DROP = 4;
private static final String TAG = "InstallShortcutReceiver";
- private static final boolean DBG = false;
// The set of shortcuts that are pending install
private static final String APPS_PENDING_INSTALL = "apps_to_install";
@@ -90,24 +78,34 @@
public static MainThreadInitializedObject<ItemInstallQueue> INSTANCE =
new MainThreadInitializedObject<>(ItemInstallQueue::new);
+ private final PersistedItemArray<PendingInstallShortcutInfo> mStorage =
+ new PersistedItemArray<>(APPS_PENDING_INSTALL);
private final Context mContext;
// Determines whether to defer installing shortcuts immediately until
// processAllPendingInstalls() is called.
private int mInstallQueueDisabledFlags = 0;
+ // Only accessed on worker thread
+ private List<PendingInstallShortcutInfo> mItems;
+
private ItemInstallQueue(Context context) {
mContext = context;
}
@WorkerThread
+ private void ensureQueueLoaded() {
+ Preconditions.assertWorkerThread();
+ if (mItems == null) {
+ mItems = mStorage.read(mContext, this::decode);
+ }
+ }
+
+ @WorkerThread
private void addToQueue(PendingInstallShortcutInfo info) {
- String encoded = info.encodeToString(mContext);
- SharedPreferences prefs = Utilities.getPrefs(mContext);
- Set<String> strings = prefs.getStringSet(APPS_PENDING_INSTALL, null);
- strings = (strings != null) ? new HashSet<>(strings) : new HashSet<>(1);
- strings.add(encoded);
- prefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).apply();
+ ensureQueueLoaded();
+ mItems.add(info);
+ mStorage.write(mContext, mItems);
}
@WorkerThread
@@ -117,28 +115,21 @@
// Launcher not loaded
return;
}
-
- ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
- SharedPreferences prefs = Utilities.getPrefs(mContext);
- Set<String> strings = prefs.getStringSet(APPS_PENDING_INSTALL, null);
- if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings);
- if (strings == null) {
+ ensureQueueLoaded();
+ if (mItems.isEmpty()) {
return;
}
- for (String encoded : strings) {
- PendingInstallShortcutInfo info = decode(encoded, mContext);
- if (info == null) {
- continue;
- }
+ List<Pair<ItemInfo, Object>> installQueue = mItems.stream()
+ .map(info -> info.getItemInfo(mContext))
+ .collect(Collectors.toList());
- // Generate a shortcut info to add into the model
- installQueue.add(info.getItemInfo(mContext));
- }
- prefs.edit().remove(APPS_PENDING_INSTALL).apply();
+ // Add the items and clear queue
if (!installQueue.isEmpty()) {
launcher.getModel().addAndBindAddedWorkspaceItems(installQueue);
}
+ mItems.clear();
+ mStorage.getFile(mContext).delete();
}
/**
@@ -149,33 +140,11 @@
if (packageNames.isEmpty()) {
return;
}
- Preconditions.assertWorkerThread();
-
- SharedPreferences sp = Utilities.getPrefs(mContext);
- Set<String> strings = sp.getStringSet(APPS_PENDING_INSTALL, null);
- if (DBG) {
- Log.d(TAG, "APPS_PENDING_INSTALL: " + strings
- + ", removing packages: " + packageNames);
+ ensureQueueLoaded();
+ if (mItems.removeIf(item ->
+ item.user.equals(user) && packageNames.contains(getIntentPackage(item.intent)))) {
+ mStorage.write(mContext, mItems);
}
- if (strings == null || ((Collection) strings).isEmpty()) {
- return;
- }
- Set<String> newStrings = new HashSet<>(strings);
- Iterator<String> newStringsIter = newStrings.iterator();
- while (newStringsIter.hasNext()) {
- String encoded = newStringsIter.next();
- try {
- Decoder decoder = new Decoder(encoded, mContext);
- if (packageNames.contains(getIntentPackage(decoder.intent))
- && user.equals(decoder.user)) {
- newStringsIter.remove();
- }
- } catch (JSONException | URISyntaxException e) {
- Log.d(TAG, "Exception reading shortcut to add: " + e);
- newStringsIter.remove();
- }
- }
- sp.edit().putStringSet(APPS_PENDING_INSTALL, newStrings).apply();
}
/**
@@ -200,28 +169,14 @@
}
/**
- * Returns all pending shorts in the queue
+ * Returns a stream of all pending shortcuts in the queue
*/
@WorkerThread
- public HashSet<ShortcutKey> getPendingShortcuts() {
- HashSet<ShortcutKey> result = new HashSet<>();
-
- Set<String> strings = Utilities.getPrefs(mContext).getStringSet(APPS_PENDING_INSTALL, null);
- if (strings == null || ((Collection) strings).isEmpty()) {
- return result;
- }
-
- for (String encoded : strings) {
- try {
- Decoder decoder = new Decoder(encoded, mContext);
- if (decoder.optInt(Favorites.ITEM_TYPE, -1) == ITEM_TYPE_DEEP_SHORTCUT) {
- result.add(ShortcutKey.fromIntent(decoder.intent, decoder.user));
- }
- } catch (JSONException | URISyntaxException e) {
- Log.d(TAG, "Exception reading shortcut to add: " + e);
- }
- }
- return result;
+ public Stream<ShortcutKey> getPendingShortcuts(UserHandle user) {
+ ensureQueueLoaded();
+ return mItems.stream()
+ .filter(item -> item.itemType == ITEM_TYPE_DEEP_SHORTCUT && user.equals(item.user))
+ .map(item -> ShortcutKey.fromIntent(item.intent, user));
}
private void queuePendingShortcutInfo(PendingInstallShortcutInfo info) {
@@ -293,19 +248,9 @@
providerInfo = info;
}
- public String encodeToString(Context context) {
- try {
- return new JSONStringer()
- .object()
- .key(Favorites.ITEM_TYPE).value(itemType)
- .key(Favorites.INTENT).value(intent.toUri(0))
- .key(PROFILE_ID).value(
- UserCache.INSTANCE.get(context).getSerialNumberForUser(user))
- .endObject().toString();
- } catch (JSONException e) {
- Log.d(TAG, "Exception when adding shortcut: " + e);
- return null;
- }
+ @Override
+ public Intent getIntent() {
+ return intent;
}
public Pair<ItemInfo, Object> getItemInfo(Context context) {
@@ -365,55 +310,33 @@
? intent.getPackage() : intent.getComponent().getPackageName();
}
- private static PendingInstallShortcutInfo decode(String encoded, Context context) {
- try {
- Decoder decoder = new Decoder(encoded, context);
- switch (decoder.optInt(Favorites.ITEM_TYPE, -1)) {
- case Favorites.ITEM_TYPE_APPLICATION:
- return new PendingInstallShortcutInfo(
- decoder.intent.getPackage(), decoder.user);
- case Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
- List<ShortcutInfo> si = ShortcutKey.fromIntent(decoder.intent, decoder.user)
- .buildRequest(context)
- .query(ShortcutRequest.ALL);
- if (si.isEmpty()) {
- return null;
- } else {
- return new PendingInstallShortcutInfo(si.get(0));
- }
+ private PendingInstallShortcutInfo decode(int itemType, UserHandle user, Intent intent) {
+ switch (itemType) {
+ case Favorites.ITEM_TYPE_APPLICATION:
+ return new PendingInstallShortcutInfo(intent.getPackage(), user);
+ case Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
+ List<ShortcutInfo> si = ShortcutKey.fromIntent(intent, user)
+ .buildRequest(mContext)
+ .query(ShortcutRequest.ALL);
+ if (si.isEmpty()) {
+ return null;
+ } else {
+ return new PendingInstallShortcutInfo(si.get(0));
}
- case Favorites.ITEM_TYPE_APPWIDGET: {
- int widgetId = decoder.intent.getIntExtra(EXTRA_APPWIDGET_ID, 0);
- AppWidgetProviderInfo info =
- AppWidgetManager.getInstance(context).getAppWidgetInfo(widgetId);
- if (info == null || !info.provider.equals(decoder.intent.getComponent())
- || !info.getProfile().equals(decoder.user)) {
- return null;
- }
- return new PendingInstallShortcutInfo(info, widgetId);
- }
- default:
- Log.e(TAG, "Unknown item type");
}
- } catch (JSONException | URISyntaxException e) {
- Log.d(TAG, "Exception reading shortcut to add: " + e);
+ case Favorites.ITEM_TYPE_APPWIDGET: {
+ int widgetId = intent.getIntExtra(EXTRA_APPWIDGET_ID, 0);
+ AppWidgetProviderInfo info =
+ AppWidgetManager.getInstance(mContext).getAppWidgetInfo(widgetId);
+ if (info == null || !info.provider.equals(intent.getComponent())
+ || !info.getProfile().equals(user)) {
+ return null;
+ }
+ return new PendingInstallShortcutInfo(info, widgetId);
+ }
+ default:
+ Log.e(TAG, "Unknown item type");
}
return null;
}
-
- private static class Decoder extends JSONObject {
- public final Intent intent;
- public final UserHandle user;
-
- private Decoder(String encoded, Context context) throws JSONException, URISyntaxException {
- super(encoded);
- intent = Intent.parseUri(getString(Favorites.INTENT), 0);
- user = has(PROFILE_ID)
- ? UserCache.INSTANCE.get(context).getUserForSerialNumber(getLong(PROFILE_ID))
- : Process.myUserHandle();
- if (user == null || intent == null) {
- throw new JSONException("Invalid data");
- }
- }
- }
}
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index ec3a467..922425f 100644
--- a/src/com/android/launcher3/settings/SettingsActivity.java
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -44,6 +44,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.SecureSettingsObserver;
@@ -171,6 +172,10 @@
protected boolean initPreference(Preference preference) {
switch (preference.getKey()) {
case NOTIFICATION_DOTS_PREFERENCE_KEY:
+ if (WidgetsModel.GO_DISABLE_NOTIFICATION_DOTS) {
+ return false;
+ }
+
// Listen to system notification dot settings while this UI is active.
mNotificationDotsObserver = newNotificationSettingsObserver(
getActivity(), (NotificationDotsPreference) preference);
diff --git a/src/com/android/launcher3/util/PersistedItemArray.java b/src/com/android/launcher3/util/PersistedItemArray.java
index ae20638..7ff2abb 100644
--- a/src/com/android/launcher3/util/PersistedItemArray.java
+++ b/src/com/android/launcher3/util/PersistedItemArray.java
@@ -68,8 +68,7 @@
*/
@WorkerThread
public void write(Context context, List<T> items) {
- AtomicFile file = new AtomicFile(context.getFileStreamPath(mFileName));
-
+ AtomicFile file = getFile(context);
FileOutputStream fos;
try {
fos = file.startWrite();
@@ -124,9 +123,7 @@
@WorkerThread
public List<T> read(Context context, ItemFactory<T> factory, LongFunction<UserHandle> userFn) {
List<T> result = new ArrayList<>();
- AtomicFile file = new AtomicFile(context.getFileStreamPath(mFileName));
-
- try (FileInputStream fis = file.openRead()) {
+ try (FileInputStream fis = getFile(context).openRead()) {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(new InputStreamReader(fis, StandardCharsets.UTF_8));
@@ -167,6 +164,13 @@
}
/**
+ * Returns the underlying file used for persisting data
+ */
+ public AtomicFile getFile(Context context) {
+ return new AtomicFile(context.getFileStreamPath(mFileName));
+ }
+
+ /**
* Interface to create an ItemInfo during parsing
*/
public interface ItemFactory<T extends ItemInfo> {
diff --git a/src/com/android/launcher3/views/HeroSearchResultView.java b/src/com/android/launcher3/views/HeroSearchResultView.java
index c2a02bc..10f3c41 100644
--- a/src/com/android/launcher3/views/HeroSearchResultView.java
+++ b/src/com/android/launcher3/views/HeroSearchResultView.java
@@ -30,7 +30,8 @@
import com.android.launcher3.DropTarget;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
-import com.android.launcher3.allapps.AlphabeticalAppsList;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
+import com.android.launcher3.allapps.search.AllAppsSearchBarController;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.graphics.DragPreviewProvider;
@@ -47,7 +48,8 @@
/**
* A view representing a high confidence app search result that includes shortcuts
*/
-public class HeroSearchResultView extends LinearLayout implements DragSource {
+public class HeroSearchResultView extends LinearLayout implements DragSource,
+ AllAppsSearchBarController.PayloadResultHandler<List<WorkspaceItemInfo>> {
BubbleTextView mBubbleTextView;
View mIconView;
@@ -96,18 +98,18 @@
/**
* Apply {@link ItemInfo} for appIcon and shortcut Icons
*/
- public void prepareUsingAdapterItem(AlphabeticalAppsList.HeroAppAdapterItem adapterItem) {
+ @Override
+ public void applyAdapterInfo(AdapterItemWithPayload<List<WorkspaceItemInfo>> adapterItem) {
mBubbleTextView.applyFromApplicationInfo(adapterItem.appInfo);
mIconView.setBackground(mBubbleTextView.getIcon());
mIconView.setTag(adapterItem.appInfo);
- List<WorkspaceItemInfo> shorcutInfos = adapterItem.getShortcutInfos();
+ List<WorkspaceItemInfo> shorcutInfos = adapterItem.getPayload();
for (int i = 0; i < mDeepShortcutTextViews.length; i++) {
mDeepShortcutTextViews[i].setVisibility(shorcutInfos.size() > i ? VISIBLE : GONE);
if (i < shorcutInfos.size()) {
mDeepShortcutTextViews[i].applyFromWorkspaceItem(shorcutInfos.get(i));
}
}
-
}
@Override
@@ -126,7 +128,6 @@
mIconView.setVisibility(willDraw ? View.VISIBLE : View.INVISIBLE);
}
-
/**
* Drag and drop handler for popup items in Launcher activity
*/
diff --git a/src/com/android/launcher3/views/SearchResultPlayItem.java b/src/com/android/launcher3/views/SearchResultPlayItem.java
new file mode 100644
index 0000000..19a4c5d
--- /dev/null
+++ b/src/com/android/launcher3/views/SearchResultPlayItem.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2015 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.util.Executors.UI_HELPER_EXECUTOR;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.BitmapDrawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
+import com.android.launcher3.allapps.search.AllAppsSearchBarController;
+
+import java.io.IOException;
+import java.net.URL;
+
+/**
+ * A View representing a PlayStore item.
+ */
+public class SearchResultPlayItem extends LinearLayout implements
+ AllAppsSearchBarController.PayloadResultHandler<Bundle> {
+ private final DeviceProfile mDeviceProfile;
+ private View mIconView;
+ private TextView mTitleView;
+ private TextView[] mDetailViews = new TextView[3];
+ private Button mPreviewButton;
+ private String mPackageName;
+ private boolean mIsInstantGame;
+
+ public SearchResultPlayItem(Context context) {
+ this(context, null, 0);
+ }
+
+ public SearchResultPlayItem(Context context,
+ @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SearchResultPlayItem(Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mDeviceProfile = Launcher.getLauncher(getContext()).getDeviceProfile();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mIconView = findViewById(R.id.icon);
+ mTitleView = findViewById(R.id.title_view);
+ mPreviewButton = findViewById(R.id.try_button);
+ mPreviewButton.setOnClickListener(view -> launchInstantGame());
+ mDetailViews[0] = findViewById(R.id.detail_0);
+ mDetailViews[1] = findViewById(R.id.detail_1);
+ mDetailViews[2] = findViewById(R.id.detail_2);
+
+ ViewGroup.LayoutParams iconParams = mIconView.getLayoutParams();
+ iconParams.height = mDeviceProfile.allAppsIconSizePx;
+ iconParams.width = mDeviceProfile.allAppsIconSizePx;
+ setOnClickListener(view -> handleSelection());
+
+ }
+
+ @Override
+ public void applyAdapterInfo(AdapterItemWithPayload<Bundle> adapterItemWithPayload) {
+ Bundle bundle = adapterItemWithPayload.getPayload();
+ adapterItemWithPayload.setSelectionHandler(this::handleSelection);
+ if (bundle.getString("package", "").equals(mPackageName)) {
+ return;
+ }
+ mIsInstantGame = bundle.getBoolean("instant_game", false);
+ mPackageName = bundle.getString("package");
+ mPreviewButton.setVisibility(mIsInstantGame ? VISIBLE : GONE);
+ mTitleView.setText(bundle.getString("title"));
+// TODO: Should use a generic type to get values b/165320033
+ showIfNecessary(mDetailViews[0], bundle.getString("price"));
+ showIfNecessary(mDetailViews[1], bundle.getString("rating"));
+ showIfNecessary(mDetailViews[2], bundle.getString("category"));
+
+ mIconView.setBackgroundResource(R.drawable.ic_deepshortcut_placeholder);
+ UI_HELPER_EXECUTOR.execute(() -> {
+ try {
+// TODO: Handle caching
+ URL url = new URL(bundle.getString("icon_url"));
+ Bitmap bitmap = BitmapFactory.decodeStream(url.openStream());
+ BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(),
+ Bitmap.createScaledBitmap(bitmap, mDeviceProfile.allAppsIconSizePx,
+ mDeviceProfile.allAppsIconSizePx, false));
+ mIconView.post(() -> mIconView.setBackground(bitmapDrawable));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ });
+ }
+
+ private void showIfNecessary(TextView textView, @Nullable String string) {
+ if (string == null || string.isEmpty()) {
+ textView.setVisibility(GONE);
+ } else {
+ textView.setText(string);
+ textView.setVisibility(VISIBLE);
+ }
+ }
+
+ private void handleSelection() {
+ if (mPackageName == null) return;
+ Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(
+ "https://play.google.com/store/apps/details?id="
+ + mPackageName));
+ i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ getContext().startActivity(i);
+ }
+
+ private void launchInstantGame() {
+ if (!mIsInstantGame) return;
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ String referrer = "Pixel_Launcher";
+ String id = mPackageName;
+ String deepLinkUrl = "market://details?id=" + id + "&launch=true&referrer=" + referrer;
+ intent.setPackage("com.android.vending");
+ intent.setData(Uri.parse(deepLinkUrl));
+ intent.putExtra("overlay", true);
+ intent.putExtra("callerId", getContext().getPackageName());
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ getContext().startActivity(intent);
+ }
+}
diff --git a/src/com/android/launcher3/views/SearchSectionHeaderView.java b/src/com/android/launcher3/views/SearchSectionHeaderView.java
new file mode 100644
index 0000000..d439ee3
--- /dev/null
+++ b/src/com/android/launcher3/views/SearchSectionHeaderView.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 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 android.content.Context;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.allapps.AllAppsGridAdapter;
+import com.android.launcher3.allapps.search.AllAppsSearchBarController;
+
+/**
+ * Header text view that shows a title for a given section in All apps search
+ */
+public class SearchSectionHeaderView extends TextView implements
+ AllAppsSearchBarController.PayloadResultHandler<String> {
+ public SearchSectionHeaderView(Context context) {
+ super(context);
+ }
+
+ public SearchSectionHeaderView(Context context,
+ @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public SearchSectionHeaderView(Context context, @Nullable AttributeSet attrs, int styleAttr) {
+ super(context, attrs, styleAttr);
+ }
+
+ @Override
+ public void applyAdapterInfo(AllAppsGridAdapter.AdapterItemWithPayload<String> adapterItem) {
+ String title = adapterItem.getPayload();
+ if (title == null || !title.isEmpty()) {
+ setText(title);
+ setVisibility(VISIBLE);
+ } else {
+ setVisibility(INVISIBLE);
+ }
+ }
+}
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index 9d87788..a64df62 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -51,6 +51,7 @@
// True is the widget support is disabled.
public static final boolean GO_DISABLE_WIDGETS = false;
+ public static final boolean GO_DISABLE_NOTIFICATION_DOTS = false;
private static final String TAG = "WidgetsModel";
private static final boolean DEBUG = false;