Adding market search.

Change-Id: Id41615653cd4fa76213add4595418ad0cc6e7852
This commit is contained in:
Winson Chung 2015-07-10 12:38:30 -07:00 committed by Winson
parent d1ea63f24a
commit bedf9232eb
27 changed files with 327 additions and 88 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 B

After

Width:  |  Height:  |  Size: 152 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 743 B

After

Width:  |  Height:  |  Size: 419 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 B

After

Width:  |  Height:  |  Size: 117 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 497 B

After

Width:  |  Height:  |  Size: 263 B

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/all_apps_search_market_button_focused_bg_color">
<item android:drawable="@color/quantum_panel_bg_color" />
</ripple>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 B

After

Width:  |  Height:  |  Size: 151 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 972 B

After

Width:  |  Height:  |  Size: 497 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 308 B

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 743 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 359 B

After

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 972 B

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:drawable="@color/all_apps_search_market_button_focused_bg_color" />
<item android:state_pressed="true" android:drawable="@color/all_apps_search_market_button_focused_bg_color" />
<item android:drawable="@android:color/transparent" />
</selector>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:height="1dp" />
<solid android:color="#ddd" />
</shape>

View File

@ -18,11 +18,14 @@
android:id="@+id/empty_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingTop="24dp"
android:paddingBottom="24dp"
android:paddingRight="@dimen/all_apps_grid_view_start_margin"
android:textSize="16sp"
android:textColor="#4c4c4c"
android:gravity="start"
android:paddingTop="20dp"
android:paddingBottom="8dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:fontFamily="sans-serif-medium"
android:textSize="14sp"
android:textColor="#212121"
android:alpha="0.56"
android:focusable="false" />

View File

@ -32,11 +32,10 @@
android:id="@+id/dismiss_search_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="16dp"
android:layout_marginStart="16dp"
android:contentDescription="@string/all_apps_button_label"
android:paddingBottom="13dp"
android:paddingTop="13dp"
android:src="@drawable/ic_arrow_back_grey" />
<com.android.launcher3.allapps.AllAppsSearchEditView
@ -63,10 +62,8 @@
android:layout_width="wrap_content"
android:layout_height="@dimen/all_apps_search_bar_height"
android:layout_gravity="end|center_vertical"
android:layout_marginEnd="4dp"
android:layout_marginRight="4dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:contentDescription="@string/all_apps_search_bar_hint"
android:paddingBottom="13dp"
android:paddingTop="13dp"
android:src="@drawable/ic_search_grey" />
</FrameLayout>

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/search_market_text"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:gravity="start|center_vertical"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:fontFamily="sans-serif-medium"
android:textSize="14sp"
android:textColor="#009688"
android:textAllCaps="true"
android:focusable="false"
android:background="@drawable/all_apps_search_market_bg" />

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingTop="16dp"
android:paddingBottom="8dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:focusable="false"
android:scaleType="matrix"
android:src="@drawable/horizontal_line" />

View File

@ -44,6 +44,7 @@
<!-- All Apps -->
<color name="all_apps_grid_section_text_color">#009688</color>
<color name="all_apps_search_market_button_focused_bg_color">#DDDDDD</color>
<!-- Widgets view -->
<color name="widgets_view_fastscroll_thumb_inactive_color">#42FFFFFF</color>

View File

@ -24,10 +24,10 @@
<!-- URI used to import old favorites. [DO NOT TRANSLATE] -->
<string name="old_launcher_provider_uri" translatable="false">content://com.android.launcher2.settings/favorites?notify=true</string>
<!-- Permission to receive the com.android.launcher3.action.LAUNCH intent -->
<!-- Permission to receive the com.android.launcher3.action.LAUNCH intent. [DO NOT TRANSLATE] -->
<string name="receive_launch_broadcasts_permission" translatable="false">com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS</string>
<!-- Permission to receive the com.android.launcher3.action.FIRST_LOAD_COMPLETE intent -->
<!-- Permission to receive the com.android.launcher3.action.FIRST_LOAD_COMPLETE intent. [DO NOT TRANSLATE] -->
<string name="receive_first_load_broadcast_permission" translatable="false">com.android.launcher3.permission.RECEIVE_FIRST_LOAD_BROADCAST</string>
<!-- Application name -->
@ -61,6 +61,9 @@
<string name="all_apps_loading_message">Loading Apps&#8230;</string>
<!-- No-search-results text. [CHAR_LIMIT=50] -->
<string name="all_apps_no_search_results">No Apps found matching \"<xliff:g id="query" example="Android">%1$s</xliff:g>\"</string>
<!-- Search market text. This is a format string where the first argument is the name of the activity
handling the search. The format string does not need to handle both of these arguments. [CHAR_LIMIT=50] -->
<string name="all_apps_search_market_message">Go to <xliff:g id="query" example="Play Store">%1$s</xliff:g></string>
<!-- Drag and drop -->
<skip />

View File

@ -2132,6 +2132,15 @@ public class Launcher extends Activity
}
}
public void startSearchFromAllApps(View v, Intent searchIntent, String searchQuery) {
if (mLauncherCallbacks != null && mLauncherCallbacks.startSearchFromAllApps(searchQuery)) {
return;
}
// If not handled, then just start the provided search intent
startActivitySafely(v, searchIntent, null);
}
public boolean isOnCustomContent() {
return mWorkspace.isOnOrMovingToCustomContent();
}
@ -2533,6 +2542,10 @@ public class Launcher extends Activity
if (!isAppsViewVisible()) {
showAppsView(true /* animated */, false /* resetListToTop */,
true /* updatePredictedApps */, false /* focusSearchBar */);
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onClickAllAppsButton(v);
}
}
}
@ -2924,7 +2937,7 @@ public class Launcher extends Activity
return false;
}
@Thunk boolean startActivitySafely(View v, Intent intent, Object tag) {
public boolean startActivitySafely(View v, Intent intent, Object tag) {
boolean success = false;
if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();

View File

@ -77,6 +77,7 @@ public interface LauncherCallbacks {
public boolean providesSearch();
public boolean startSearch(String initialQuery, boolean selectInitialQuery,
Bundle appSearchData, Rect sourceBounds);
public boolean startSearchFromAllApps(String query);
@Deprecated
public void startVoice();
public boolean hasCustomContentToLeft();

View File

@ -16,34 +16,26 @@
package com.android.launcher3.allapps;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.InsetDrawable;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.widget.RecyclerView;
import android.text.Selection;
import android.text.SpannableStringBuilder;
import android.text.method.TextKeyListener;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import com.android.launcher3.AppInfo;
import com.android.launcher3.BaseContainerView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.CheckLongPressHelper;
import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DragSource;
@ -53,7 +45,6 @@ import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherTransitionable;
import com.android.launcher3.R;
import com.android.launcher3.Stats;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.util.ComponentKey;
@ -155,6 +146,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
@Thunk AllAppsSearchBarController mSearchBarController;
private ViewGroup mSearchBarContainerView;
private View mSearchBarView;
private SpannableStringBuilder mSearchQueryBuilder = null;
private int mSectionNamesMargin;
private int mNumAppsPerRow;
@ -165,7 +157,13 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
// This coordinate is relative to its parent
private final Point mIconLastTouchPos = new Point();
private SpannableStringBuilder mSearchQueryBuilder = null;
private View.OnClickListener mSearchClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent searchIntent = (Intent) v.getTag();
mLauncher.startActivitySafely(v, searchIntent, null);
}
};
public AllAppsContainerView(Context context) {
this(context, null);
@ -182,8 +180,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
mLauncher = (Launcher) context;
mSectionNamesMargin = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
mApps = new AlphabeticalAppsList(context);
mAdapter = new AllAppsGridAdapter(context, mApps, this, mLauncher, this);
mAdapter.setEmptySearchText(res.getString(R.string.all_apps_loading_message));
mAdapter = new AllAppsGridAdapter(mLauncher, mApps, this, mLauncher, this);
mApps.setAdapter(mAdapter);
mLayoutManager = mAdapter.getLayoutManager();
mItemDecoration = mAdapter.getItemDecoration();
@ -615,13 +612,9 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
@Override
public void onSearchResult(String query, ArrayList<ComponentKey> apps) {
if (apps != null) {
if (apps.isEmpty()) {
String formatStr = getResources().getString(R.string.all_apps_no_search_results);
mAdapter.setEmptySearchText(String.format(formatStr, query));
} else {
mAppsRecyclerView.scrollToTop();
}
mApps.setOrderedFilter(apps);
mAdapter.setLastSearchQuery(query);
mAppsRecyclerView.scrollToTop();
}
}

View File

@ -16,14 +16,17 @@
package com.android.launcher3.allapps;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Handler;
import android.support.v4.view.accessibility.AccessibilityRecordCompat;
import android.support.v4.view.accessibility.AccessibilityEventCompat;
import android.net.Uri;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
@ -34,6 +37,7 @@ import android.view.accessibility.AccessibilityEvent;
import android.widget.TextView;
import com.android.launcher3.AppInfo;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Thunk;
@ -58,6 +62,10 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
public static final int PREDICTION_ICON_VIEW_TYPE = 2;
// The message shown when there are no filtered results
public static final int EMPTY_SEARCH_VIEW_TYPE = 3;
// A divider that separates the apps list and the search market button
public static final int SEARCH_MARKET_DIVIDER_VIEW_TYPE = 4;
// The message to continue to a market search when there are no filtered results
public static final int SEARCH_MARKET_VIEW_TYPE = 5;
/**
* ViewHolder for each icon.
@ -83,12 +91,12 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
if (mApps.hasNoFilteredResults()) {
// Disregard the no-search-results text as a list item for accessibility
final AccessibilityRecordCompat record = AccessibilityEventCompat
.asRecord(event);
record.setItemCount(0);
}
// Ensure that we only report the number apps for accessibility not including other
// adapter views
final AccessibilityRecordCompat record = AccessibilityEventCompat
.asRecord(event);
record.setItemCount(mApps.getNumFilteredApps());
}
@Override
@ -115,11 +123,6 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
@Override
public int getSpanSize(int position) {
if (mApps.hasNoFilteredResults()) {
// Empty view spans full width
return mAppsPerRow;
}
switch (mApps.getAdapterItems().get(position).viewType) {
case AllAppsGridAdapter.ICON_VIEW_TYPE:
case AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE:
@ -314,6 +317,7 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
}
}
private Launcher mLauncher;
private LayoutInflater mLayoutInflater;
@Thunk AlphabeticalAppsList mApps;
private GridLayoutManager mGridLayoutMgr;
@ -326,7 +330,19 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
@Thunk int mPredictionBarDividerOffset;
@Thunk int mAppsPerRow;
@Thunk boolean mIsRtl;
private String mEmptySearchText;
// The text to show when there are no search results and no market search handler.
private String mEmptySearchMessage;
// The name of the market app which handles searches, to be used in the format str
// below when updating the search-market view. Only needs to be loaded once.
private String mMarketAppName;
// The text to show when there is a market app which can handle a specific query, updated
// each time the search query changes.
private String mMarketSearchMessage;
// The intent to send off to the market app, updated each time the search query changes.
private Intent mMarketSearchIntent;
// The last query that the user entered into the search field
private String mLastSearchQuery;
// Section drawing
@Thunk int mSectionNamesMargin;
@ -334,16 +350,18 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
@Thunk Paint mSectionTextPaint;
@Thunk Paint mPredictedAppsDividerPaint;
public AllAppsGridAdapter(Context context, AlphabeticalAppsList apps,
public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps,
View.OnTouchListener touchListener, View.OnClickListener iconClickListener,
View.OnLongClickListener iconLongClickListener) {
Resources res = context.getResources();
Resources res = launcher.getResources();
mLauncher = launcher;
mApps = apps;
mEmptySearchMessage = res.getString(R.string.all_apps_loading_message);
mGridSizer = new GridSpanSizer();
mGridLayoutMgr = new AppsGridLayoutManager(context);
mGridLayoutMgr = new AppsGridLayoutManager(launcher);
mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
mItemDecoration = new GridItemDecoration();
mLayoutInflater = LayoutInflater.from(context);
mLayoutInflater = LayoutInflater.from(launcher);
mTouchListener = touchListener;
mIconClickListener = iconClickListener;
mIconLongClickListener = iconLongClickListener;
@ -363,6 +381,14 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
mPredictionBarDividerOffset =
(-res.getDimensionPixelSize(R.dimen.all_apps_prediction_icon_bottom_padding) +
res.getDimensionPixelSize(R.dimen.all_apps_icon_top_bottom_padding)) / 2;
// Resolve the market app handling additional searches
PackageManager pm = launcher.getPackageManager();
ResolveInfo marketInfo = pm.resolveActivity(createMarketSearchIntent(""),
PackageManager.MATCH_DEFAULT_ONLY);
if (marketInfo != null) {
mMarketAppName = marketInfo.loadLabel(pm).toString();
}
}
/**
@ -381,10 +407,19 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
}
/**
* Sets the text to show when there are no apps.
* Sets the last search query that was made, used to show when there are no results and to also
* seed the intent for searching the market.
*/
public void setEmptySearchText(String query) {
mEmptySearchText = query;
public void setLastSearchQuery(String query) {
Resources res = mLauncher.getResources();
String formatStr = res.getString(R.string.all_apps_no_search_results);
mLastSearchQuery = query;
mEmptySearchMessage = String.format(formatStr, query);
if (mMarketAppName != null) {
mMarketSearchMessage = String.format(res.getString(R.string.all_apps_search_market_message),
mMarketAppName);
mMarketSearchIntent = createMarketSearchIntent(query);
}
}
/**
@ -413,9 +448,6 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case EMPTY_SEARCH_VIEW_TYPE:
return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search, parent,
false));
case SECTION_BREAK_VIEW_TYPE:
return new ViewHolder(new View(parent.getContext()));
case ICON_VIEW_TYPE: {
@ -440,6 +472,22 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
icon.setFocusable(true);
return new ViewHolder(icon);
}
case EMPTY_SEARCH_VIEW_TYPE:
return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search,
parent, false));
case SEARCH_MARKET_DIVIDER_VIEW_TYPE:
return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_search_market_divider,
parent, false));
case SEARCH_MARKET_VIEW_TYPE:
View searchMarketView = mLayoutInflater.inflate(R.layout.all_apps_search_market,
parent, false);
searchMarketView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mLauncher.startSearchFromAllApps(v, mMarketSearchIntent, mLastSearchQuery);
}
});
return new ViewHolder(searchMarketView);
default:
throw new RuntimeException("Unexpected view type");
}
@ -461,28 +509,44 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
break;
}
case EMPTY_SEARCH_VIEW_TYPE:
TextView emptyViewText = (TextView) holder.mContent.findViewById(R.id.empty_text);
emptyViewText.setText(mEmptySearchText);
TextView emptyViewText = (TextView) holder.mContent;
emptyViewText.setText(mEmptySearchMessage);
break;
case SEARCH_MARKET_VIEW_TYPE:
View searchView = holder.mContent;
if (mMarketSearchIntent != null) {
searchView.setVisibility(View.VISIBLE);
searchView.setContentDescription(mMarketSearchMessage);
((TextView) searchView.findViewById(R.id.search_market_text))
.setText(mMarketSearchMessage);
} else {
searchView.setVisibility(View.GONE);
}
break;
}
}
@Override
public int getItemCount() {
if (mApps.hasNoFilteredResults()) {
// For the empty view
return 1;
}
return mApps.getAdapterItems().size();
}
@Override
public int getItemViewType(int position) {
if (mApps.hasNoFilteredResults()) {
return EMPTY_SEARCH_VIEW_TYPE;
}
AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position);
return item.viewType;
}
/**
* Creates a new market search intent.
*/
private Intent createMarketSearchIntent(String query) {
Uri marketSearchUri = Uri.parse("market://search")
.buildUpon()
.appendQueryParameter("q", query)
.build();
Intent marketSearchIntent = new Intent(Intent.ACTION_VIEW);
marketSearchIntent.setData(marketSearchUri);
return marketSearchIntent;
}
}

View File

@ -90,6 +90,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView
RecyclerView.RecycledViewPool pool = getRecycledViewPool();
int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx);
pool.setMaxRecycledViews(AllAppsGridAdapter.EMPTY_SEARCH_VIEW_TYPE, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.SEARCH_MARKET_DIVIDER_VIEW_TYPE, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.SEARCH_MARKET_VIEW_TYPE, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.ICON_VIEW_TYPE, approxRows * mNumAppsPerRow);
pool.setMaxRecycledViews(AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE, mNumAppsPerRow);
pool.setMaxRecycledViews(AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE, approxRows);

View File

@ -81,12 +81,12 @@ public class AlphabeticalAppsList {
public int position;
// The type of this item
public int viewType;
// The row that this item shows up on
public int rowIndex;
/** Section & App properties */
// The section for this item
public SectionInfo sectionInfo;
// The row that this item shows up on
public int rowIndex;
/** App-only properties */
// The section name of this app. Note that there can be multiple items with different
@ -111,14 +111,14 @@ public class AlphabeticalAppsList {
}
public static AdapterItem asPredictedApp(int pos, SectionInfo section, String sectionName,
int sectionAppIndex, AppInfo appInfo, int appIndex) {
int sectionAppIndex, AppInfo appInfo, int appIndex) {
AdapterItem item = asApp(pos, section, sectionName, sectionAppIndex, appInfo, appIndex);
item.viewType = AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE;
return item;
}
public static AdapterItem asApp(int pos, SectionInfo section, String sectionName,
int sectionAppIndex, AppInfo appInfo, int appIndex) {
int sectionAppIndex, AppInfo appInfo, int appIndex) {
AdapterItem item = new AdapterItem();
item.viewType = AllAppsGridAdapter.ICON_VIEW_TYPE;
item.position = pos;
@ -129,6 +129,27 @@ public class AlphabeticalAppsList {
item.appIndex = appIndex;
return item;
}
public static AdapterItem asEmptySearch(int pos) {
AdapterItem item = new AdapterItem();
item.viewType = AllAppsGridAdapter.EMPTY_SEARCH_VIEW_TYPE;
item.position = pos;
return item;
}
public static AdapterItem asDivider(int pos) {
AdapterItem item = new AdapterItem();
item.viewType = AllAppsGridAdapter.SEARCH_MARKET_DIVIDER_VIEW_TYPE;
item.position = pos;
return item;
}
public static AdapterItem asMarketSearch(int pos) {
AdapterItem item = new AdapterItem();
item.viewType = AllAppsGridAdapter.SEARCH_MARKET_VIEW_TYPE;
item.position = pos;
return item;
}
}
/**
@ -167,6 +188,7 @@ public class AlphabeticalAppsList {
private int mNumAppsPerRow;
private int mNumPredictedAppsPerRow;
private int mNumAppRowsInAdapter;
private boolean mDisableEmptyText;
public AlphabeticalAppsList(Context context) {
mLauncher = (Launcher) context;
@ -193,6 +215,13 @@ public class AlphabeticalAppsList {
mAdapter = adapter;
}
/**
* Disables the empty text message when there are no search results.
*/
public void disableEmptyText() {
mDisableEmptyText = true;
}
/**
* Returns all the apps.
*/
@ -221,13 +250,6 @@ public class AlphabeticalAppsList {
return mAdapterItems;
}
/**
* Returns the number of applications in this list.
*/
public int getSize() {
return mFilteredApps.size();
}
/**
* Returns the number of rows of applications (not including predictions)
*/
@ -235,6 +257,13 @@ public class AlphabeticalAppsList {
return mNumAppRowsInAdapter;
}
/**
* Returns the number of applications in this list.
*/
public int getNumFilteredApps() {
return mFilteredApps.size();
}
/**
* Returns whether there are is a filter set.
*/
@ -457,6 +486,16 @@ public class AlphabeticalAppsList {
mFilteredApps.add(info);
}
// Append the search market item if we are currently searching
if (hasFilter()) {
if (hasNoFilteredResults()) {
mAdapterItems.add(AdapterItem.asEmptySearch(position++));
} else {
mAdapterItems.add(AdapterItem.asDivider(position++));
}
mAdapterItems.add(AdapterItem.asMarketSearch(position++));
}
// Merge multiple sections together as requested by the merge strategy for this device
mergeSections();

View File

@ -169,19 +169,21 @@ final class DefaultAppSearchController extends AllAppsSearchBarController
if (actionId != EditorInfo.IME_ACTION_DONE) {
return false;
}
// Skip if there isn't exactly one item
if (mApps.getSize() != 1) {
// Skip if there are more than one icon
if (mApps.getNumFilteredApps() > 1) {
return false;
}
// If there is exactly one icon, then quick-launch it
// Otherwise, find the first icon, or fallback to the search-market-view and launch it
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
for (int i = 0; i < items.size(); i++) {
AlphabeticalAppsList.AdapterItem item = items.get(i);
if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) {
mAppsRecyclerView.getChildAt(i).performClick();
mInputMethodManager.hideSoftInputFromWindow(
mContainerView.getWindowToken(), 0);
return true;
switch (item.viewType) {
case AllAppsGridAdapter.ICON_VIEW_TYPE:
case AllAppsGridAdapter.SEARCH_MARKET_VIEW_TYPE:
mAppsRecyclerView.getChildAt(i).performClick();
mInputMethodManager.hideSoftInputFromWindow(
mContainerView.getWindowToken(), 0);
return true;
}
}
return false;

View File

@ -201,6 +201,11 @@ public class LauncherExtension extends Launcher {
return false;
}
@Override
public boolean startSearchFromAllApps(String query) {
return false;
}
@Override
public void startVoice() {
}