Adding market search.
Change-Id: Id41615653cd4fa76213add4595418ad0cc6e7852
Before Width: | Height: | Size: 190 B After Width: | Height: | Size: 152 B |
Before Width: | Height: | Size: 743 B After Width: | Height: | Size: 419 B |
Before Width: | Height: | Size: 151 B After Width: | Height: | Size: 117 B |
Before Width: | Height: | Size: 497 B After Width: | Height: | Size: 263 B |
|
@ -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>
|
Before Width: | Height: | Size: 234 B After Width: | Height: | Size: 151 B |
Before Width: | Height: | Size: 972 B After Width: | Height: | Size: 497 B |
Before Width: | Height: | Size: 308 B After Width: | Height: | Size: 190 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 743 B |
Before Width: | Height: | Size: 359 B After Width: | Height: | Size: 234 B |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 972 B |
|
@ -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>
|
|
@ -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>
|
|
@ -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" />
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -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" />
|
|
@ -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" />
|
|
@ -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>
|
||||
|
|
|
@ -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…</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 />
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -201,6 +201,11 @@ public class LauncherExtension extends Launcher {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startSearchFromAllApps(String query) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startVoice() {
|
||||
}
|
||||
|
|