Merge "Import translations. DO NOT MERGE" into ub-launcher3-dorval-polish
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index d6bdac2..7d97f25 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -64,33 +64,9 @@
android:layout_alignParentEnd="true"
android:layout_marginEnd="@dimen/container_fastscroll_popup_margin" />
- <FrameLayout
- android:id="@+id/search_container"
- android:layout_width="match_parent"
- android:layout_height="@dimen/all_apps_search_bar_height"
- android:layout_gravity="center|top"
- android:gravity="center|bottom"
- android:orientation="horizontal"
- android:saveEnabled="false">
-
- <com.android.launcher3.ExtendedEditText
- android:id="@+id/search_box_input"
- android:layout_width="match_parent"
- android:layout_height="@dimen/all_apps_search_bar_field_height"
- android:background="@android:color/transparent"
- android:layout_gravity="bottom"
- android:focusableInTouchMode="true"
- android:gravity="center"
- android:imeOptions="actionSearch|flagNoExtractUi"
- android:inputType="text|textNoSuggestions|textCapWords"
- android:maxLines="1"
- android:scrollHorizontally="true"
- android:singleLine="true"
- android:textColor="?android:attr/textColorSecondary"
- android:hint="@string/all_apps_search_bar_hint"
- android:textColorHint="@drawable/all_apps_search_hint"
- android:textSize="16sp" />
- </FrameLayout>
+ <include
+ layout="@layout/all_apps_search_container"
+ android:id="@+id/search_container" />
</com.android.launcher3.allapps.AllAppsRecyclerViewContainerView>
<View
diff --git a/res/layout/all_apps_search_container.xml b/res/layout/all_apps_search_container.xml
new file mode 100644
index 0000000..6addee1
--- /dev/null
+++ b/res/layout/all_apps_search_container.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<com.android.launcher3.allapps.search.AppsSearchContainerLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/all_apps_search_bar_height"
+ android:layout_gravity="center|top"
+ android:gravity="center|bottom"
+ android:id="@+id/search_container"
+ android:saveEnabled="false">
+
+ <com.android.launcher3.ExtendedEditText
+ android:id="@+id/search_box_input"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/all_apps_search_bar_field_height"
+ android:background="@android:color/transparent"
+ android:layout_gravity="bottom"
+ android:focusableInTouchMode="true"
+ android:gravity="center"
+ android:imeOptions="actionSearch|flagNoExtractUi"
+ android:inputType="text|textNoSuggestions|textCapWords"
+ android:maxLines="1"
+ android:scrollHorizontally="true"
+ android:singleLine="true"
+ android:textColor="?android:attr/textColorSecondary"
+ android:hint="@string/all_apps_search_bar_hint"
+ android:textColorHint="@drawable/all_apps_search_hint"
+ android:textSize="16sp" />
+</com.android.launcher3.allapps.search.AppsSearchContainerLayout>
\ No newline at end of file
diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml
index 2f11c28..476901d 100644
--- a/res/layout/widgets_view.xml
+++ b/res/layout/widgets_view.xml
@@ -51,7 +51,6 @@
<!-- Fast scroller popup -->
<TextView
style="@style/FastScrollerPopup"
- android:layout_below="@+id/search_container"
android:id="@+id/fast_scroller_popup"
android:layout_gravity="top|end"
android:layout_marginEnd="@dimen/container_fastscroll_popup_margin" />
diff --git a/res/values/config.xml b/res/values/config.xml
index 751954f..71984d6 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -13,6 +13,10 @@
easily override the app name without providing all translations -->
<string name="derived_app_name" translatable="false">@string/app_name</string>
+ <!-- String representing the intent for search on the apps market. To specify a query, add
+ q=<query> to the data to the intent -->
+ <string name="market_search_intent" translatable="false">market://search?c=apps</string>
+
<!-- Values for icon shape overrides. These should correspond to entries defined
in icon_shape_override_paths_names -->
<string-array name="icon_shape_override_paths_values">
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index 6fdf454..c056336 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -82,10 +82,6 @@
}
}
- public void reset() {
- mScrollbar.reattachThumbToScroll();
- }
-
@Override
protected void onFinishInflate() {
super.onFinishInflate();
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index d0c01f4..c96c2a7 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -85,7 +85,6 @@
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.allapps.DefaultAppSearchController;
import com.android.launcher3.anim.AnimationLayerSet;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
@@ -560,47 +559,6 @@
public boolean setLauncherCallbacks(LauncherCallbacks callbacks) {
mLauncherCallbacks = callbacks;
- mLauncherCallbacks.setLauncherSearchCallback(new Launcher.LauncherSearchCallbacks() {
- private boolean mWorkspaceImportanceStored = false;
- private boolean mHotseatImportanceStored = false;
- private int mWorkspaceImportanceForAccessibility =
- View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
- private int mHotseatImportanceForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
-
- @Override
- public void onSearchOverlayOpened() {
- if (mWorkspaceImportanceStored || mHotseatImportanceStored) {
- return;
- }
- // The underlying workspace and hotseat are temporarily suppressed by the search
- // overlay. So they shouldn't be accessible.
- if (mWorkspace != null) {
- mWorkspaceImportanceForAccessibility =
- mWorkspace.getImportantForAccessibility();
- mWorkspace.setImportantForAccessibility(
- View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
- mWorkspaceImportanceStored = true;
- }
- if (mHotseat != null) {
- mHotseatImportanceForAccessibility = mHotseat.getImportantForAccessibility();
- mHotseat.setImportantForAccessibility(
- View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
- mHotseatImportanceStored = true;
- }
- }
-
- @Override
- public void onSearchOverlayClosed() {
- if (mWorkspaceImportanceStored && mWorkspace != null) {
- mWorkspace.setImportantForAccessibility(mWorkspaceImportanceForAccessibility);
- }
- if (mHotseatImportanceStored && mHotseat != null) {
- mHotseat.setImportantForAccessibility(mHotseatImportanceForAccessibility);
- }
- mWorkspaceImportanceStored = false;
- mHotseatImportanceStored = false;
- }
- });
return true;
}
@@ -1140,18 +1098,6 @@
public void setOverlayCallbacks(LauncherOverlayCallbacks callbacks);
}
- public interface LauncherSearchCallbacks {
- /**
- * Called when the search overlay is shown.
- */
- public void onSearchOverlayOpened();
-
- /**
- * Called when the search overlay is dismissed.
- */
- public void onSearchOverlayClosed();
- }
-
public interface LauncherOverlayCallbacks {
public void onScrollChanged(float progress);
}
@@ -1344,11 +1290,6 @@
// Setup Apps and Widgets
mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view);
mWidgetsView = (WidgetsContainerView) findViewById(R.id.widgets_view);
- if (mLauncherCallbacks != null && mLauncherCallbacks.getAllAppsSearchBarController() != null) {
- mAppsView.setSearchBarController(mLauncherCallbacks.getAllAppsSearchBarController());
- } else {
- mAppsView.setSearchBarController(new DefaultAppSearchController());
- }
// Setup the drag controller (drop targets have to be added in reverse order in priority)
mDragController.setMoveTarget(mWorkspace);
@@ -1777,7 +1718,7 @@
// Reset the apps view
if (!alreadyOnHome && mAppsView != null) {
- mAppsView.scrollToTop();
+ mAppsView.reset();
}
// Reset the widgets view
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 32f179f..ea4aeb9 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -21,7 +21,6 @@
import android.view.Menu;
import android.view.View;
-import com.android.launcher3.allapps.AllAppsSearchBarController;
import com.android.launcher3.util.ComponentKey;
import java.io.FileDescriptor;
@@ -92,20 +91,11 @@
*/
boolean shouldMoveToDefaultScreenOnHomeIntent();
boolean hasSettings();
- AllAppsSearchBarController getAllAppsSearchBarController();
List<ComponentKey> getPredictedApps();
int SEARCH_BAR_HEIGHT_NORMAL = 0, SEARCH_BAR_HEIGHT_TALL = 1;
/** Must return one of {@link #SEARCH_BAR_HEIGHT_NORMAL} or {@link #SEARCH_BAR_HEIGHT_TALL} */
int getSearchBarHeight();
- /**
- * Sets the callbacks to allow reacting the actions of search overlays of the launcher.
- *
- * @param callbacks A set of callbacks to the Launcher, is actually a LauncherSearchCallback,
- * but for implementation purposes is passed around as an object.
- */
- void setLauncherSearchCallback(Object callbacks);
-
boolean shouldShowDiscoveryBounce();
void onExtractedColorsChanged();
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 7be8e8f..0ea61f4 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -20,15 +20,9 @@
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.InsetDrawable;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.text.Selection;
-import android.text.Spannable;
-import android.text.SpannableString;
import android.text.SpannableStringBuilder;
-import android.text.TextUtils;
-import android.text.method.TextKeyListener;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -42,7 +36,6 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
-import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Insettable;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
@@ -50,18 +43,14 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.discovery.AppDiscoveryItem;
-import com.android.launcher3.discovery.AppDiscoveryUpdateState;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.folder.Folder;
-import com.android.launcher3.graphics.TintedDrawableSpan;
import com.android.launcher3.keyboard.FocusedItemDecorator;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageUserKey;
-import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -69,7 +58,7 @@
* The all apps view container.
*/
public class AllAppsContainerView extends BaseContainerView implements DragSource,
- View.OnLongClickListener, AllAppsSearchBarController.Callbacks, Insettable {
+ View.OnLongClickListener, Insettable {
private final Launcher mLauncher;
private final AlphabeticalAppsList mApps;
@@ -77,12 +66,8 @@
private final RecyclerView.LayoutManager mLayoutManager;
private AllAppsRecyclerView mAppsRecyclerView;
- private AllAppsSearchBarController mSearchBarController;
-
+ private SearchUiManager mSearchUiManager;
private View mSearchContainer;
- private int mSearchContainerMinHeight;
- private ExtendedEditText mSearchInput;
- private HeaderElevationController mElevationController;
private SpannableStringBuilder mSearchQueryBuilder = null;
@@ -106,8 +91,6 @@
mApps.setAdapter(mAdapter);
mLayoutManager = mAdapter.getLayoutManager();
mSearchQueryBuilder = new SpannableStringBuilder();
- mSearchContainerMinHeight
- = getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_height);
Selection.setSelection(mSearchQueryBuilder, 0);
}
@@ -149,7 +132,7 @@
*/
public void addApps(List<AppInfo> apps) {
mApps.addApps(apps);
- mSearchBarController.refreshSearchResult();
+ mSearchUiManager.refreshSearchResult();
}
/**
@@ -157,7 +140,7 @@
*/
public void updateApps(List<AppInfo> apps) {
mApps.updateApps(apps);
- mSearchBarController.refreshSearchResult();
+ mSearchUiManager.refreshSearchResult();
}
public void updatePromiseAppProgress(PromiseAppInfo app) {
@@ -176,34 +159,7 @@
*/
public void removeApps(List<AppInfo> apps) {
mApps.removeApps(apps);
- mSearchBarController.refreshSearchResult();
- }
-
- public void setSearchBarVisible(boolean visible) {
- if (visible) {
- mSearchBarController.setVisibility(View.VISIBLE);
- } else {
- mSearchBarController.setVisibility(View.INVISIBLE);
- }
- }
-
- /**
- * Sets the search bar that shows above the a-z list.
- */
- public void setSearchBarController(AllAppsSearchBarController searchController) {
- if (mSearchBarController != null) {
- throw new RuntimeException("Expected search bar controller to only be set once");
- }
- mSearchBarController = searchController;
- mSearchBarController.initialize(mApps, mSearchInput, mLauncher, this);
- mAdapter.setSearchController(mSearchBarController);
- }
-
- /**
- * Scrolls this list view to the top.
- */
- public void scrollToTop() {
- mAppsRecyclerView.scrollToTop();
+ mSearchUiManager.refreshSearchResult();
}
/**
@@ -238,9 +194,7 @@
* Focuses the search field and begins an app search.
*/
public void startAppsSearch() {
- if (mSearchBarController != null) {
- mSearchBarController.focusSearchField();
- }
+ mSearchUiManager.startAppsSearch();
}
/**
@@ -248,9 +202,8 @@
*/
public void reset() {
// Reset the search bar and base recycler view after transitioning home
- scrollToTop();
- mSearchBarController.reset();
- mAppsRecyclerView.reset();
+ mAppsRecyclerView.scrollToTop();
+ mSearchUiManager.reset();
}
@Override
@@ -268,28 +221,17 @@
}
});
- mSearchContainer = findViewById(R.id.search_container);
- mSearchInput = (ExtendedEditText) findViewById(R.id.search_box_input);
-
- // Update the hint to contain the icon.
- // Prefix the original hint with two spaces. The first space gets replaced by the icon
- // using span. The second space is used for a singe space character between the hint
- // and the icon.
- SpannableString spanned = new SpannableString(" " + mSearchInput.getHint());
- spanned.setSpan(new TintedDrawableSpan(getContext(), R.drawable.ic_allapps_search),
- 0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
- mSearchInput.setHint(spanned);
-
- mElevationController = new HeaderElevationController(mSearchContainer);
-
// Load the all apps recycler view
mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);
mAppsRecyclerView.setApps(mApps);
mAppsRecyclerView.setLayoutManager(mLayoutManager);
mAppsRecyclerView.setAdapter(mAdapter);
mAppsRecyclerView.setHasFixedSize(true);
- mAppsRecyclerView.addOnScrollListener(mElevationController);
- mAppsRecyclerView.setElevationController(mElevationController);
+
+ mSearchContainer = findViewById(R.id.search_container);
+ mSearchUiManager = (SearchUiManager) mSearchContainer;
+ mSearchUiManager.initialize(mApps, mAppsRecyclerView);
+
FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(mAppsRecyclerView);
mAppsRecyclerView.addItemDecoration(focusedItemDecorator);
@@ -309,12 +251,11 @@
}
@Override
- public void onBoundsChanged(Rect newBounds) { }
-
- @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
DeviceProfile grid = mLauncher.getDeviceProfile();
+ // Update the number of items in the grid before we measure the view
grid.updateAppsViewNumCols();
+
if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
if (mNumAppsPerRow != grid.inv.numColumns ||
mNumPredictedAppsPerRow != grid.inv.numColumns) {
@@ -325,22 +266,11 @@
mAdapter.setNumAppsPerRow(mNumAppsPerRow);
mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow);
}
- if (!grid.isVerticalBarLayout()) {
- MarginLayoutParams searchContainerLp =
- (MarginLayoutParams) mSearchContainer.getLayoutParams();
-
- searchContainerLp.height = mLauncher.getDragLayer().getInsets().top
- + mSearchContainerMinHeight;
- mSearchContainer.setLayoutParams(searchContainerLp);
- }
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}
// --- remove START when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. ---
-
- // Update the number of items in the grid before we measure the view
- grid.updateAppsViewNumCols();
if (mNumAppsPerRow != grid.allAppsNumCols ||
mNumPredictedAppsPerRow != grid.allAppsNumPredictiveCols) {
mNumAppsPerRow = grid.allAppsNumCols;
@@ -357,22 +287,7 @@
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
- // Determine if the key event was actual text, if so, focus the search bar and then dispatch
- // the key normally so that it can process this key event
- if (!mSearchBarController.isSearchFieldFocused() &&
- event.getAction() == KeyEvent.ACTION_DOWN) {
- final int unicodeChar = event.getUnicodeChar();
- final boolean isKeyNotWhitespace = unicodeChar > 0 &&
- !Character.isWhitespace(unicodeChar) && !Character.isSpaceChar(unicodeChar);
- if (isKeyNotWhitespace) {
- boolean gotKey = TextKeyListener.getInstance().onKeyDown(this, mSearchQueryBuilder,
- event.getKeyCode(), event);
- if (gotKey && mSearchQueryBuilder.length() > 0) {
- mSearchBarController.focusSearchField();
- }
- }
- }
-
+ mSearchUiManager.preDispatchKeyEvent(event);
return super.dispatchKeyEvent(event);
}
@@ -440,42 +355,12 @@
}
@Override
- public void onSearchResult(String query, ArrayList<ComponentKey> apps) {
- if (apps != null) {
- mApps.setOrderedFilter(apps);
- mAppsRecyclerView.onSearchResultsChanged();
- mAdapter.setLastSearchQuery(query);
- }
- }
-
- @Override
- public void onAppDiscoverySearchUpdate(@Nullable AppDiscoveryItem app,
- @NonNull AppDiscoveryUpdateState state) {
- if (!mLauncher.isDestroyed()) {
- mApps.onAppDiscoverySearchUpdate(app, state);
- mAppsRecyclerView.onSearchResultsChanged();
- }
- }
-
- @Override
- public void clearSearchResult() {
- if (mApps.setOrderedFilter(null)) {
- mAppsRecyclerView.onSearchResultsChanged();
- }
-
- // Clear the search query
- mSearchQueryBuilder.clear();
- mSearchQueryBuilder.clearSpans();
- Selection.setSelection(mSearchQueryBuilder, 0);
- }
-
- @Override
public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
targetParent.containerType = mAppsRecyclerView.getContainerType(v);
}
public boolean shouldRestoreImeState() {
- return !TextUtils.isEmpty(mSearchInput.getText());
+ return mSearchUiManager.shouldRestoreImeState();
}
@Override
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 938e84e..e126102 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -40,6 +40,7 @@
import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
import com.android.launcher3.discovery.AppDiscoveryAppInfo;
import com.android.launcher3.discovery.AppDiscoveryItemView;
+import com.android.launcher3.util.PackageManagerHelper;
import java.util.List;
@@ -199,7 +200,6 @@
private int mAppsPerRow;
private BindViewCallback mBindViewCallback;
- private AllAppsSearchBarController mSearchController;
private OnFocusChangeListener mIconFocusListener;
// The text to show when there are no search results and no market search handler.
@@ -241,10 +241,6 @@
mGridLayoutMgr.setSpanCount(appsPerRow);
}
- public void setSearchController(AllAppsSearchBarController searchController) {
- mSearchController = searchController;
- }
-
public void setIconFocusListener(OnFocusChangeListener focusListener) {
mIconFocusListener = focusListener;
}
@@ -256,7 +252,7 @@
public void setLastSearchQuery(String query) {
Resources res = mLauncher.getResources();
mEmptySearchMessage = res.getString(R.string.all_apps_no_search_results, query);
- mMarketSearchIntent = mSearchController.createMarketSearchIntent(query);
+ mMarketSearchIntent = PackageManagerHelper.getMarketSearchIntent(mLauncher, query);
}
/**
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 64e2fcb..f3089d2 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -53,8 +53,6 @@
private AllAppsBackgroundDrawable mEmptySearchBackground;
private int mEmptySearchBackgroundTopOffset;
- private HeaderElevationController mElevationController;
-
public AllAppsRecyclerView(Context context) {
this(context, null);
}
@@ -85,10 +83,6 @@
mFastScrollHelper = new AllAppsFastScrollHelper(this, apps);
}
- public void setElevationController(HeaderElevationController elevationController) {
- mElevationController = elevationController;
- }
-
/**
* Sets the number of apps per row in this recycler view.
*/
@@ -152,13 +146,8 @@
*/
public void scrollToTop() {
// Ensure we reattach the scrollbar if it was previously detached while fast-scrolling
- if (mScrollbar.isThumbDetached()) {
- mScrollbar.reattachThumbToScroll();
- }
+ mScrollbar.reattachThumbToScroll();
scrollToPosition(0);
- if (mElevationController != null) {
- mElevationController.reset();
- }
}
@Override
diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchController.java b/src/com/android/launcher3/allapps/DefaultAppSearchController.java
deleted file mode 100644
index 57747e3..0000000
--- a/src/com/android/launcher3/allapps/DefaultAppSearchController.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.allapps;
-
-/**
- * The default search controller.
- */
-public class DefaultAppSearchController extends AllAppsSearchBarController {
-
- public DefaultAppSearchAlgorithm onInitializeSearch() {
- return new DefaultAppSearchAlgorithm(mApps.getApps());
- }
-}
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
new file mode 100644
index 0000000..15455bc
--- /dev/null
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.allapps;
+
+import android.view.KeyEvent;
+
+/**
+ * Interface for controlling the Apps search UI.
+ */
+public interface SearchUiManager {
+
+ /**
+ * Initializes the search manager.
+ */
+ void initialize(AlphabeticalAppsList appsList, AllAppsRecyclerView recyclerView);
+
+ /**
+ * Notifies the search manager that the apps-list has changed and the search UI should be
+ * updated accordingly.
+ */
+ void refreshSearchResult();
+
+ /**
+ * Notifies the search manager to close any active search session.
+ */
+ void reset();
+
+ /**
+ * Called before dispatching a key event, in case the search manager wants to initialize
+ * some UI beforehand.
+ */
+ void preDispatchKeyEvent(KeyEvent keyEvent);
+
+ /**
+ * Returns true if the IME should be brought back.
+ * TODO: Remove when removing support for opening all-apps in search mode.
+ */
+ boolean shouldRestoreImeState();
+
+ /**
+ * Starts the search UI
+ * TODO: Remove when removing support for opening all-apps in search mode.
+ */
+ void startAppsSearch();
+}
diff --git a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
similarity index 85%
rename from src/com/android/launcher3/allapps/AllAppsSearchBarController.java
rename to src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index c7ba3ab..547d9e1 100644
--- a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -13,12 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.launcher3.allapps;
+package com.android.launcher3.allapps.search;
import android.content.Context;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.Editable;
@@ -34,16 +31,18 @@
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.AlphabeticalAppsList;
import com.android.launcher3.discovery.AppDiscoveryItem;
import com.android.launcher3.discovery.AppDiscoveryUpdateState;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.PackageManagerHelper;
import java.util.ArrayList;
/**
* An interface to a search box that AllApps can command.
*/
-public abstract class AllAppsSearchBarController
+public class AllAppsSearchBarController
implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener {
protected Launcher mLauncher;
@@ -88,9 +87,11 @@
}
/**
- * To be implemented by subclasses. This method will get called when the controller is set.
+ * This method will get called when the controller is set.
*/
- protected abstract DefaultAppSearchAlgorithm onInitializeSearch();
+ public DefaultAppSearchAlgorithm onInitializeSearch() {
+ return new DefaultAppSearchAlgorithm(mApps.getApps());
+ }
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@@ -114,7 +115,7 @@
}
}
- protected void refreshSearchResult() {
+ public void refreshSearchResult() {
if (TextUtils.isEmpty(mQuery)) {
return;
}
@@ -135,7 +136,8 @@
if (query.isEmpty()) {
return false;
}
- return mLauncher.startActivitySafely(v, createMarketSearchIntent(query), null);
+ return mLauncher.startActivitySafely(v,
+ PackageManagerHelper.getMarketSearchIntent(mLauncher, query), null);
}
@Override
@@ -186,29 +188,11 @@
}
/**
- * Creates a new market search intent.
- */
- public Intent createMarketSearchIntent(String query) {
- Uri marketSearchUri = Uri.parse("market://search")
- .buildUpon()
- .appendQueryParameter("c", "apps")
- .appendQueryParameter("q", query)
- .build();
- return new Intent(Intent.ACTION_VIEW).setData(marketSearchUri);
- }
-
- /**
* Callback for getting search results.
*/
public interface Callbacks {
/**
- * Called when the bounds of the search bar has changed.
- */
- @Deprecated
- void onBoundsChanged(Rect newBounds);
-
- /**
* Called when the search is complete.
*
* @param apps sorted list of matching components or null if in case of failure.
@@ -220,7 +204,6 @@
*/
void clearSearchResult();
-
/**
* Called when the app discovery is providing an update of search, which can either be
* START for starting a new discovery,
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
new file mode 100644
index 0000000..116ec88
--- /dev/null
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.allapps.search;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.text.Selection;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+import android.text.method.TextKeyListener;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.widget.FrameLayout;
+
+import com.android.launcher3.ExtendedEditText;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.allapps.AllAppsGridAdapter;
+import com.android.launcher3.allapps.AllAppsRecyclerView;
+import com.android.launcher3.allapps.AlphabeticalAppsList;
+import com.android.launcher3.allapps.SearchUiManager;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.discovery.AppDiscoveryItem;
+import com.android.launcher3.discovery.AppDiscoveryUpdateState;
+import com.android.launcher3.graphics.TintedDrawableSpan;
+import com.android.launcher3.util.ComponentKey;
+
+import java.util.ArrayList;
+
+/**
+ * Layout to contain the All-apps search UI.
+ */
+public class AppsSearchContainerLayout extends FrameLayout
+ implements SearchUiManager, AllAppsSearchBarController.Callbacks {
+
+ private final Launcher mLauncher;
+ private final int mMinHeight;
+ private final AllAppsSearchBarController mSearchBarController;
+ private final SpannableStringBuilder mSearchQueryBuilder;
+ private final HeaderElevationController mElevationController;
+
+ private ExtendedEditText mSearchInput;
+ private AlphabeticalAppsList mApps;
+ private AllAppsRecyclerView mAppsRecyclerView;
+ private AllAppsGridAdapter mAdapter;
+
+ public AppsSearchContainerLayout(Context context) {
+ this(context, null);
+ }
+
+ public AppsSearchContainerLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public AppsSearchContainerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ mLauncher = Launcher.getLauncher(context);
+ mMinHeight = getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_height);
+ mSearchBarController = new AllAppsSearchBarController();
+ mElevationController = new HeaderElevationController(this);
+
+ mSearchQueryBuilder = new SpannableStringBuilder();
+ Selection.setSelection(mSearchQueryBuilder, 0);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mSearchInput = findViewById(R.id.search_box_input);
+
+ // Update the hint to contain the icon.
+ // Prefix the original hint with two spaces. The first space gets replaced by the icon
+ // using span. The second space is used for a singe space character between the hint
+ // and the icon.
+ SpannableString spanned = new SpannableString(" " + mSearchInput.getHint());
+ spanned.setSpan(new TintedDrawableSpan(getContext(), R.drawable.ic_allapps_search),
+ 0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
+ mSearchInput.setHint(spanned);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP &&
+ !mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+ getLayoutParams().height = mLauncher.getDragLayer().getInsets().top + mMinHeight;
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+
+ @Override
+ public void initialize(
+ AlphabeticalAppsList appsList, AllAppsRecyclerView recyclerView) {
+ mApps = appsList;
+ mAppsRecyclerView = recyclerView;
+ mAppsRecyclerView.addOnScrollListener(mElevationController);
+ mAdapter = (AllAppsGridAdapter) mAppsRecyclerView.getAdapter();
+
+ mSearchBarController.initialize(appsList, mSearchInput, mLauncher, this);
+ }
+
+ @Override
+ public void refreshSearchResult() {
+ mSearchBarController.refreshSearchResult();
+ }
+
+ @Override
+ public void reset() {
+ mElevationController.reset();
+ mSearchBarController.reset();
+ }
+
+ @Override
+ public void preDispatchKeyEvent(KeyEvent event) {
+ // Determine if the key event was actual text, if so, focus the search bar and then dispatch
+ // the key normally so that it can process this key event
+ if (!mSearchBarController.isSearchFieldFocused() &&
+ event.getAction() == KeyEvent.ACTION_DOWN) {
+ final int unicodeChar = event.getUnicodeChar();
+ final boolean isKeyNotWhitespace = unicodeChar > 0 &&
+ !Character.isWhitespace(unicodeChar) && !Character.isSpaceChar(unicodeChar);
+ if (isKeyNotWhitespace) {
+ boolean gotKey = TextKeyListener.getInstance().onKeyDown(this, mSearchQueryBuilder,
+ event.getKeyCode(), event);
+ if (gotKey && mSearchQueryBuilder.length() > 0) {
+ mSearchBarController.focusSearchField();
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean shouldRestoreImeState() {
+ return !TextUtils.isEmpty(mSearchInput.getText());
+ }
+
+ @Override
+ public void startAppsSearch() {
+ if (mApps != null) {
+ mSearchBarController.focusSearchField();
+ }
+ }
+
+ @Override
+ public void onSearchResult(String query, ArrayList<ComponentKey> apps) {
+ if (apps != null) {
+ mApps.setOrderedFilter(apps);
+ notifyResultChanged();
+ mAdapter.setLastSearchQuery(query);
+ }
+ }
+
+ @Override
+ public void clearSearchResult() {
+ if (mApps.setOrderedFilter(null)) {
+ notifyResultChanged();
+ }
+
+ // Clear the search query
+ mSearchQueryBuilder.clear();
+ mSearchQueryBuilder.clearSpans();
+ Selection.setSelection(mSearchQueryBuilder, 0);
+ }
+
+ @Override
+ public void onAppDiscoverySearchUpdate(
+ @Nullable AppDiscoveryItem app, @NonNull AppDiscoveryUpdateState state) {
+ if (!mLauncher.isDestroyed()) {
+ mApps.onAppDiscoverySearchUpdate(app, state);
+ notifyResultChanged();
+ }
+ }
+
+ private void notifyResultChanged() {
+ mElevationController.reset();
+ mAppsRecyclerView.onSearchResultsChanged();
+ }
+}
diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
similarity index 98%
rename from src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java
rename to src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
index 06cf9aa..457b454 100644
--- a/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.launcher3.allapps;
+package com.android.launcher3.allapps.search;
import android.os.Handler;
diff --git a/src/com/android/launcher3/allapps/HeaderElevationController.java b/src/com/android/launcher3/allapps/search/HeaderElevationController.java
similarity index 97%
rename from src/com/android/launcher3/allapps/HeaderElevationController.java
rename to src/com/android/launcher3/allapps/search/HeaderElevationController.java
index b167fed..ab4e88f 100644
--- a/src/com/android/launcher3/allapps/HeaderElevationController.java
+++ b/src/com/android/launcher3/allapps/search/HeaderElevationController.java
@@ -1,4 +1,4 @@
-package com.android.launcher3.allapps;
+package com.android.launcher3.allapps.search;
import android.content.res.Resources;
import android.graphics.Outline;
diff --git a/src/com/android/launcher3/dynamicui/ColorExtractionService.java b/src/com/android/launcher3/dynamicui/ColorExtractionService.java
index f6b02aa..349b4ff 100644
--- a/src/com/android/launcher3/dynamicui/ColorExtractionService.java
+++ b/src/com/android/launcher3/dynamicui/ColorExtractionService.java
@@ -66,7 +66,7 @@
if (FeatureFlags.QSB_IN_HOTSEAT || FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
extractedColors.updateWallpaperThemePalette(null);
if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
- extractedColors.updateAllAppsGradientPalette(null);
+ extractedColors.updateAllAppsGradientPalette(this);
}
}
} else {
@@ -79,10 +79,9 @@
}
if (FeatureFlags.QSB_IN_HOTSEAT || FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
- Palette wallpaperPalette = getWallpaperPalette();
- extractedColors.updateWallpaperThemePalette(wallpaperPalette);
+ extractedColors.updateWallpaperThemePalette(getWallpaperPalette());
if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
- extractedColors.updateAllAppsGradientPalette(wallpaperPalette);
+ extractedColors.updateAllAppsGradientPalette(this);
}
}
}
diff --git a/src/com/android/launcher3/dynamicui/ExtractedColors.java b/src/com/android/launcher3/dynamicui/ExtractedColors.java
index 3c4aba1..e72ab3d 100644
--- a/src/com/android/launcher3/dynamicui/ExtractedColors.java
+++ b/src/com/android/launcher3/dynamicui/ExtractedColors.java
@@ -16,6 +16,7 @@
package com.android.launcher3.dynamicui;
+import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Color;
import android.support.annotation.Nullable;
@@ -25,6 +26,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dynamicui.colorextraction.ColorExtractor;
import java.util.Arrays;
@@ -161,14 +163,17 @@
? defaultColor : wallpaperPalette.getVibrantColor(defaultColor));
}
- public void updateAllAppsGradientPalette(@Nullable Palette wallpaperPalette) {
- // TODO b/37089857 will be modified to take the system extracted colors into account
- int idx;
- idx = ALLAPPS_GRADIENT_MAIN_INDEX;
- setColorAtIndex(idx, wallpaperPalette == null
- ? DEFAULT_VALUES[idx] : wallpaperPalette.getDarkVibrantColor(DEFAULT_VALUES[idx]));
- idx = ALLAPPS_GRADIENT_SECONDARY_INDEX;
- setColorAtIndex(idx, wallpaperPalette == null
- ? DEFAULT_VALUES[idx] : wallpaperPalette.getVibrantColor(DEFAULT_VALUES[idx]));
+ public void updateAllAppsGradientPalette(Context context) {
+ // TODO use isAtLeastO when available
+ try {
+ WallpaperManager.class.getDeclaredMethod("getWallpaperColors", int.class);
+ ColorExtractor extractor = new ColorExtractor(context);
+ ColorExtractor.GradientColors colors = extractor.getColors(WallpaperManager.FLAG_SYSTEM);
+ setColorAtIndex(ALLAPPS_GRADIENT_MAIN_INDEX, colors.getMainColor());
+ setColorAtIndex(ALLAPPS_GRADIENT_SECONDARY_INDEX, colors.getSecondaryColor());
+ } catch (NoSuchMethodException e) {
+ setColorAtIndex(ALLAPPS_GRADIENT_MAIN_INDEX, Color.WHITE);
+ setColorAtIndex(ALLAPPS_GRADIENT_SECONDARY_INDEX, Color.WHITE);
+ }
}
}
diff --git a/src/com/android/launcher3/dynamicui/colorextraction/ColorExtractor.java b/src/com/android/launcher3/dynamicui/colorextraction/ColorExtractor.java
new file mode 100644
index 0000000..153b529
--- /dev/null
+++ b/src/com/android/launcher3/dynamicui/colorextraction/ColorExtractor.java
@@ -0,0 +1,136 @@
+package com.android.launcher3.dynamicui.colorextraction;
+
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.graphics.Color;
+import android.os.Parcelable;
+import android.util.Log;
+
+import com.android.launcher3.dynamicui.colorextraction.types.ExtractionType;
+import com.android.launcher3.dynamicui.colorextraction.types.Tonal;
+
+import java.lang.reflect.Method;
+
+
+/**
+ * Class to process wallpaper colors and generate a tonal palette based on them.
+ *
+ * TODO remove this class if available by platform
+ */
+public class ColorExtractor {
+ private static final String TAG = "ColorExtractor";
+ private static final int FALLBACK_COLOR = Color.WHITE;
+
+ private int mMainFallbackColor = FALLBACK_COLOR;
+ private int mSecondaryFallbackColor = FALLBACK_COLOR;
+ private final GradientColors mSystemColors;
+ private final GradientColors mLockColors;
+ private final Context mContext;
+ private final ExtractionType mExtractionType;
+
+ public ColorExtractor(Context context) {
+ mContext = context;
+ mSystemColors = new GradientColors();
+ mLockColors = new GradientColors();
+ mExtractionType = new Tonal();
+ WallpaperManager wallpaperManager = mContext.getSystemService(WallpaperManager.class);
+
+ if (wallpaperManager == null) {
+ Log.w(TAG, "Can't listen to color changes!");
+ } else {
+ Parcelable wallpaperColorsObj;
+ try {
+ Method method = WallpaperManager.class
+ .getDeclaredMethod("getWallpaperColors", int.class);
+
+ wallpaperColorsObj = (Parcelable) method.invoke(wallpaperManager,
+ WallpaperManager.FLAG_SYSTEM);
+ extractInto(new WallpaperColorsCompat(wallpaperColorsObj), mSystemColors);
+ wallpaperColorsObj = (Parcelable) method.invoke(wallpaperManager,
+ WallpaperManager.FLAG_LOCK);
+ extractInto(new WallpaperColorsCompat(wallpaperColorsObj), mLockColors);
+ } catch (Exception e) {
+ Log.e(TAG, "reflection failed", e);
+ }
+ }
+ }
+
+ public GradientColors getColors(int which) {
+ if (which == WallpaperManager.FLAG_LOCK) {
+ return mLockColors;
+ } else if (which == WallpaperManager.FLAG_SYSTEM) {
+ return mSystemColors;
+ } else {
+ throw new IllegalArgumentException("which should be either FLAG_SYSTEM or FLAG_LOCK");
+ }
+ }
+
+ private void extractInto(WallpaperColorsCompat inWallpaperColors, GradientColors outGradientColors) {
+ applyFallback(outGradientColors);
+ if (inWallpaperColors == null) {
+ return;
+ }
+ mExtractionType.extractInto(inWallpaperColors, outGradientColors);
+ }
+
+ private void applyFallback(GradientColors outGradientColors) {
+ outGradientColors.setMainColor(mMainFallbackColor);
+ outGradientColors.setSecondaryColor(mSecondaryFallbackColor);
+ }
+
+ public static class GradientColors {
+ private int mMainColor = FALLBACK_COLOR;
+ private int mSecondaryColor = FALLBACK_COLOR;
+ private boolean mSupportsDarkText;
+
+ public void setMainColor(int mainColor) {
+ mMainColor = mainColor;
+ }
+
+ public void setSecondaryColor(int secondaryColor) {
+ mSecondaryColor = secondaryColor;
+ }
+
+ public void setSupportsDarkText(boolean supportsDarkText) {
+ mSupportsDarkText = supportsDarkText;
+ }
+
+ public void set(GradientColors other) {
+ mMainColor = other.mMainColor;
+ mSecondaryColor = other.mSecondaryColor;
+ mSupportsDarkText = other.mSupportsDarkText;
+ }
+
+ public int getMainColor() {
+ return mMainColor;
+ }
+
+ public int getSecondaryColor() {
+ return mSecondaryColor;
+ }
+
+ public boolean supportsDarkText() {
+ return mSupportsDarkText;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || o.getClass() != getClass()) {
+ return false;
+ }
+ GradientColors other = (GradientColors) o;
+ return other.mMainColor == mMainColor &&
+ other.mSecondaryColor == mSecondaryColor &&
+ other.mSupportsDarkText == mSupportsDarkText;
+ }
+
+ @Override
+ public int hashCode() {
+ int code = mMainColor;
+ code = 31 * code + mSecondaryColor;
+ code = 31 * code + (mSupportsDarkText ? 0 : 1);
+ return code;
+ }
+ }
+}
+
diff --git a/src/com/android/launcher3/dynamicui/colorextraction/WallpaperColorsCompat.java b/src/com/android/launcher3/dynamicui/colorextraction/WallpaperColorsCompat.java
new file mode 100644
index 0000000..f80a675
--- /dev/null
+++ b/src/com/android/launcher3/dynamicui/colorextraction/WallpaperColorsCompat.java
@@ -0,0 +1,69 @@
+package com.android.launcher3.dynamicui.colorextraction;
+
+import android.graphics.Color;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Pair;
+
+import java.util.List;
+
+/**
+ * A wrapper around platform implementation of WallpaperColors until the
+ * updated SDK is available.
+ *
+ * TODO remove this class if available by platform
+ */
+public class WallpaperColorsCompat implements Parcelable {
+
+ private final Parcelable mObject;
+
+ public WallpaperColorsCompat(Parcelable object) {
+ mObject = object;
+ }
+
+ private Object invokeMethod(String methodName) {
+ try {
+ return mObject.getClass().getDeclaredMethod(methodName).invoke(mObject);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int i) {
+ parcel.writeParcelable(mObject, i);
+ }
+
+ public static final Parcelable.Creator<WallpaperColorsCompat> CREATOR =
+ new Parcelable.Creator<WallpaperColorsCompat>() {
+ public WallpaperColorsCompat createFromParcel(Parcel source) {
+ Parcelable object = source.readParcelable(null);
+ return new WallpaperColorsCompat(object);
+ }
+
+ public WallpaperColorsCompat[] newArray(int size) {
+ return new WallpaperColorsCompat[size];
+ }
+ };
+
+ public List<Pair<Color, Integer>> getColors() {
+ try {
+ return (List<Pair<Color, Integer>>) invokeMethod("getColors");
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ public boolean supportsDarkText() {
+ try {
+ return (Boolean) invokeMethod("supportsDarkText");
+ } catch (Exception e) {
+ return false;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/dynamicui/colorextraction/types/ExtractionType.java b/src/com/android/launcher3/dynamicui/colorextraction/types/ExtractionType.java
new file mode 100644
index 0000000..166c7c6
--- /dev/null
+++ b/src/com/android/launcher3/dynamicui/colorextraction/types/ExtractionType.java
@@ -0,0 +1,23 @@
+package com.android.launcher3.dynamicui.colorextraction.types;
+
+import com.android.launcher3.dynamicui.colorextraction.ColorExtractor;
+import com.android.launcher3.dynamicui.colorextraction.WallpaperColorsCompat;
+
+
+/**
+ * Interface to allow various color extraction implementations.
+ *
+ * TODO remove this class if available by platform
+ */
+public interface ExtractionType {
+
+ /**
+ * Executes color extraction by reading WallpaperColors and setting
+ * main and secondary colors on GradientColors.
+ *
+ * @param inWallpaperColors where to read from
+ * @param outGradientColors object that should receive the colors
+ */
+ void extractInto(WallpaperColorsCompat inWallpaperColors,
+ ColorExtractor.GradientColors outGradientColors);
+}
diff --git a/src/com/android/launcher3/dynamicui/colorextraction/types/Tonal.java b/src/com/android/launcher3/dynamicui/colorextraction/types/Tonal.java
new file mode 100644
index 0000000..1e165a3
--- /dev/null
+++ b/src/com/android/launcher3/dynamicui/colorextraction/types/Tonal.java
@@ -0,0 +1,299 @@
+package com.android.launcher3.dynamicui.colorextraction.types;
+
+import android.graphics.Color;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.graphics.ColorUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.launcher3.dynamicui.colorextraction.ColorExtractor;
+import com.android.launcher3.dynamicui.colorextraction.WallpaperColorsCompat;
+
+import java.util.Comparator;
+
+
+/**
+ * Implementation of tonal color extraction
+ *
+ * TODO remove this class if available by platform
+ */
+public class Tonal implements ExtractionType {
+ private static final String TAG = "Tonal";
+
+ // Used for tonal palette fitting
+ private static final float FIT_WEIGHT_H = 1.0f;
+ private static final float FIT_WEIGHT_S = 1.0f;
+ private static final float FIT_WEIGHT_L = 10.0f;
+
+ private static final float MIN_COLOR_OCCURRENCE = 0.1f;
+ private static final float MIN_LUMINOSITY = 0.5f;
+
+ public void extractInto(WallpaperColorsCompat wallpaperColors,
+ ColorExtractor.GradientColors gradientColors) {
+ if (wallpaperColors.getColors().size() == 0) {
+ return;
+ }
+ // Tonal is not really a sort, it takes a color from the extracted
+ // palette and finds a best fit amongst a collection of pre-defined
+ // palettes. The best fit is tweaked to be closer to the source color
+ // and replaces the original palette
+
+ // First find the most representative color in the image
+ populationSort(wallpaperColors);
+ // Calculate total
+ int total = 0;
+ for (Pair<Color, Integer> weightedColor : wallpaperColors.getColors()) {
+ total += weightedColor.second;
+ }
+
+ // Get bright colors that occur often enough in this image
+ Pair<Color, Integer> bestColor = null;
+ float[] hsl = new float[3];
+ for (Pair<Color, Integer> weightedColor : wallpaperColors.getColors()) {
+ float colorOccurrence = weightedColor.second / (float) total;
+ if (colorOccurrence < MIN_COLOR_OCCURRENCE) {
+ break;
+ }
+
+ int colorValue = weightedColor.first.toArgb();
+ ColorUtils.RGBToHSL(Color.red(colorValue), Color.green(colorValue),
+ Color.blue(colorValue), hsl);
+ if (hsl[2] > MIN_LUMINOSITY) {
+ bestColor = weightedColor;
+ }
+ }
+
+ // Fallback to first color
+ if (bestColor == null) {
+ bestColor = wallpaperColors.getColors().get(0);
+ }
+
+ int colorValue = bestColor.first.toArgb();
+ ColorUtils.RGBToHSL(Color.red(colorValue), Color.green(colorValue), Color.blue(colorValue),
+ hsl);
+ hsl[0] /= 360.0f; // normalize
+
+ // TODO, we're finding a tonal palette for a hue, not all components
+ TonalPalette palette = findTonalPalette(hsl[0]);
+
+ // Fall back to population sort if we couldn't find a tonal palette
+ if (palette == null) {
+ Log.w(TAG, "Could not find a tonal palette!");
+ return;
+ }
+
+ int fitIndex = bestFit(palette, hsl[0], hsl[1], hsl[2]);
+ if (fitIndex == -1) {
+ Log.w(TAG, "Could not find best fit!");
+ return;
+ }
+ float[] h = fit(palette.h, hsl[0], fitIndex,
+ Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY);
+ float[] s = fit(palette.s, hsl[1], fitIndex, 0.0f, 1.0f);
+ float[] l = fit(palette.l, hsl[2], fitIndex, 0.0f, 1.0f);
+
+
+ hsl[0] = fract(h[0]) * 360.0f;
+ hsl[1] = s[0];
+ hsl[2] = l[0];
+ gradientColors.setMainColor(ColorUtils.HSLToColor(hsl));
+
+ hsl[0] = fract(h[1]) * 360.0f;
+ hsl[1] = s[1];
+ hsl[2] = l[1];
+ gradientColors.setSecondaryColor(ColorUtils.HSLToColor(hsl));
+ }
+
+ private static void populationSort(@NonNull WallpaperColorsCompat wallpaperColors) {
+ wallpaperColors.getColors().sort(new Comparator<Pair<Color, Integer>>() {
+ @Override
+ public int compare(Pair<Color, Integer> a, Pair<Color, Integer> b) {
+ return b.second - a.second;
+ }
+ });
+ }
+
+ /**
+ * Offsets all colors by a delta, clamping values that go beyond what's
+ * supported on the color space.
+ * @param data what you want to fit
+ * @param v how big should be the offset
+ * @param index which index to calculate the delta against
+ * @param min minimum accepted value (clamp)
+ * @param max maximum accepted value (clamp)
+ * @return
+ */
+ private static float[] fit(float[] data, float v, int index, float min, float max) {
+ float[] fitData = new float[data.length];
+ float delta = v - data[index];
+
+ for (int i = 0; i < data.length; i++) {
+ fitData[i] = constrain(data[i] + delta, min, max);
+ }
+
+ return fitData;
+ }
+
+ // TODO no MathUtils
+ private static float constrain(float x, float min, float max) {
+ x = Math.min(x, max);
+ x = Math.max(x, min);
+ return x;
+ }
+
+ /*function adjustSatLumForFit(val, points, fitIndex) {
+ var fitValue = lerpBetweenPoints(points, fitIndex);
+ var diff = val - fitValue;
+
+ var newPoints = [];
+ for (var ii=0; ii<points.length; ii++) {
+ var point = [points[ii][0], points[ii][1]];
+ point[1] += diff;
+ if (point[1] > 1) point[1] = 1;
+ if (point[1] < 0) point[1] = 0;
+ newPoints[ii] = point;
+ }
+ return newPoints;
+ }*/
+
+ /**
+ * Finds the closest color in a palette, given another HSL color
+ *
+ * @param palette where to search
+ * @param h hue
+ * @param s saturation
+ * @param l lightness
+ * @return closest index or -1 if palette is empty.
+ */
+ private static int bestFit(@NonNull TonalPalette palette, float h, float s, float l) {
+ int minErrorIndex = -1;
+ float minError = Float.POSITIVE_INFINITY;
+
+ for (int i = 0; i < palette.h.length; i++) {
+ float error =
+ FIT_WEIGHT_H * Math.abs(h - palette.h[i])
+ + FIT_WEIGHT_S * Math.abs(s - palette.s[i])
+ + FIT_WEIGHT_L * Math.abs(l - palette.l[i]);
+ if (error < minError) {
+ minError = error;
+ minErrorIndex = i;
+ }
+ }
+
+ return minErrorIndex;
+ }
+
+ @Nullable
+ private static TonalPalette findTonalPalette(float h) {
+ TonalPalette best = null;
+ float error = Float.POSITIVE_INFINITY;
+
+ for (TonalPalette candidate : TONAL_PALETTES) {
+ if (h >= candidate.minHue && h <= candidate.maxHue) {
+ best = candidate;
+ break;
+ }
+
+ if (candidate.maxHue > 1.0f && h >= 0.0f && h <= fract(candidate.maxHue)) {
+ best = candidate;
+ break;
+ }
+
+ if (candidate.minHue < 0.0f && h >= fract(candidate.minHue) && h <= 1.0f) {
+ best = candidate;
+ break;
+ }
+
+ if (h <= candidate.minHue && candidate.minHue - h < error) {
+ best = candidate;
+ error = candidate.minHue - h;
+ } else if (h >= candidate.maxHue && h - candidate.maxHue < error) {
+ best = candidate;
+ error = h - candidate.maxHue;
+ } else if (candidate.maxHue > 1.0f && h >= fract(candidate.maxHue)
+ && h - fract(candidate.maxHue) < error) {
+ best = candidate;
+ error = h - fract(candidate.maxHue);
+ } else if (candidate.minHue < 0.0f && h <= fract(candidate.minHue)
+ && fract(candidate.minHue) - h < error) {
+ best = candidate;
+ error = fract(candidate.minHue) - h;
+ }
+ }
+
+ return best;
+ }
+
+ private static float fract(float v) {
+ return v - (float) Math.floor(v);
+ }
+
+ static class TonalPalette {
+ final float[] h;
+ final float[] s;
+ final float[] l;
+ final float minHue;
+ final float maxHue;
+
+ TonalPalette(float[] h, float[] s, float[] l) {
+ this.h = h;
+ this.s = s;
+ this.l = l;
+
+ float minHue = Float.POSITIVE_INFINITY;
+ float maxHue = Float.NEGATIVE_INFINITY;
+
+ for (float v : h) {
+ minHue = Math.min(v, minHue);
+ maxHue = Math.max(v, maxHue);
+ }
+
+ this.minHue = minHue;
+ this.maxHue = maxHue;
+ }
+ }
+
+ // Data definition of Material Design tonal palettes
+ // When the sort type is set to TONAL, these palettes are used to find
+ // a best fist. Each palette is defined as 10 HSL colors
+ private static final TonalPalette[] TONAL_PALETTES = {
+ // Orange
+ new TonalPalette(
+ new float[] { 0.028f, 0.042f, 0.053f, 0.061f, 0.078f, 0.1f, 0.111f, 0.111f, 0.111f, 0.111f },
+ new float[] { 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f },
+ new float[] { 0.5f, 0.53f, 0.54f, 0.55f, 0.535f, 0.52f, 0.5f, 0.63f, 0.75f, 0.85f }
+ ),
+ // Yellow
+ new TonalPalette(
+ new float[] { 0.111f, 0.111f, 0.125f, 0.133f, 0.139f, 0.147f, 0.156f, 0.156f, 0.156f, 0.156f },
+ new float[] { 1f, 0.942f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f },
+ new float[] { 0.43f, 0.484f, 0.535f, 0.555f, 0.57f, 0.575f, 0.595f, 0.715f, 0.78f, 0.885f }
+ ),
+ // Green
+ new TonalPalette(
+ new float[] { 0.325f, 0.336f, 0.353f, 0.353f, 0.356f, 0.356f, 0.356f, 0.356f, 0.356f, 0.356f },
+ new float[] { 1f, 1f, 0.852f, 0.754f, 0.639f, 0.667f, 0.379f, 0.542f, 1f, 1f },
+ new float[] { 0.06f, 0.1f, 0.151f, 0.194f, 0.25f, 0.312f, 0.486f, 0.651f, 0.825f, 0.885f }
+ ),
+ // Blue
+ new TonalPalette(
+ new float[] { 0.631f, 0.603f, 0.592f, 0.586f, 0.572f, 0.544f, 0.519f, 0.519f, 0.519f, 0.519f },
+ new float[] { 0.852f, 1f, 0.887f, 0.852f, 0.871f, 0.907f, 0.949f, 0.934f, 0.903f, 0.815f },
+ new float[] { 0.34f, 0.38f, 0.482f, 0.497f, 0.536f, 0.571f, 0.608f, 0.696f, 0.794f, 0.892f }
+ ),
+ // Purple
+ new TonalPalette(
+ new float[] { 0.839f, 0.831f, 0.825f, 0.819f, 0.803f, 0.803f, 0.772f, 0.772f, 0.772f, 0.772f },
+ new float[] { 1f, 1f, 1f, 1f, 1f, 1f, 0.769f, 0.701f, 0.612f, 0.403f },
+ new float[] { 0.125f, 0.15f, 0.2f, 0.245f, 0.31f, 0.36f, 0.567f, 0.666f, 0.743f, 0.833f }
+ ),
+ // Red
+ new TonalPalette(
+ new float[] { 0.964f, 0.975f, 0.975f, 0.975f, 0.972f, 0.992f, 1.003f, 1.011f, 1.011f, 1.011f },
+ new float[] { 0.869f, 0.802f, 0.739f, 0.903f, 1f, 1f, 1f, 1f, 1f, 1f },
+ new float[] { 0.241f, 0.316f, 0.46f, 0.586f, 0.655f, 0.7f, 0.75f, 0.8f, 0.84f, 0.88f }
+ )
+ };
+}
+
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 578921f..bee0bd4 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -139,6 +139,7 @@
final Rect folderIconPos = new Rect();
float scaleRelativeToDragLayer = mLauncher.getDragLayer()
.getDescendantRectRelativeToSelf(mFolderIcon, folderIconPos);
+ float initialSize = (mFolderIcon.mBackground.getRadius() * 2) * scaleRelativeToDragLayer;
// Match size/scale of icons in the preview
float previewScale = rule.scaleForItem(0, itemsInPreview.size());
@@ -156,6 +157,9 @@
// expected path to their final locations. ie. an icon should not move right, if it's final
// location is to its left. This value is arbitrarily defined.
int previewItemOffsetX = (int) (previewSize / 2);
+ if (Utilities.isRtl(mContext.getResources())) {
+ previewItemOffsetX = (int) (lp.width * initialScale - initialSize - previewItemOffsetX);
+ }
final int paddingOffsetX = (int) ((mFolder.getPaddingLeft() + mContent.getPaddingLeft())
* initialScale);
@@ -186,9 +190,6 @@
: finalTextColor);
// Set up the reveal animation that clips the Folder.
- float initialSize = (mFolderIcon.mBackground.getRadius() * 2
- + mPreviewBackground.getStrokeWidth()) * scaleRelativeToDragLayer;
-
int totalOffsetX = paddingOffsetX + previewItemOffsetX;
Rect startRect = new Rect(
Math.round(totalOffsetX / initialScale),
diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java
index 031da20..36df22c 100644
--- a/src/com/android/launcher3/testing/LauncherExtension.java
+++ b/src/com/android/launcher3/testing/LauncherExtension.java
@@ -10,7 +10,6 @@
import com.android.launcher3.AppInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherCallbacks;
-import com.android.launcher3.allapps.AllAppsSearchBarController;
import com.android.launcher3.util.ComponentKey;
import java.io.FileDescriptor;
@@ -198,11 +197,6 @@
}
@Override
- public AllAppsSearchBarController getAllAppsSearchBarController() {
- return null;
- }
-
- @Override
public List<ComponentKey> getPredictedApps() {
// To debug app predictions, enable AlphabeticalAppsList#DEBUG_PREDICTIONS
return new ArrayList<>();
@@ -214,11 +208,6 @@
}
@Override
- public void setLauncherSearchCallback(Object callbacks) {
- // Do nothing
- }
-
- @Override
public void onAttachedToWindow() {
}
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index e12b2d4..13034dd 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -30,9 +30,11 @@
import android.text.TextUtils;
import com.android.launcher3.AppInfo;
+import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.LauncherAppsCompat;
+import java.net.URISyntaxException;
import java.util.List;
/**
@@ -149,4 +151,20 @@
.appendQueryParameter("id", packageName)
.build());
}
+
+ /**
+ * Creates a new market search intent.
+ */
+ public static Intent getMarketSearchIntent(Context context, String query) {
+ try {
+ Intent intent = Intent.parseUri(context.getString(R.string.market_search_intent), 0);
+ if (!TextUtils.isEmpty(query)) {
+ intent.setData(
+ intent.getData().buildUpon().appendQueryParameter("q", query).build());
+ }
+ return intent;
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/tests/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithmTest.java b/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
similarity index 98%
rename from tests/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithmTest.java
rename to tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
index 18570de..20b23b0 100644
--- a/tests/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithmTest.java
+++ b/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.launcher3.allapps;
+package com.android.launcher3.allapps.search;
import android.content.ComponentName;
import android.test.InstrumentationTestCase;