diff options
34 files changed, 344 insertions, 93 deletions
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java index c723b39be..069638f8c 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java @@ -551,6 +551,10 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { @Override public void onClick(View v) { if (mSelectedTile != null) { + // Prevent user from selecting any new tile. + mWallpaperStrip.setVisibility(View.GONE); + actionBar.hide(); + WallpaperTileInfo info = (WallpaperTileInfo) mSelectedTile.getTag(); info.onSave(WallpaperPickerActivity.this); } else { diff --git a/res/drawable-hdpi/ic_arrow_back_grey.png b/res/drawable-hdpi/ic_arrow_back_grey.png Binary files differindex ccd3900dd..c7c00886f 100755 --- a/res/drawable-hdpi/ic_arrow_back_grey.png +++ b/res/drawable-hdpi/ic_arrow_back_grey.png diff --git a/res/drawable-hdpi/ic_search_grey.png b/res/drawable-hdpi/ic_search_grey.png Binary files differindex f4c5e27d2..bd20ba062 100755 --- a/res/drawable-hdpi/ic_search_grey.png +++ b/res/drawable-hdpi/ic_search_grey.png diff --git a/res/drawable-mdpi/ic_arrow_back_grey.png b/res/drawable-mdpi/ic_arrow_back_grey.png Binary files differindex 11996efe3..5892c77d5 100755 --- a/res/drawable-mdpi/ic_arrow_back_grey.png +++ b/res/drawable-mdpi/ic_arrow_back_grey.png diff --git a/res/drawable-mdpi/ic_search_grey.png b/res/drawable-mdpi/ic_search_grey.png Binary files differindex e83891c11..c386dbb38 100755 --- a/res/drawable-mdpi/ic_search_grey.png +++ b/res/drawable-mdpi/ic_search_grey.png diff --git a/res/drawable-v21/all_apps_search_market_bg.xml b/res/drawable-v21/all_apps_search_market_bg.xml new file mode 100644 index 000000000..7bd2f8816 --- /dev/null +++ b/res/drawable-v21/all_apps_search_market_bg.xml @@ -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> diff --git a/res/drawable-xhdpi/ic_arrow_back_grey.png b/res/drawable-xhdpi/ic_arrow_back_grey.png Binary files differindex 79b9b486c..11996efe3 100755 --- a/res/drawable-xhdpi/ic_arrow_back_grey.png +++ b/res/drawable-xhdpi/ic_arrow_back_grey.png diff --git a/res/drawable-xhdpi/ic_search_grey.png b/res/drawable-xhdpi/ic_search_grey.png Binary files differindex bd5fdf444..e83891c11 100755 --- a/res/drawable-xhdpi/ic_search_grey.png +++ b/res/drawable-xhdpi/ic_search_grey.png diff --git a/res/drawable-xxhdpi/ic_arrow_back_grey.png b/res/drawable-xxhdpi/ic_arrow_back_grey.png Binary files differindex 8e42e091d..ccd3900dd 100755 --- a/res/drawable-xxhdpi/ic_arrow_back_grey.png +++ b/res/drawable-xxhdpi/ic_arrow_back_grey.png diff --git a/res/drawable-xxhdpi/ic_search_grey.png b/res/drawable-xxhdpi/ic_search_grey.png Binary files differindex 1d5c91361..f4c5e27d2 100755 --- a/res/drawable-xxhdpi/ic_search_grey.png +++ b/res/drawable-xxhdpi/ic_search_grey.png diff --git a/res/drawable-xxxhdpi/ic_arrow_back_grey.png b/res/drawable-xxxhdpi/ic_arrow_back_grey.png Binary files differindex 854a9bd1a..79b9b486c 100755 --- a/res/drawable-xxxhdpi/ic_arrow_back_grey.png +++ b/res/drawable-xxxhdpi/ic_arrow_back_grey.png diff --git a/res/drawable-xxxhdpi/ic_search_grey.png b/res/drawable-xxxhdpi/ic_search_grey.png Binary files differindex 28519fda6..bd5fdf444 100755 --- a/res/drawable-xxxhdpi/ic_search_grey.png +++ b/res/drawable-xxxhdpi/ic_search_grey.png diff --git a/res/drawable/all_apps_search_market_bg.xml b/res/drawable/all_apps_search_market_bg.xml new file mode 100644 index 000000000..5278e00a6 --- /dev/null +++ b/res/drawable/all_apps_search_market_bg.xml @@ -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> diff --git a/res/drawable/horizontal_line.xml b/res/drawable/horizontal_line.xml new file mode 100644 index 000000000..3f3f17e35 --- /dev/null +++ b/res/drawable/horizontal_line.xml @@ -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> diff --git a/res/layout/all_apps_empty_search.xml b/res/layout/all_apps_empty_search.xml index f60c4a09a..b9b493eab 100644 --- a/res/layout/all_apps_empty_search.xml +++ b/res/layout/all_apps_empty_search.xml @@ -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" /> diff --git a/res/layout/all_apps_search_bar.xml b/res/layout/all_apps_search_bar.xml index c1399715d..0c183eaee 100644 --- a/res/layout/all_apps_search_bar.xml +++ b/res/layout/all_apps_search_bar.xml @@ -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>
\ No newline at end of file diff --git a/res/layout/all_apps_search_market.xml b/res/layout/all_apps_search_market.xml new file mode 100644 index 000000000..1282069c8 --- /dev/null +++ b/res/layout/all_apps_search_market.xml @@ -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" /> diff --git a/res/layout/all_apps_search_market_divider.xml b/res/layout/all_apps_search_market_divider.xml new file mode 100644 index 000000000..39097818f --- /dev/null +++ b/res/layout/all_apps_search_market_divider.xml @@ -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" />
\ No newline at end of file diff --git a/res/values/colors.xml b/res/values/colors.xml index 51e4d40a5..0add48cd8 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -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> diff --git a/res/values/strings.xml b/res/values/strings.xml index 88f149bd0..fefadef28 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -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 /> diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 32e7ee681..298b2c469 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -2137,6 +2137,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(); } @@ -2538,6 +2547,10 @@ public class Launcher extends Activity if (!isAppsViewVisible()) { showAppsView(true /* animated */, false /* resetListToTop */, true /* updatePredictedApps */, false /* focusSearchBar */); + + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onClickAllAppsButton(v); + } } } @@ -2929,7 +2942,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(); diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java index 611ab2e92..3debef60e 100644 --- a/src/com/android/launcher3/LauncherBackupAgentHelper.java +++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java @@ -98,7 +98,7 @@ public class LauncherBackupAgentHelper extends BackupAgentHelper { LauncherAppState.getLauncherProvider().updateFolderItemsRank(); } - if (mHelper.shouldAttemptWorkspaceMigration()) { + if (MigrateFromRestoreTask.ENABLED && mHelper.shouldAttemptWorkspaceMigration()) { MigrateFromRestoreTask.markForMigration(getApplicationContext(), (int) mHelper.migrationCompatibleProfileData.desktopCols, (int) mHelper.migrationCompatibleProfileData.desktopRows, diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index 136556b92..2d11d3af1 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -52,6 +52,7 @@ import com.android.launcher3.backup.BackupProtos.Screen; import com.android.launcher3.backup.BackupProtos.Widget; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.model.MigrateFromRestoreTask; import com.android.launcher3.util.Thunk; import com.google.protobuf.nano.InvalidProtocolBufferNanoException; import com.google.protobuf.nano.MessageNano; @@ -314,7 +315,8 @@ public class LauncherBackupHelper implements BackupHelper { return true; } - if ((oldProfile.desktopCols - currentProfile.desktopCols <= 1) && + if (MigrateFromRestoreTask.ENABLED && + (oldProfile.desktopCols - currentProfile.desktopCols <= 1) && (oldProfile.desktopRows - currentProfile.desktopRows <= 1)) { // Allow desktop migration when row and/or column count contracts by 1. diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index 6618cca78..e34bd57fd 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -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(); diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index f029561a3..d9b0cd490 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -1758,8 +1758,7 @@ public class LauncherModel extends BroadcastReceiver int countX = (int) profile.numColumns; int countY = (int) profile.numRows; - - if (MigrateFromRestoreTask.shouldRunTask(mContext)) { + if (MigrateFromRestoreTask.ENABLED && MigrateFromRestoreTask.shouldRunTask(mContext)) { long migrationStartTime = System.currentTimeMillis(); Log.v(TAG, "Starting workspace migration after restore"); try { diff --git a/src/com/android/launcher3/PreloadIconDrawable.java b/src/com/android/launcher3/PreloadIconDrawable.java index bcb59c448..45e4b2c4a 100644 --- a/src/com/android/launcher3/PreloadIconDrawable.java +++ b/src/com/android/launcher3/PreloadIconDrawable.java @@ -213,6 +213,10 @@ class PreloadIconDrawable extends Drawable { return mAnimationProgress; } + public boolean hasNotCompleted() { + return mAnimationProgress < ANIMATION_PROGRESS_COMPLETED; + } + @Override public int getIntrinsicHeight() { return mIcon.getIntrinsicHeight(); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 17e59a6d7..e83ef7c2b 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -4334,8 +4334,9 @@ public class Workspace extends PagedView updates.contains(info)) { ShortcutInfo si = (ShortcutInfo) info; BubbleTextView shortcut = (BubbleTextView) v; - boolean oldPromiseState = getTextViewIcon(shortcut) - instanceof PreloadIconDrawable; + Drawable oldIcon = getTextViewIcon(shortcut); + boolean oldPromiseState = (oldIcon instanceof PreloadIconDrawable) + && ((PreloadIconDrawable) oldIcon).hasNotCompleted(); shortcut.applyFromShortcutInfo(si, mIconCache, si.isPromise() != oldPromiseState); diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 010b2cb48..e129dc6d3 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -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(); } } diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index e613d50bc..f7c4489be 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -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 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. 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 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. @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 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. @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 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. } } + private Launcher mLauncher; private LayoutInflater mLayoutInflater; @Thunk AlphabeticalAppsList mApps; private GridLayoutManager mGridLayoutMgr; @@ -326,7 +330,19 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. @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 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. @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 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. 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 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. } /** - * 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 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. @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 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. 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 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. 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; + } } diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index 730c8d15a..5aa973a15 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -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); diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 47241ce5d..396f75790 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -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; @@ -194,6 +216,13 @@ public class AlphabeticalAppsList { } /** + * Disables the empty text message when there are no search results. + */ + public void disableEmptyText() { + mDisableEmptyText = true; + } + + /** * Returns all the apps. */ public List<AppInfo> getApps() { @@ -222,17 +251,17 @@ public class AlphabeticalAppsList { } /** - * Returns the number of applications in this list. + * Returns the number of rows of applications (not including predictions) */ - public int getSize() { - return mFilteredApps.size(); + public int getNumAppRows() { + return mNumAppRowsInAdapter; } /** - * Returns the number of rows of applications (not including predictions) + * Returns the number of applications in this list. */ - public int getNumAppRows() { - return mNumAppRowsInAdapter; + public int getNumFilteredApps() { + return mFilteredApps.size(); } /** @@ -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(); diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchController.java b/src/com/android/launcher3/allapps/DefaultAppSearchController.java index 39b48aada..e1a2b7fda 100644 --- a/src/com/android/launcher3/allapps/DefaultAppSearchController.java +++ b/src/com/android/launcher3/allapps/DefaultAppSearchController.java @@ -169,19 +169,21 @@ final class DefaultAppSearchController extends AllAppsSearchBarController if (actionId != EditorInfo.IME_ACTION_SEARCH) { 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; diff --git a/src/com/android/launcher3/model/MigrateFromRestoreTask.java b/src/com/android/launcher3/model/MigrateFromRestoreTask.java index 8d4472fd1..6a529f61f 100644 --- a/src/com/android/launcher3/model/MigrateFromRestoreTask.java +++ b/src/com/android/launcher3/model/MigrateFromRestoreTask.java @@ -37,6 +37,8 @@ import java.util.HashSet; */ public class MigrateFromRestoreTask { + public static boolean ENABLED = false; + private static final String TAG = "MigrateFromRestoreTask"; private static final boolean DEBUG = true; diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java index 34492e4ca..8702877bf 100644 --- a/src/com/android/launcher3/testing/LauncherExtension.java +++ b/src/com/android/launcher3/testing/LauncherExtension.java @@ -202,6 +202,11 @@ public class LauncherExtension extends Launcher { } @Override + public boolean startSearchFromAllApps(String query) { + return false; + } + + @Override public void startVoice() { } |