Show settings icon at start of settings results
Bug: 170792963
Test: Manual
Screenshot: https://screenshot.googleplex.com/MVBtDZGtQ3aChwU
Change-Id: I16a28a7bd9e3129b40102e020cd7c0111e6fe29b
diff --git a/res/layout/search_result_icon_row.xml b/res/layout/search_result_icon_row.xml
index b51896e..280bbc9 100644
--- a/res/layout/search_result_icon_row.xml
+++ b/res/layout/search_result_icon_row.xml
@@ -40,13 +40,13 @@
android:gravity="start|center_vertical"
android:textAlignment="viewStart"
android:textColor="?android:attr/textColorPrimary"
- android:textSize="@dimen/settings_hero_title_size" />
+ android:textSize="@dimen/search_hero_title_size" />
<TextView
android:layout_width="wrap_content"
android:id="@+id/desc"
android:textColor="?android:attr/textColorTertiary"
- android:textSize="@dimen/settings_hero_subtitle_size"
+ android:textSize="@dimen/search_hero_subtitle_size"
android:layout_height="wrap_content" />
</LinearLayout>
@@ -57,7 +57,7 @@
android:layout_height="match_parent"
android:gravity="start|center_vertical"
launcher:iconDisplay="shortcut_popup"
- android:textSize="@dimen/settings_hero_subtitle_size"
+ android:textSize="@dimen/search_hero_subtitle_size"
launcher:iconSizeOverride="@dimen/deep_shortcut_icon_size"
launcher:layoutHorizontal="false" />
@@ -67,7 +67,7 @@
android:layout_width="@dimen/deep_shortcut_icon_size"
android:layout_height="match_parent"
launcher:iconDisplay="shortcut_popup"
- android:textSize="@dimen/settings_hero_inline_button_size"
+ android:textSize="@dimen/search_hero_inline_button_size"
launcher:iconSizeOverride="@dimen/deep_shortcut_icon_size"
launcher:layoutHorizontal="false" />
diff --git a/res/layout/search_result_settings_row.xml b/res/layout/search_result_settings_row.xml
index 19daf34..05f7561 100644
--- a/res/layout/search_result_settings_row.xml
+++ b/res/layout/search_result_settings_row.xml
@@ -18,40 +18,42 @@
android:background="?android:attr/selectableItemBackground"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
+ android:orientation="horizontal"
android:gravity="center_vertical"
- android:padding="4dp"
- android:minHeight="48dp"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="14sp">
+ android:padding="@dimen/dynamic_grid_cell_padding_x"
+ android:textColor="?android:attr/textColorPrimary">
- <TextView
- android:id="@+id/title"
- style="@style/TextTitle"
- android:layout_width="wrap_content"
+ <View
+ android:layout_width="@dimen/search_settings_icon_size"
+ android:src="@drawable/ic_setting"
+ android:id="@+id/icon"
+ android:layout_height="@dimen/search_settings_icon_size" />
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:orientation="vertical"
+ android:paddingRight="@dimen/dynamic_grid_cell_padding_x"
+ android:paddingLeft="@dimen/dynamic_grid_cell_padding_x"
android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="16sp" />
-
- <TextView
- android:id="@+id/description"
- style="@style/TextTitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="14sp" />
-
- <TextView
- android:id="@+id/breadcrumbs"
- style="@style/TextTitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- android:alpha=".7"
- android:textColor="?android:attr/textColorSecondary"
- android:textSize="14sp" />
+ android:layout_weight="1">
+ <TextView
+ android:id="@+id/title"
+ style="@style/TextTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/search_line_spacing"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="@dimen/search_hero_title_size" />
+
+ <TextView
+ android:id="@+id/breadcrumbs"
+ style="@style/TextTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="@dimen/search_hero_subtitle_size" />
+ </LinearLayout>
</com.android.launcher3.views.SearchSettingsRowView>
\ No newline at end of file
diff --git a/res/layout/search_result_slice.xml b/res/layout/search_result_slice.xml
index ea1d49a..24d75e9 100644
--- a/res/layout/search_result_slice.xml
+++ b/res/layout/search_result_slice.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
+<?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.
@@ -13,7 +12,29 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<androidx.slice.widget.SliceView xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher3.views.SearchResultSettingsSlice xmlns:android="http://schemas.android.com/apk/res/android"
+ android:paddingHorizontal="@dimen/dynamic_grid_cell_padding_x"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingHorizontal="4dp" />
\ No newline at end of file
+ android:layout_height="wrap_content">
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:paddingTop="@dimen/search_settings_icon_vertical_offset"
+ android:layout_height="wrap_content">
+
+ <View
+ android:layout_width="@dimen/search_settings_icon_size"
+ android:src="@drawable/ic_setting"
+ android:id="@+id/icon"
+ android:layout_height="@dimen/search_settings_icon_size" />
+ </FrameLayout>
+
+ <androidx.slice.widget.SliceView
+ android:id="@+id/slice"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginStart="@dimen/dynamic_grid_cell_padding_x"
+ android:layout_width="0dp" />
+
+</com.android.launcher3.views.SearchResultSettingsSlice>
+
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 5387e1b..7df3f77 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -249,8 +249,11 @@
<dimen name="bottom_sheet_edu_padding">24dp</dimen>
<!-- Search related -->
- <dimen name="settings_hero_title_size">16sp</dimen>
- <dimen name="settings_hero_subtitle_size">15sp</dimen>
- <dimen name="settings_hero_inline_button_size">12sp</dimen>
+ <dimen name="search_hero_title_size">16sp</dimen>
+ <dimen name="search_hero_subtitle_size">15sp</dimen>
+ <dimen name="search_hero_inline_button_size">12sp</dimen>
+ <dimen name="search_settings_icon_size">36dp</dimen>
+ <dimen name="search_settings_icon_vertical_offset">16dp</dimen>
+ <dimen name="search_line_spacing">4dp</dimen>
</resources>
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 7018f20..434bc14 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -37,7 +37,6 @@
import androidx.core.view.accessibility.AccessibilityRecordCompat;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import androidx.slice.widget.SliceView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.BubbleTextView;
@@ -47,7 +46,6 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.util.PackageManagerHelper;
-import com.android.launcher3.views.SearchSliceWrapper;
import com.android.systemui.plugins.shared.SearchTarget;
import java.util.List;
@@ -462,16 +460,9 @@
searchView.setVisibility(View.GONE);
}
break;
- case VIEW_TYPE_SEARCH_SLICE:
- SliceView sliceView = (SliceView) holder.itemView;
- SearchAdapterItem slicePayload = (SearchAdapterItem) mApps.getAdapterItems().get(
- position);
- SearchTarget searchTarget = slicePayload.getSearchTarget();
- sliceView.setTag(new SearchSliceWrapper(mLauncher, sliceView, searchTarget));
-
- break;
case VIEW_TYPE_SEARCH_CORPUS_TITLE:
case VIEW_TYPE_SEARCH_ROW_WITH_BUTTON:
+ case VIEW_TYPE_SEARCH_SLICE:
case VIEW_TYPE_SEARCH_ROW:
case VIEW_TYPE_SEARCH_ICON:
case VIEW_TYPE_SEARCH_ICON_ROW:
@@ -496,13 +487,6 @@
if (holder.itemView instanceof AllAppsSectionDecorator.SelfDecoratingView) {
((AllAppsSectionDecorator.SelfDecoratingView) holder.itemView).removeDecoration();
}
- if (holder.itemView instanceof SliceView) {
- SliceView sliceView = (SliceView) holder.itemView;
- if (sliceView.getTag() instanceof SearchSliceWrapper) {
- ((SearchSliceWrapper) sliceView.getTag()).destroy();
- }
- sliceView.setTag(null);
- }
}
@Override
diff --git a/src/com/android/launcher3/views/SearchResultSettingsSlice.java b/src/com/android/launcher3/views/SearchResultSettingsSlice.java
new file mode 100644
index 0000000..2d726e7
--- /dev/null
+++ b/src/com/android/launcher3/views/SearchResultSettingsSlice.java
@@ -0,0 +1,129 @@
+/*
+ * 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.net.Uri;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.LiveData;
+import androidx.slice.Slice;
+import androidx.slice.SliceItem;
+import androidx.slice.widget.EventInfo;
+import androidx.slice.widget.SliceLiveData;
+import androidx.slice.widget.SliceView;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.allapps.search.AllAppsSearchBarController;
+import com.android.launcher3.allapps.search.SearchEventTracker;
+import com.android.systemui.plugins.shared.SearchTarget;
+import com.android.systemui.plugins.shared.SearchTargetEvent;
+
+/**
+ * A slice view wrapper with settings app icon at start
+ */
+public class SearchResultSettingsSlice extends LinearLayout implements
+ AllAppsSearchBarController.SearchTargetHandler, SliceView.OnSliceActionListener {
+
+
+ public static final String TARGET_TYPE_SLICE = "settings_slice";
+
+ private static final String TAG = "SearchSliceController";
+ private static final String URI_EXTRA_KEY = "slice_uri";
+
+ private SliceView mSliceView;
+ private View mIcon;
+ private LiveData<Slice> mSliceLiveData;
+ private SearchTarget mSearchTarget;
+ private Launcher mLauncher;
+
+ public SearchResultSettingsSlice(Context context) {
+ this(context, null, 0);
+ }
+
+ public SearchResultSettingsSlice(Context context,
+ @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SearchResultSettingsSlice(Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mLauncher = Launcher.getLauncher(getContext());
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mSliceView = findViewById(R.id.slice);
+ mIcon = findViewById(R.id.icon);
+ SearchSettingsRowView.applySettingsIcon(mLauncher, mIcon);
+ }
+
+ @Override
+ public void applySearchTarget(SearchTarget searchTarget) {
+ reset();
+ mSearchTarget = searchTarget;
+ try {
+ mSliceLiveData = SliceLiveData.fromUri(mLauncher, getSliceUri());
+ mSliceLiveData.observe(mLauncher, mSliceView);
+ } catch (Exception ex) {
+ Log.e(TAG, "unable to bind slice", ex);
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mSliceView.setOnSliceActionListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ reset();
+ }
+
+ @Override
+ public void handleSelection(int eventType) {
+ SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(
+ new SearchTargetEvent.Builder(mSearchTarget,
+ SearchTargetEvent.CHILD_SELECT).build());
+ }
+
+ private void reset() {
+ mSliceView.setOnSliceActionListener(null);
+ if (mSliceLiveData != null) {
+ mSliceLiveData.removeObservers(mLauncher);
+ }
+ }
+
+ @Override
+ public void onSliceAction(@NonNull EventInfo eventInfo, @NonNull SliceItem sliceItem) {
+ handleSelection(SearchTargetEvent.CHILD_SELECT);
+ }
+
+ private Uri getSliceUri() {
+ return mSearchTarget.getExtras().getParcelable(URI_EXTRA_KEY);
+ }
+
+}
diff --git a/src/com/android/launcher3/views/SearchSettingsRowView.java b/src/com/android/launcher3/views/SearchSettingsRowView.java
index f0884f8..160ee65 100644
--- a/src/com/android/launcher3/views/SearchSettingsRowView.java
+++ b/src/com/android/launcher3/views/SearchSettingsRowView.java
@@ -15,8 +15,14 @@
*/
package com.android.launcher3.views;
+import static com.android.launcher3.FastBitmapDrawable.newIcon;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -27,38 +33,41 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
import com.android.launcher3.allapps.search.SearchEventTracker;
+import com.android.launcher3.model.data.PackageItemInfo;
import com.android.systemui.plugins.shared.SearchTarget;
import com.android.systemui.plugins.shared.SearchTargetEvent;
import java.util.ArrayList;
+import java.util.List;
/**
- * A row of tappable TextViews with a breadcrumb for settings search.
+ * A row of clickable TextViews with a breadcrumb for settings search.
*/
public class SearchSettingsRowView extends LinearLayout implements
View.OnClickListener, AllAppsSearchBarController.SearchTargetHandler {
public static final String TARGET_TYPE_SETTINGS_ROW = "settings_row";
-
+ private View mIconView;
private TextView mTitleView;
- private TextView mDescriptionView;
private TextView mBreadcrumbsView;
private Intent mIntent;
private SearchTarget mSearchTarget;
public SearchSettingsRowView(@NonNull Context context) {
- super(context);
+ this(context, null, 0);
}
public SearchSettingsRowView(@NonNull Context context,
@Nullable AttributeSet attrs) {
- super(context, attrs);
+ this(context, attrs, 0);
}
public SearchSettingsRowView(@NonNull Context context, @Nullable AttributeSet attrs,
@@ -69,10 +78,11 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+ mIconView = findViewById(R.id.icon);
mTitleView = findViewById(R.id.title);
- mDescriptionView = findViewById(R.id.description);
mBreadcrumbsView = findViewById(R.id.breadcrumbs);
setOnClickListener(this);
+ applySettingsIcon(Launcher.getLauncher(getContext()), mIconView);
}
@Override
@@ -81,6 +91,7 @@
Bundle bundle = searchTarget.getExtras();
mIntent = bundle.getParcelable("intent");
showIfAvailable(mTitleView, bundle.getString("title"));
+ mIconView.setContentDescription(bundle.getString("title"));
ArrayList<String> breadcrumbs = bundle.getStringArrayList("breadcrumbs");
//TODO: implement RTL friendly breadcrumbs view
showIfAvailable(mBreadcrumbsView, breadcrumbs != null
@@ -113,4 +124,30 @@
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
}
+
+ /**
+ * Requests settings app icon from {@link com.android.launcher3.icons.IconCache} and applies
+ * to to view
+ */
+ public static void applySettingsIcon(Launcher launcher, View view) {
+ LauncherAppState appState = LauncherAppState.getInstance(launcher);
+ MODEL_EXECUTOR.post(() -> {
+ PackageItemInfo packageItemInfo = new PackageItemInfo(getSettingsPackageName(launcher));
+ appState.getIconCache().getTitleAndIconForApp(packageItemInfo, false);
+ MAIN_EXECUTOR.post(() -> {
+ FastBitmapDrawable iconDrawable = newIcon(appState.getContext(), packageItemInfo);
+ view.setBackground(iconDrawable);
+ });
+ });
+ }
+
+ private static String getSettingsPackageName(Launcher launcher) {
+ Intent intent = new Intent(android.provider.Settings.ACTION_SETTINGS);
+ List<ResolveInfo> resolveInfos = launcher.getPackageManager().queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ if (resolveInfos.size() == 0) {
+ return "";
+ }
+ return resolveInfos.get(0).activityInfo.packageName;
+ }
}
diff --git a/src/com/android/launcher3/views/SearchSliceWrapper.java b/src/com/android/launcher3/views/SearchSliceWrapper.java
deleted file mode 100644
index f8a7dc0..0000000
--- a/src/com/android/launcher3/views/SearchSliceWrapper.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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.net.Uri;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.lifecycle.LiveData;
-import androidx.slice.Slice;
-import androidx.slice.SliceItem;
-import androidx.slice.widget.EventInfo;
-import androidx.slice.widget.SliceLiveData;
-import androidx.slice.widget.SliceView;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.allapps.search.SearchEventTracker;
-import com.android.systemui.plugins.shared.SearchTarget;
-import com.android.systemui.plugins.shared.SearchTargetEvent;
-
-/**
- * A Wrapper class for {@link SliceView} search results
- */
-public class SearchSliceWrapper implements SliceView.OnSliceActionListener {
-
- public static final String TARGET_TYPE_SLICE = "settings_slice";
-
- private static final String TAG = "SearchSliceController";
- private static final String URI_EXTRA_KEY = "slice_uri";
-
-
- private final Launcher mLauncher;
- private final SearchTarget mSearchTarget;
- private final SliceView mSliceView;
- private LiveData<Slice> mSliceLiveData;
-
- public SearchSliceWrapper(Context context, SliceView sliceView, SearchTarget searchTarget) {
- mLauncher = Launcher.getLauncher(context);
- mSearchTarget = searchTarget;
- mSliceView = sliceView;
- sliceView.setOnSliceActionListener(this);
- try {
- mSliceLiveData = SliceLiveData.fromUri(mLauncher, getSliceUri());
- mSliceLiveData.observe((Launcher) mLauncher, sliceView);
- } catch (Exception ex) {
- Log.e(TAG, "unable to bind slice", ex);
- }
- }
-
- /**
- * Unregisters event handlers and removes lifecycle observer
- */
- public void destroy() {
- mSliceView.setOnSliceActionListener(null);
- mSliceLiveData.removeObservers(mLauncher);
- }
-
- @Override
- public void onSliceAction(@NonNull EventInfo info, @NonNull SliceItem item) {
- SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(
- new SearchTargetEvent.Builder(mSearchTarget,
- SearchTargetEvent.CHILD_SELECT).build());
- }
-
- private Uri getSliceUri() {
- return mSearchTarget.getExtras().getParcelable(URI_EXTRA_KEY);
- }
-}