Show settings icon at start of settings results

Bug: 170792963
Test: Manual
Screenshot: https://screenshot.googleplex.com/MVBtDZGtQ3aChwU
Change-Id: I16a28a7bd9e3129b40102e020cd7c0111e6fe29b
This commit is contained in:
Samuel Fufa 2020-11-02 13:45:08 -06:00
parent d15915f5fd
commit 984e0d0a63
7 changed files with 178 additions and 84 deletions

View File

@ -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" />

View File

@ -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>

View File

@ -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" />
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>

View File

@ -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>

View File

@ -37,7 +37,6 @@ import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
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.allapps.search.SearchSectionInfo;
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;
@ -461,17 +459,10 @@ public class AllAppsGridAdapter extends
} else {
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 @@ public class AllAppsGridAdapter extends
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

View File

@ -17,9 +17,13 @@ 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;
@ -28,55 +32,98 @@ 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 Wrapper class for {@link SliceView} search results
* A slice view wrapper with settings app icon at start
*/
public class SearchSliceWrapper implements SliceView.OnSliceActionListener {
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 final Launcher mLauncher;
private final SearchTarget mSearchTarget;
private final SliceView mSliceView;
private SliceView mSliceView;
private View mIcon;
private LiveData<Slice> mSliceLiveData;
private SearchTarget mSearchTarget;
private Launcher mLauncher;
public SearchSliceWrapper(Context context, SliceView sliceView, SearchTarget searchTarget) {
mLauncher = Launcher.getLauncher(context);
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;
mSliceView = sliceView;
sliceView.setOnSliceActionListener(this);
try {
mSliceLiveData = SliceLiveData.fromUri(mLauncher, getSliceUri());
mSliceLiveData.observe((Launcher) mLauncher, sliceView);
mSliceLiveData.observe(mLauncher, mSliceView);
} 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
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mSliceView.setOnSliceActionListener(this);
}
@Override
public void onSliceAction(@NonNull EventInfo info, @NonNull SliceItem item) {
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);
}
}

View File

@ -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 android.widget.TextView;
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 @@ public class SearchSettingsRowView extends LinearLayout implements
@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 @@ public class SearchSettingsRowView extends LinearLayout implements
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 @@ public class SearchSettingsRowView extends LinearLayout implements
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;
}
}