From 114de69c9812284f541b0588ea68c71c2b8bb739 Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Sat, 15 Aug 2020 09:40:26 -0700 Subject: [PATCH] Introduce support for play results in launcher Introduces PluginSearchPipeline class, a plugin listener for AllAppsSearchPlugin. Coverts from List results from callback to AdapterItems to be rendered in SearchController. - Moves AdapterItem to AllAppsGridAdapter Bug: 164699827 Test: Manual Change-Id: I20ec147e6b3f4707cf69d62b4b4ac70a90196345 --- res/layout/search_result_play_item.xml | 75 +++++++++ res/layout/search_section_title.xml | 16 +- res/values/strings.xml | 2 + .../allapps/AllAppsContainerView.java | 21 +++ .../launcher3/allapps/AllAppsGridAdapter.java | 125 ++++++++++++-- .../allapps/AllAppsRecyclerView.java | 12 +- .../allapps/AllAppsSectionDecorator.java | 4 +- .../allapps/AlphabeticalAppsList.java | 101 +----------- .../search/AllAppsSearchBarController.java | 95 ++++------- .../search/AppsSearchContainerLayout.java | 3 +- .../allapps/search/AppsSearchPipeline.java | 11 +- .../allapps/search/SearchPipeline.java | 4 +- .../allapps/search/SearchSectionInfo.java | 17 +- .../launcher3/views/HeroSearchResultView.java | 13 +- .../launcher3/views/SearchResultPlayItem.java | 154 ++++++++++++++++++ .../views/SearchSectionHeaderView.java | 55 +++++++ 16 files changed, 493 insertions(+), 215 deletions(-) create mode 100644 res/layout/search_result_play_item.xml create mode 100644 src/com/android/launcher3/views/SearchResultPlayItem.java create mode 100644 src/com/android/launcher3/views/SearchSectionHeaderView.java diff --git a/res/layout/search_result_play_item.xml b/res/layout/search_result_play_item.xml new file mode 100644 index 0000000000..4e82eafc61 --- /dev/null +++ b/res/layout/search_result_play_item.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + diff --git a/res/layout/search_section_title.xml b/res/layout/search_section_title.xml index c39a641d66..941901591c 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. --> - \ No newline at end of file + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index ef47eefba1..ad3e2b79f4 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -70,6 +70,8 @@ Apps + + Try Now diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index af3722a1f8..99fff4d095 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; @@ -527,6 +529,25 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo return mViewPager == null ? getActiveRecyclerView() : mViewPager; } + /** + * 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. */ diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index 2cec797fd0..c61f01f206 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -40,10 +40,10 @@ import androidx.recyclerview.widget.RecyclerView; 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 class AllAppsGridAdapter extends RecyclerView.Adapter Play load Type + */ + public static class AdapterItemWithPayload 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. */ @@ -286,6 +390,9 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter items = mApps.getAdapterItems(); + List 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 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine @Override public int getCurrentScrollY() { // Return early if there are no items or we haven't been measured - List items = mApps.getAdapterItems(); + List items = mApps.getAdapterItems(); if (items.isEmpty() || mNumAppsPerRow == 0 || getChildCount() == 0) { return -1; } @@ -368,14 +368,14 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine } public int getCurrentScrollY(int position, int offset) { - List items = mApps.getAdapterItems(); - AlphabeticalAppsList.AdapterItem posItem = position < items.size() ? - items.get(position) : null; + List 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 a168c06d72..6f29e1102c 100644 --- a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java +++ b/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java @@ -47,13 +47,13 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration { // 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 adapterItems = mAppsView.getApps().getAdapterItems(); + List 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 7379dbed9a..8c059d5fce 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -19,10 +19,10 @@ package com.android.launcher3.allapps; 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 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener { } } - /** - * 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 mShortcutInfos; - - public HeroAppAdapterItem(AppInfo info, ArrayList shortcutInfos) { - viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_HERO_APP; - mShortcutInfos = shortcutInfos; - appInfo = info; - } - - /** - * Returns list of shortcuts for appInfo - */ - public ArrayList getShortcutInfos() { - return mShortcutInfos; - } - - } - private final BaseDraggingActivity mLauncher; @@ -396,7 +301,9 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener { 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 0d8748165a..2e5ed3ec35 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 @@ import java.util.function.Consumer; */ public class AllAppsSearchBarController implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener, - OnFocusChangeListener, PluginListener { + 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 @@ public class AllAppsSearchBarController 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 @@ public class AllAppsSearchBarController } else { mSearchAlgorithm.cancel(false); mSearchAlgorithm.doSearch(mQuery, mCb); - if (mPlugin != null) { - mPlugin.performSearch(mQuery, mPlubinCb); - } } } @@ -133,10 +113,8 @@ public class AllAppsSearchBarController 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 @@ public class AllAppsSearchBarController 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 plugin); } /** @@ -246,11 +195,23 @@ public class AllAppsSearchBarController * * @param items sorted list of search result adapter items. */ - void onSearchResult(String query, ArrayList items); + void onSearchResult(String query, ArrayList items); /** * Called when the search results should be cleared. */ void clearSearchResult(); } + + /** + * An interface for supporting dynamic search results + * + * @param Type of payload + */ + public interface PayloadResultHandler { + /** + * Updates View using Adapter's payload + */ + void applyAdapterInfo(AdapterItemWithPayload 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 e8a0d7a493..6f183eebc0 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.Insettable; 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 @@ public class AppsSearchContainerLayout extends ExtendedEditText } @Override - public void onSearchResult(String query, ArrayList items) { + public void onSearchResult(String query, ArrayList 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 e67e8972e7..fb3d953bac 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 android.content.pm.ShortcutInfo; 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 @@ public class AppsSearchPipeline implements SearchPipeline { /** * 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 getShortcutInfos(Context context, AppInfo appInfo) { List shortcuts = new ShortcutRequest(context, appInfo.user) @@ -126,7 +127,9 @@ public class AppsSearchPipeline implements SearchPipeline { //hero app AppInfo appInfo = apps.get(i); ArrayList 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 321674025a..545f0e3fa2 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 @@ public interface SearchPipeline { /** * Perform query */ - void performSearch(String query, Consumer> cb); + void performSearch(String query, Consumer> cb); } diff --git a/src/com/android/launcher3/allapps/search/SearchSectionInfo.java b/src/com/android/launcher3/allapps/search/SearchSectionInfo.java index dee0ffd2df..e026e8449d 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 @@ import com.android.launcher3.allapps.AllAppsSectionDecorator.SectionDecorationHa */ public class SearchSectionInfo { - private final int mTitleResId; + private String mTitle; private SectionDecorationHandler mDecorationHandler; public int getPosStart() { @@ -47,11 +45,11 @@ public class SearchSectionInfo { 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 @@ public class SearchSectionInfo { /** * 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/views/HeroSearchResultView.java b/src/com/android/launcher3/views/HeroSearchResultView.java index c2a02bcdab..10f3c41f35 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.DragSource; 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 @@ import java.util.List; /** * 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> { BubbleTextView mBubbleTextView; View mIconView; @@ -96,18 +98,18 @@ public class HeroSearchResultView extends LinearLayout implements DragSource { /** * Apply {@link ItemInfo} for appIcon and shortcut Icons */ - public void prepareUsingAdapterItem(AlphabeticalAppsList.HeroAppAdapterItem adapterItem) { + @Override + public void applyAdapterInfo(AdapterItemWithPayload> adapterItem) { mBubbleTextView.applyFromApplicationInfo(adapterItem.appInfo); mIconView.setBackground(mBubbleTextView.getIcon()); mIconView.setTag(adapterItem.appInfo); - List shorcutInfos = adapterItem.getShortcutInfos(); + List 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 @@ public class HeroSearchResultView extends LinearLayout implements DragSource { 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 0000000000..19a4c5d84c --- /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 { + 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 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 0000000000..d439ee3d68 --- /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 { + 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 adapterItem) { + String title = adapterItem.getPayload(); + if (title == null || !title.isEmpty()) { + setText(title); + setVisibility(VISIBLE); + } else { + setVisibility(INVISIBLE); + } + } +}