Merge "Merging search bar with all apps" into ub-launcher3-burnaby-polish

This commit is contained in:
Sunny Goyal
2016-02-10 19:18:12 +00:00
committed by Android (Google) Code Review
20 changed files with 644 additions and 682 deletions

View File

@@ -14,7 +14,25 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/quantum_panel"
android:insetTop="@dimen/container_bounds_minus_quantum_panel_padding_inset"
android:insetBottom="@dimen/container_bounds_minus_quantum_panel_padding_inset" />
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="@color/quantum_panel_bg_color" />
<corners
android:topLeftRadius="2dp"
android:topRightRadius="2dp" />
</shape>
</item>
<item
android:top="@dimen/all_apps_search_bar_bg_overflow"
android:left="@dimen/all_apps_search_bar_bg_overflow"
android:right="@dimen/all_apps_search_bar_bg_overflow"
android:bottom="0dp">
<shape android:shape="rectangle">
<stroke
android:width="@dimen/all_apps_search_bar_divider_width"
android:color="#1E000000"/>
</shape>
</item>
</layer-list>

View File

@@ -25,6 +25,8 @@
<com.android.launcher3.DragLayer
android:id="@+id/drag_layer"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent">

View File

@@ -26,6 +26,8 @@
<com.android.launcher3.DragLayer
android:id="@+id/drag_layer"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent">

View File

@@ -25,6 +25,8 @@
<com.android.launcher3.DragLayer
android:id="@+id/drag_layer"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent">

View File

@@ -18,40 +18,72 @@
will bake the left/right padding into that view's background itself. -->
<com.android.launcher3.allapps.AllAppsContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:id="@+id/apps_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:orientation="vertical"
launcher:revealBackground="@drawable/quantum_panel_shape">
<!-- Both android:focusable and android:focusableInTouchMode are needed for
the view to get focus change events. -->
<FrameLayout
android:id="@+id/search_box_container"
<View
android:id="@+id/reveal_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
android:focusable="false"
android:elevation="2dp"
android:visibility="invisible" />
<com.android.launcher3.allapps.AllAppsRecyclerViewContainerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/main_content"
android:visibility="gone"
android:layout_gravity="center"
android:focusable="true"
android:focusableInTouchMode="true"
android:visibility="gone" />
android:elevation="15dp" >
<FrameLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<FrameLayout
android:id="@+id/all_apps_reveal"
<!-- DO NOT CHANGE THE ID -->
<com.android.launcher3.allapps.AllAppsRecyclerView
android:id="@+id/apps_list_view"
android:theme="@style/Theme.Light.CustomOverscroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:focusable="false"
android:elevation="2dp"
android:visibility="invisible" />
<include
layout="@layout/all_apps_container"
android:id="@+id/all_apps_container"
android:layout_gravity="center_horizontal|top"
android:clipToPadding="false"
android:focusable="true"
android:layout_marginTop="@dimen/all_apps_search_bar_height"
android:descendantFocusability="afterDescendants" />
<LinearLayout
android:id="@+id/search_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:visibility="gone" />
</FrameLayout>
android:layout_height="@dimen/all_apps_search_bar_height"
android:layout_gravity="start|top"
android:orientation="horizontal"
android:background="@drawable/all_apps_search_bg" >
<com.android.launcher3.ExtendedEditText
android:id="@+id/search_box_input"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent"
android:focusableInTouchMode="true"
android:gravity="fill_horizontal|center_vertical"
android:hint="@string/all_apps_search_bar_hint"
android:inputType="text|textNoSuggestions|textCapWords"
android:imeOptions="actionSearch|flagNoExtractUi"
android:maxLines="1"
android:scrollHorizontally="true"
android:layout_marginLeft="@dimen/container_fastscroll_thumb_max_width"
android:layout_marginRight="@dimen/container_fastscroll_thumb_max_width"
android:singleLine="true"
android:textColor="#4c4c4c"
android:textColorHint="#9c9c9c"
android:textSize="16sp" />
</LinearLayout>
</com.android.launcher3.allapps.AllAppsRecyclerViewContainerView>
</com.android.launcher3.allapps.AllAppsContainerView>

View File

@@ -18,34 +18,29 @@
will bake the left/right padding into that view's background itself. -->
<com.android.launcher3.widget.WidgetsContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:id="@+id/widgets_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:descendantFocusability="afterDescendants">
android:descendantFocusability="afterDescendants"
launcher:revealBackground="@drawable/quantum_panel_shape_dark">
<FrameLayout
android:id="@+id/content"
<View
android:id="@+id/reveal_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/widgets_reveal_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:focusable="false"
android:elevation="2dp"
android:visibility="invisible" />
android:layout_height="match_parent"
android:layout_gravity="center"
android:focusable="false"
android:elevation="2dp"
android:visibility="invisible" />
<!-- DO NOT CHANGE THE ID -->
<com.android.launcher3.widget.WidgetsRecyclerView
android:id="@+id/widgets_list_view"
android:theme="@style/Theme.Dark.CustomOverscroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:elevation="15dp"
android:visibility="gone" />
</FrameLayout>
<com.android.launcher3.widget.WidgetsRecyclerView
android:id="@+id/main_content"
android:theme="@style/Theme.Dark.CustomOverscroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:elevation="15dp"
android:visibility="gone" />
</com.android.launcher3.widget.WidgetsContainerView>

View File

@@ -83,6 +83,13 @@
<attr name="pageIndicator" format="reference" />
</declare-styleable>
<!-- BaseContainerView specific attributes. These attributes are used to customize
AllApps view and WidgetsView in xml. -->
<declare-styleable name="BaseContainerView">
<!-- Drawable to use for the reveal animation -->
<attr name="revealBackground" format="reference" />
</declare-styleable>
<!-- XML attributes used by default_workspace.xml -->
<declare-styleable name="Favorite">
<attr name="className" format="string" />

View File

@@ -77,7 +77,7 @@
<dimen name="all_apps_grid_view_start_margin">0dp</dimen>
<dimen name="all_apps_grid_section_y_offset">8dp</dimen>
<dimen name="all_apps_grid_section_text_size">24sp</dimen>
<dimen name="all_apps_search_bar_height">48dp</dimen>
<dimen name="all_apps_search_bar_height">60dp</dimen>
<dimen name="all_apps_search_bar_prediction_bar_padding">8dp</dimen>
<dimen name="all_apps_icon_top_bottom_padding">8dp</dimen>
<dimen name="all_apps_icon_width_gap">24dp</dimen>
@@ -90,7 +90,20 @@
<dimen name="all_apps_background_canvas_width">700dp</dimen>
<dimen name="all_apps_background_canvas_height">475dp</dimen>
<!-- Widget tray -->
<!-- Search bar in All Apps -->
<dimen name="all_apps_header_max_elevation">4dp</dimen>
<dimen name="all_apps_header_scroll_to_elevation">16dp</dimen>
<dimen name="all_apps_header_shadow_height">6dp</dimen>
<!-- The overflow is used to create a bottom border, by drawing other three sides
outside the bounds. Ensure that:
all_apps_search_bar_bg_overflow < (-3 * all_apps_search_bar_divider_width)
-6dp is picked at random, any smaller value would do.
-->
<dimen name="all_apps_search_bar_bg_overflow">-6dp</dimen>
<dimen name="all_apps_search_bar_divider_width">1dp</dimen>
<!-- Widget tray -->
<dimen name="widget_container_inset">8dp</dimen>
<dimen name="widget_preview_label_vertical_padding">8dp</dimen>
<dimen name="widget_preview_label_horizontal_padding">8dp</dimen>

View File

@@ -56,7 +56,7 @@
<!-- All Apps -->
<!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
<string name="all_apps_search_bar_hint">Search Apps</string>
<string name="all_apps_search_bar_hint">Search Apps&#8230;</string>
<!-- Loading apps text. [CHAR_LIMIT=50] -->
<string name="all_apps_loading_message">Loading Apps&#8230;</string>
<!-- No-search-results text. [CHAR_LIMIT=50] -->

View File

@@ -17,32 +17,38 @@
package com.android.launcher3;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.InsetDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.LinearLayout;
import android.view.View;
import android.widget.FrameLayout;
/**
* A base container view, which supports resizing.
*/
public abstract class BaseContainerView extends LinearLayout implements Insettable {
public abstract class BaseContainerView extends FrameLayout implements Insettable {
private final static String TAG = "BaseContainerView";
// The window insets
private Rect mInsets = new Rect();
private final Rect mInsets = new Rect();
// The bounds of the search bar. Only the left, top, right are used to inset the
// search bar and the height is determined by the measurement of the layout
private Rect mFixedSearchBarBounds = new Rect();
// The computed bounds of the search bar
private Rect mSearchBarBounds = new Rect();
private final Rect mFixedSearchBarBounds = new Rect();
// The computed bounds of the container
protected Rect mContentBounds = new Rect();
protected final Rect mContentBounds = new Rect();
// The computed padding to apply to the container to achieve the container bounds
private Rect mContentPadding = new Rect();
private final Rect mContentPadding = new Rect();
// The inset to apply to the edges and between the search bar and the container
private int mContainerBoundsInset;
private boolean mHasSearchBar;
private final int mContainerBoundsInset;
private final Drawable mRevealDrawable;
private View mRevealView;
private View mContent;
public BaseContainerView(Context context) {
this(context, null);
@@ -55,6 +61,19 @@ public abstract class BaseContainerView extends LinearLayout implements Insettab
public BaseContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContainerBoundsInset = getResources().getDimensionPixelSize(R.dimen.container_bounds_inset);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.BaseContainerView, defStyleAttr, 0);
mRevealDrawable = a.getDrawable(R.styleable.BaseContainerView_revealBackground);
a.recycle();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mContent = findViewById(R.id.main_content);
mRevealView = findViewById(R.id.reveal_view);
}
@Override
@@ -63,10 +82,6 @@ public abstract class BaseContainerView extends LinearLayout implements Insettab
updateBackgroundAndPaddings();
}
protected void setHasSearchBar() {
mHasSearchBar = true;
}
/**
* Sets the search bar bounds for this container view to match.
*/
@@ -92,47 +107,48 @@ public abstract class BaseContainerView extends LinearLayout implements Insettab
*/
protected void updateBackgroundAndPaddings() {
Rect padding;
Rect searchBarBounds = new Rect();
if (!isValidSearchBarBounds(mFixedSearchBarBounds)) {
// Use the default bounds
padding = new Rect(mInsets.left + mContainerBoundsInset,
(mHasSearchBar ? 0 : (mInsets.top + mContainerBoundsInset)),
mInsets.right + mContainerBoundsInset,
mInsets.bottom + mContainerBoundsInset);
// Special case -- we have the search bar, but no specific bounds, so just give it
// the inset bounds without a height.
searchBarBounds.set(mInsets.left + mContainerBoundsInset,
if (isValidSearchBarBounds(mFixedSearchBarBounds)) {
padding = new Rect(
mFixedSearchBarBounds.left,
mInsets.top + mContainerBoundsInset,
getMeasuredWidth() - (mInsets.right + mContainerBoundsInset), 0);
} else {
// Use the search bounds, if there is a search bar, the bounds will contain
// the offsets for the insets so we can ignore that
padding = new Rect(mFixedSearchBarBounds.left,
(mHasSearchBar ? 0 : (mInsets.top + mContainerBoundsInset)),
getMeasuredWidth() - mFixedSearchBarBounds.right,
mInsets.bottom + mContainerBoundsInset);
// Use the search bounds
searchBarBounds.set(mFixedSearchBarBounds);
mInsets.bottom + mContainerBoundsInset
);
} else {
padding = new Rect(
mInsets.left + mContainerBoundsInset,
mInsets.top + mContainerBoundsInset,
mInsets.right + mContainerBoundsInset,
mInsets.bottom + mContainerBoundsInset
);
}
// If either the computed container padding has changed, or the computed search bar bounds
// has changed, then notify the container
if (!padding.equals(mContentPadding) || !searchBarBounds.equals(mSearchBarBounds)) {
// The container padding changed, notify the container.
if (!padding.equals(mContentPadding)) {
mContentPadding.set(padding);
mContentBounds.set(padding.left, padding.top,
getMeasuredWidth() - padding.right,
getMeasuredHeight() - padding.bottom);
mSearchBarBounds.set(searchBarBounds);
onUpdateBackgroundAndPaddings(mSearchBarBounds, padding);
onUpdateBackgroundAndPaddings(padding);
}
}
/**
* To be implemented by container views to update themselves when the bounds changes.
*/
protected abstract void onUpdateBackgroundAndPaddings(Rect searchBarBounds, Rect padding);
private void onUpdateBackgroundAndPaddings(Rect padding) {
// Apply the top-bottom padding to itself so that the launcher transition is
// clipped correctly
setPadding(0, padding.top, 0, padding.bottom);
InsetDrawable background = new InsetDrawable(mRevealDrawable,
padding.left, 0, padding.right, 0);
mRevealView.setBackground(background.getConstantState().newDrawable());
mContent.setBackground(background);
Rect bgPadding = new Rect();
background.getPadding(bgPadding);
onUpdateBgPadding(padding, bgPadding);
}
protected abstract void onUpdateBgPadding(Rect padding, Rect bgPadding);
/**
* Returns whether the search bar bounds we got are considered valid.
@@ -142,4 +158,12 @@ public abstract class BaseContainerView extends LinearLayout implements Insettab
searchBarBounds.right <= getMeasuredWidth() &&
searchBarBounds.bottom <= getMeasuredHeight();
}
public final View getContentView() {
return mContent;
}
public final View getRevealView() {
return mRevealView;
}
}

View File

@@ -97,6 +97,7 @@ import android.widget.Toast;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.PagedView.PageSwitchListener;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.DefaultAppSearchController;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherActivityInfoCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
@@ -1408,7 +1409,7 @@ public class Launcher extends Activity
if (mLauncherCallbacks != null && mLauncherCallbacks.getAllAppsSearchBarController() != null) {
mAppsView.setSearchBarController(mLauncherCallbacks.getAllAppsSearchBarController());
} else {
mAppsView.setSearchBarController(mAppsView.newDefaultAppSearchController());
mAppsView.setSearchBarController(new DefaultAppSearchController());
}
// Setup the drag controller (drop targets have to be added in reverse order in priority)

View File

@@ -80,13 +80,18 @@ import java.util.HashMap;
*/
public class LauncherStateTransitionAnimation {
private static final float FINAL_REVEAL_ALPHA_FOR_WIDGETS = 0.3f;
/**
* Private callbacks made during transition setup.
*/
static abstract class PrivateTransitionCallbacks {
float getMaterialRevealViewFinalAlpha(View revealView) {
return 0;
private static class PrivateTransitionCallbacks {
private final float materialRevealViewFinalAlpha;
PrivateTransitionCallbacks(float revealAlpha) {
materialRevealViewFinalAlpha = revealAlpha;
}
float getMaterialRevealViewStartFinalRadius() {
return 0;
}
@@ -97,7 +102,7 @@ public class LauncherStateTransitionAnimation {
void onTransitionComplete() {}
}
public static final String TAG = "LauncherStateTransitionAnimation";
public static final String TAG = "LSTAnimation";
// Flags to determine how to set the layers on views before the transition animation
public static final int BUILD_LAYER = 0;
@@ -121,11 +126,7 @@ public class LauncherStateTransitionAnimation {
final boolean animated, final boolean startSearchAfterTransition) {
final AllAppsContainerView toView = mLauncher.getAppsView();
final View buttonView = mLauncher.getAllAppsButton();
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
@Override
public float getMaterialRevealViewFinalAlpha(View revealView) {
return 1f;
}
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks(1f) {
@Override
public float getMaterialRevealViewStartFinalRadius() {
int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
@@ -152,8 +153,7 @@ public class LauncherStateTransitionAnimation {
};
// Only animate the search bar if animating from spring loaded mode back to all apps
mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState,
Workspace.State.NORMAL_HIDDEN, buttonView, toView, toView.getContentView(),
toView.getRevealView(), toView.getSearchBarView(), animated, cb);
Workspace.State.NORMAL_HIDDEN, buttonView, toView, animated, cb);
}
/**
@@ -164,15 +164,9 @@ public class LauncherStateTransitionAnimation {
final WidgetsContainerView toView = mLauncher.getWidgetsView();
final View buttonView = mLauncher.getWidgetsButton();
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
@Override
public float getMaterialRevealViewFinalAlpha(View revealView) {
return 0.3f;
}
};
mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState,
Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, toView.getContentView(),
toView.getRevealView(), null, animated, cb);
Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, animated,
new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS));
}
/**
@@ -200,16 +194,15 @@ public class LauncherStateTransitionAnimation {
* Creates and starts a new animation to a particular overlay view.
*/
@SuppressLint("NewApi")
private AnimatorSet startAnimationToOverlay(final Workspace.State fromWorkspaceState,
final Workspace.State toWorkspaceState, final View buttonView, final View toView,
final View contentView, final View revealView, final View overlaySearchBarView,
private AnimatorSet startAnimationToOverlay(
final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
final View buttonView, final BaseContainerView toView,
final boolean animated, final PrivateTransitionCallbacks pCb) {
final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
final Resources res = mLauncher.getResources();
final boolean material = Utilities.ATLEAST_LOLLIPOP;
final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
final int itemsAlphaStagger =
res.getInteger(R.integer.config_overlayItemsAlphaStagger);
final int itemsAlphaStagger = res.getInteger(R.integer.config_overlayItemsAlphaStagger);
final View fromView = mLauncher.getWorkspace();
@@ -227,13 +220,17 @@ public class LauncherStateTransitionAnimation {
animated, layerViews);
// Animate the search bar
startWorkspaceSearchBarAnimation(animation, fromWorkspaceState, toWorkspaceState,
animated ? revealDuration : 0, overlaySearchBarView);
startWorkspaceSearchBarAnimation(
toWorkspaceState, animated ? revealDuration : 0, animation);
Animator updateTransitionStepAnim = dispatchOnLauncherTransitionStepAnim(fromView, toView);
final View contentView = toView.getContentView();
if (animated && initialized) {
// Setup the reveal view animation
final View revealView = toView.getRevealView();
int width = revealView.getMeasuredWidth();
int height = revealView.getMeasuredHeight();
float revealRadius = (float) Math.hypot(width / 2, height / 2);
@@ -247,9 +244,9 @@ public class LauncherStateTransitionAnimation {
final float revealViewToXDrift;
final float revealViewToYDrift;
if (material) {
int[] buttonViewToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
buttonView, null);
revealViewToAlpha = pCb.getMaterialRevealViewFinalAlpha(revealView);
int[] buttonViewToPanelDelta = Utilities.getCenterDeltaInScreenSpace(
revealView, buttonView, null);
revealViewToAlpha = pCb.materialRevealViewFinalAlpha;
revealViewToYDrift = buttonViewToPanelDelta[1];
revealViewToXDrift = buttonViewToPanelDelta[0];
} else {
@@ -274,15 +271,6 @@ public class LauncherStateTransitionAnimation {
layerViews.put(revealView, BUILD_AND_SET_LAYER);
animation.play(panelAlphaAndDrift);
if (overlaySearchBarView != null) {
overlaySearchBarView.setAlpha(0f);
ObjectAnimator searchBarAlpha = ObjectAnimator.ofFloat(overlaySearchBarView, "alpha", 0f, 1f);
searchBarAlpha.setDuration(revealDuration / 2);
searchBarAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
animation.play(searchBarAlpha);
}
// Setup the animation for the content view
contentView.setVisibility(View.VISIBLE);
contentView.setAlpha(0f);
@@ -430,12 +418,8 @@ public class LauncherStateTransitionAnimation {
final Workspace.State toWorkspaceState, final int toWorkspacePage,
final boolean animated, final Runnable onCompleteRunnable) {
AllAppsContainerView appsView = mLauncher.getAppsView();
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
@Override
float getMaterialRevealViewFinalAlpha(View revealView) {
// No alpha anim from all apps
return 1f;
}
// No alpha anim from all apps
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks(1f) {
@Override
float getMaterialRevealViewStartFinalRadius() {
int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
@@ -463,9 +447,8 @@ public class LauncherStateTransitionAnimation {
};
// Only animate the search bar if animating to spring loaded mode from all apps
mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState,
toWorkspacePage, mLauncher.getAllAppsButton(), appsView, appsView.getContentView(),
appsView.getRevealView(), appsView.getSearchBarView(), animated,
onCompleteRunnable, cb);
toWorkspacePage, mLauncher.getAllAppsButton(), appsView,
animated, onCompleteRunnable, cb);
}
/**
@@ -475,11 +458,8 @@ public class LauncherStateTransitionAnimation {
final Workspace.State toWorkspaceState, final int toWorkspacePage,
final boolean animated, final Runnable onCompleteRunnable) {
final WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
@Override
float getMaterialRevealViewFinalAlpha(View revealView) {
return 0.3f;
}
PrivateTransitionCallbacks cb =
new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS) {
@Override
public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
final View revealView, final View widgetsButtonView) {
@@ -491,19 +471,20 @@ public class LauncherStateTransitionAnimation {
};
}
};
mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState,
toWorkspaceState, toWorkspacePage, mLauncher.getWidgetsButton(), widgetsView,
widgetsView.getContentView(), widgetsView.getRevealView(), null, animated,
onCompleteRunnable, cb);
mCurrentAnimation = startAnimationToWorkspaceFromOverlay(
fromWorkspaceState, toWorkspaceState,
toWorkspacePage, mLauncher.getWidgetsButton(), widgetsView,
animated, onCompleteRunnable, cb);
}
/**
* Creates and starts a new animation to the workspace.
*/
private AnimatorSet startAnimationToWorkspaceFromOverlay(final Workspace.State fromWorkspaceState,
final Workspace.State toWorkspaceState, final int toWorkspacePage, final View buttonView,
final View fromView, final View contentView, final View revealView,
final View overlaySearchBarView, final boolean animated, final Runnable onCompleteRunnable,
private AnimatorSet startAnimationToWorkspaceFromOverlay(
final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
final int toWorkspacePage,
final View buttonView, final BaseContainerView fromView,
final boolean animated, final Runnable onCompleteRunnable,
final PrivateTransitionCallbacks pCb) {
final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
final Resources res = mLauncher.getResources();
@@ -528,8 +509,8 @@ public class LauncherStateTransitionAnimation {
toWorkspacePage, animated, layerViews);
// Animate the search bar
startWorkspaceSearchBarAnimation(animation, fromWorkspaceState, toWorkspaceState,
animated ? revealDuration : 0, overlaySearchBarView);
startWorkspaceSearchBarAnimation(
toWorkspaceState, animated ? revealDuration : 0, animation);
Animator updateTransitionStepAnim = dispatchOnLauncherTransitionStepAnim(fromView, toView);
@@ -540,6 +521,8 @@ public class LauncherStateTransitionAnimation {
}
animation.play(updateTransitionStepAnim);
final View revealView = fromView.getRevealView();
final View contentView = fromView.getContentView();
// hideAppsCustomizeHelper is called in some cases when it is already hidden
// don't perform all these no-op animations. In particularly, this was causing
@@ -588,7 +571,7 @@ public class LauncherStateTransitionAnimation {
// Setup animation for the reveal panel alpha
final float revealViewToAlpha = !material ? 0f :
pCb.getMaterialRevealViewFinalAlpha(revealView);
pCb.materialRevealViewFinalAlpha;
if (revealViewToAlpha != 1f) {
ObjectAnimator panelAlpha = ObjectAnimator.ofFloat(revealView, "alpha",
1f, revealViewToAlpha);
@@ -616,16 +599,6 @@ public class LauncherStateTransitionAnimation {
itemsAlpha.setInterpolator(decelerateInterpolator);
animation.play(itemsAlpha);
if (overlaySearchBarView != null) {
overlaySearchBarView.setAlpha(1f);
ObjectAnimator searchAlpha = ObjectAnimator.ofFloat(overlaySearchBarView, "alpha", 1f, 0f);
searchAlpha.setDuration(revealDuration / 2);
searchAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
searchAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
animation.play(searchAlpha);
}
if (material) {
// Animate the all apps button
float finalRadius = pCb.getMaterialRevealViewStartFinalRadius();
@@ -671,9 +644,6 @@ public class LauncherStateTransitionAnimation {
contentView.setTranslationY(0);
contentView.setAlpha(1);
}
if (overlaySearchBarView != null) {
overlaySearchBarView.setAlpha(1f);
}
// This can hold unnecessary references to views.
cleanupAnimation();
@@ -729,36 +699,11 @@ public class LauncherStateTransitionAnimation {
/**
* Coordinates the workspace search bar animation along with the launcher state animation.
*/
private void startWorkspaceSearchBarAnimation(AnimatorSet animation,
final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState, int duration,
View overlaySearchBar) {
private void startWorkspaceSearchBarAnimation(
final Workspace.State toWorkspaceState, int duration, AnimatorSet animation) {
final SearchDropTargetBar.State toSearchBarState =
toWorkspaceState.getSearchDropTargetBarState();
if (overlaySearchBar != null) {
if (mLauncher.launcherCallbacksProvidesSearch()) {
if ((toWorkspaceState == Workspace.State.NORMAL) &&
(fromWorkspaceState == Workspace.State.NORMAL_HIDDEN)) {
// If we are transitioning from the overlay to the workspace, then show the
// workspace search bar immediately and let the overlay search bar fade out on
// top
mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, 0);
return;
} else if (fromWorkspaceState == Workspace.State.NORMAL) {
// If we are transitioning from the workspace to the overlay, then keep the
// workspace search bar visible until the overlay search bar fades in on top
animation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, 0);
}
});
return;
}
}
}
// Fallback to the default search bar animation otherwise
mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, duration);
mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, duration, animation);
}
/**

View File

@@ -18,12 +18,18 @@ package com.android.launcher3;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import com.android.launcher3.util.Thunk;
@@ -34,35 +40,32 @@ import com.android.launcher3.util.Thunk;
*/
public class SearchDropTargetBar extends FrameLayout implements DragController.DragListener {
private static final TimeInterpolator MOVE_DOWN_INTERPOLATOR = new DecelerateInterpolator(0.6f);
private static final TimeInterpolator MOVE_UP_INTERPOLATOR = new DecelerateInterpolator(1.5f);
private static final TimeInterpolator DEFAULT_INTERPOLATOR = new AccelerateInterpolator();
/** The different states that the search bar space can be in. */
public enum State {
INVISIBLE (0f, 0f),
SEARCH_BAR (1f, 0f),
DROP_TARGET (0f, 1f);
INVISIBLE (0f, 0f, 0f),
INVISIBLE_TRANSLATED (0f, 0f, -1f),
SEARCH_BAR (1f, 0f, 0f),
DROP_TARGET (0f, 1f, 0f);
private final float mSearchBarAlpha;
private final float mDropTargetBarAlpha;
private final float mTranslation;
State(float sbAlpha, float dtbAlpha) {
State(float sbAlpha, float dtbAlpha, float translation) {
mSearchBarAlpha = sbAlpha;
mDropTargetBarAlpha = dtbAlpha;
mTranslation = translation;
}
float getSearchBarAlpha() {
return mSearchBarAlpha;
}
float getDropTargetBarAlpha() {
return mDropTargetBarAlpha;
}
}
private static int DEFAULT_DRAG_FADE_DURATION = 175;
private LauncherViewPropertyAnimator mDropTargetBarAnimator;
private LauncherViewPropertyAnimator mQSBSearchBarAnimator;
private static final AccelerateInterpolator sAccelerateInterpolator =
new AccelerateInterpolator();
private AnimatorSet mCurrentAnimation;
private State mState = State.SEARCH_BAR;
@Thunk View mQSB;
@@ -117,50 +120,10 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
// Create the various fade animations
mDropTargetBar.setAlpha(0f);
AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
mDropTargetBarAnimator = new LauncherViewPropertyAnimator(mDropTargetBar);
mDropTargetBarAnimator.setInterpolator(sAccelerateInterpolator);
mDropTargetBarAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
// Ensure that the view is visible for the animation
mDropTargetBar.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Animator animation) {
if (mDropTargetBar != null) {
AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
}
}
});
}
public void setQsbSearchBar(View qsb) {
mQSB = qsb;
if (mQSB != null) {
// Update the search ber animation
mQSBSearchBarAnimator = new LauncherViewPropertyAnimator(mQSB);
mQSBSearchBarAnimator.setInterpolator(sAccelerateInterpolator);
mQSBSearchBarAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
// Ensure that the view is visible for the animation
if (mQSB != null) {
mQSB.setVisibility(View.VISIBLE);
}
}
@Override
public void onAnimationEnd(Animator animation) {
if (mQSB != null) {
AlphaUpdateListener.updateVisibility(mQSB, mAccessibilityEnabled);
}
}
});
} else {
mQSBSearchBarAnimator = null;
}
}
/**
@@ -168,6 +131,10 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
* the state is applied immediately.
*/
public void animateToState(State newState, int duration) {
animateToState(newState, duration, null);
}
public void animateToState(State newState, int duration, AnimatorSet animation) {
if (mState != newState) {
mState = newState;
@@ -176,30 +143,63 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
mAccessibilityEnabled = am.isEnabled();
animateViewAlpha(mQSBSearchBarAnimator, mQSB, newState.getSearchBarAlpha(),
duration);
animateViewAlpha(mDropTargetBarAnimator, mDropTargetBar, newState.getDropTargetBarAlpha(),
duration);
if (mCurrentAnimation != null) {
mCurrentAnimation.cancel();
mCurrentAnimation = null;
}
mCurrentAnimation = null;
if (duration > 0) {
mCurrentAnimation = new AnimatorSet();
mCurrentAnimation.setDuration(duration);
animateAlpha(mDropTargetBar, mState.mDropTargetBarAlpha, DEFAULT_INTERPOLATOR);
} else {
mDropTargetBar.setAlpha(mState.mDropTargetBarAlpha);
AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
}
if (mQSB != null) {
boolean isVertical = ((Launcher) getContext()).getDeviceProfile()
.isVerticalBarLayout();
float translation = isVertical ? 0 : mState.mTranslation * getMeasuredHeight();
if (duration > 0) {
int translationChange = Float.compare(mQSB.getTranslationY(), translation);
animateAlpha(mQSB, mState.mSearchBarAlpha,
translationChange == 0 ? DEFAULT_INTERPOLATOR
: (translationChange < 0 ? MOVE_DOWN_INTERPOLATOR
: MOVE_UP_INTERPOLATOR));
if (translationChange != 0) {
mCurrentAnimation.play(
ObjectAnimator.ofFloat(mQSB, View.TRANSLATION_Y, translation));
}
} else {
mQSB.setTranslationY(translation);
mQSB.setAlpha(mState.mSearchBarAlpha);
AlphaUpdateListener.updateVisibility(mQSB, mAccessibilityEnabled);
}
}
// Start the final animation
if (duration > 0) {
if (animation != null) {
animation.play(mCurrentAnimation);
} else {
mCurrentAnimation.start();
}
}
}
}
/**
* Convenience method to animate the alpha of a view using hardware layers.
*/
private void animateViewAlpha(LauncherViewPropertyAnimator animator, View v, float alpha,
int duration) {
if (v == null) {
return;
}
animator.cancel();
private void animateAlpha(View v, float alpha, TimeInterpolator interpolator) {
if (Float.compare(v.getAlpha(), alpha) != 0) {
if (duration > 0) {
animator.alpha(alpha).withLayer().setDuration(duration).start();
} else {
v.setAlpha(alpha);
AlphaUpdateListener.updateVisibility(v, mAccessibilityEnabled);
}
ObjectAnimator anim = ObjectAnimator.ofFloat(v, View.ALPHA, alpha);
anim.setInterpolator(interpolator);
anim.addListener(new ViewVisiblilyUpdateHandler(v));
mCurrentAnimation.play(anim);
}
}
@@ -255,4 +255,24 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
mDeleteDropTarget.enableAccessibleDrag(enable);
mUninstallDropTarget.enableAccessibleDrag(enable);
}
private class ViewVisiblilyUpdateHandler extends AnimatorListenerAdapter {
private final View mView;
ViewVisiblilyUpdateHandler(View v) {
mView = v;
}
@Override
public void onAnimationStart(Animator animation) {
// Ensure that the view is visible for the animation
mView.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Animator animation){
AlphaUpdateListener.updateVisibility(mView, mAccessibilityEnabled);
}
}
}

View File

@@ -181,7 +181,7 @@ public class Workspace extends PagedView
enum State {
NORMAL (SearchDropTargetBar.State.SEARCH_BAR),
NORMAL_HIDDEN (SearchDropTargetBar.State.INVISIBLE),
NORMAL_HIDDEN (SearchDropTargetBar.State.INVISIBLE_TRANSLATED),
SPRING_LOADED (SearchDropTargetBar.State.DROP_TARGET),
OVERVIEW (SearchDropTargetBar.State.INVISIBLE),
OVERVIEW_HIDDEN (SearchDropTargetBar.State.INVISIBLE);

View File

@@ -17,11 +17,9 @@ package com.android.launcher3.allapps;
import android.annotation.SuppressLint;
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.support.v7.widget.RecyclerView;
import android.text.Selection;
import android.text.SpannableStringBuilder;
@@ -32,8 +30,7 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import com.android.launcher3.AppInfo;
import com.android.launcher3.BaseContainerView;
import com.android.launcher3.BubbleTextView;
@@ -42,6 +39,7 @@ import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Folder;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
@@ -50,7 +48,6 @@ import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.Thunk;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
@@ -135,19 +132,19 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
private static final int MIN_ROWS_IN_MERGED_SECTION_PHONE = 3;
private static final int MAX_NUM_MERGES_PHONE = 2;
@Thunk Launcher mLauncher;
@Thunk AlphabeticalAppsList mApps;
private AllAppsGridAdapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
private RecyclerView.ItemDecoration mItemDecoration;
private final Launcher mLauncher;
private final AlphabeticalAppsList mApps;
private final AllAppsGridAdapter mAdapter;
private final RecyclerView.LayoutManager mLayoutManager;
private final RecyclerView.ItemDecoration mItemDecoration;
private AllAppsRecyclerView mAppsRecyclerView;
private AllAppsSearchBarController mSearchBarController;
private View mSearchContainer;
private ExtendedEditText mSearchInput;
private HeaderElevationController mElevationController;
@Thunk View mContent;
@Thunk View mContainerView;
@Thunk View mRevealView;
@Thunk AllAppsRecyclerView mAppsRecyclerView;
@Thunk AllAppsSearchBarController mSearchBarController;
private ViewGroup mSearchBarContainerView;
private View mSearchBarView;
private SpannableStringBuilder mSearchQueryBuilder = null;
private int mSectionNamesMargin;
@@ -159,14 +156,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
// This coordinate is relative to its parent
private final Point mIconLastTouchPos = new Point();
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);
}
@@ -236,14 +225,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
throw new RuntimeException("Expected search bar controller to only be set once");
}
mSearchBarController = searchController;
mSearchBarController.initialize(mApps, this);
// Add the new search view to the layout
View searchBarView = searchController.getView(mSearchBarContainerView);
mSearchBarContainerView.addView(searchBarView);
mSearchBarContainerView.setVisibility(View.VISIBLE);
mSearchBarView = searchBarView;
setHasSearchBar();
mSearchBarController.initialize(mApps, mSearchInput, mAppsRecyclerView, this);
updateBackgroundAndPaddings();
}
@@ -255,34 +237,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
mAppsRecyclerView.scrollToTop();
}
/**
* Returns the content view used for the launcher transitions.
*/
public View getContentView() {
return mContainerView;
}
/**
* Returns the all apps search view.
*/
public View getSearchBarView() {
return mSearchBarView;
}
/**
* Returns the reveal view used for the launcher transitions.
*/
public View getRevealView() {
return mRevealView;
}
/**
* Returns an new instance of the default app search controller.
*/
public AllAppsSearchBarController newDefaultAppSearchController() {
return new DefaultAppSearchController(getContext(), this, mAppsRecyclerView);
}
/**
* Focuses the search field and begins an app search.
*/
@@ -304,25 +258,24 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
@Override
protected void onFinishInflate() {
super.onFinishInflate();
boolean isRtl = Utilities.isRtl(getResources());
mAdapter.setRtl(isRtl);
mContent = findViewById(R.id.content);
mAdapter.setRtl(Utilities.isRtl(getResources()));
// This is a focus listener that proxies focus from a view into the list view. This is to
// work around the search box from getting first focus and showing the cursor.
View.OnFocusChangeListener focusProxyListener = new View.OnFocusChangeListener() {
getContentView().setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
mAppsRecyclerView.requestFocus();
}
}
};
mSearchBarContainerView = (ViewGroup) findViewById(R.id.search_box_container);
mSearchBarContainerView.setOnFocusChangeListener(focusProxyListener);
mContainerView = findViewById(R.id.all_apps_container);
mContainerView.setOnFocusChangeListener(focusProxyListener);
mRevealView = findViewById(R.id.all_apps_reveal);
});
mSearchContainer = findViewById(R.id.search_container);
mSearchInput = (ExtendedEditText) findViewById(R.id.search_box_input);
mElevationController = Utilities.ATLEAST_LOLLIPOP
? new HeaderElevationController.ControllerVL(mSearchContainer)
: new HeaderElevationController.ControllerV16(mSearchContainer);
// Load the all apps recycler view
mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);
@@ -330,22 +283,28 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
mAppsRecyclerView.setLayoutManager(mLayoutManager);
mAppsRecyclerView.setAdapter(mAdapter);
mAppsRecyclerView.setHasFixedSize(true);
mAppsRecyclerView.addOnScrollListener(mElevationController);
mAppsRecyclerView.setElevationController(mElevationController);
if (mItemDecoration != null) {
mAppsRecyclerView.addItemDecoration(mItemDecoration);
}
// Precalculate the prediction icon and normal icon sizes
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(
getResources().getDisplayMetrics().widthPixels, MeasureSpec.AT_MOST);
final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(
getResources().getDisplayMetrics().heightPixels, MeasureSpec.AT_MOST);
BubbleTextView icon = (BubbleTextView) layoutInflater.inflate(
R.layout.all_apps_icon, this, false);
icon.applyDummyInfo();
icon.measure(MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE, MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE, MeasureSpec.AT_MOST));
icon.measure(widthMeasureSpec, heightMeasureSpec);
BubbleTextView predIcon = (BubbleTextView) layoutInflater.inflate(
R.layout.all_apps_prediction_bar_icon, this, false);
predIcon.applyDummyInfo();
predIcon.measure(MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE, MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE, MeasureSpec.AT_MOST));
predIcon.measure(widthMeasureSpec, heightMeasureSpec);
mAppsRecyclerView.setPremeasuredIconHeights(predIcon.getMeasuredHeight(),
icon.getMeasuredHeight());
@@ -360,8 +319,11 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Update the number of items in the grid before we measure the view
int availableWidth = !mContentBounds.isEmpty() ? mContentBounds.width() :
MeasureSpec.getSize(widthMeasureSpec);
// TODO: mSectionNamesMargin is currently 0, but also account for it,
// if it's enabled in the future.
int availableWidth = (!mContentBounds.isEmpty() ? mContentBounds.width() :
MeasureSpec.getSize(widthMeasureSpec))
- 2 * mAppsRecyclerView.getMaxScrollbarWidth();
DeviceProfile grid = mLauncher.getDeviceProfile();
grid.updateAppsViewNumCols(getResources(), availableWidth);
if (mNumAppsPerRow != grid.allAppsNumCols ||
@@ -380,6 +342,12 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow);
mAdapter.setNumAppsPerRow(mNumAppsPerRow);
mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, mergeAlgorithm);
if (mNumAppsPerRow > 0) {
int iconSize = availableWidth / mNumAppsPerRow;
int iconSpacing = (iconSize - grid.allAppsIconSizePx) / 2;
mSearchInput.setPaddingRelative(iconSpacing, 0, iconSpacing, 0);
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -391,51 +359,33 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
* recycler view to handle touch events (for fast scrolling) all the way to the edge.
*/
@Override
protected void onUpdateBackgroundAndPaddings(Rect searchBarBounds, Rect padding) {
boolean isRtl = Utilities.isRtl(getResources());
// TODO: Use quantum_panel instead of quantum_panel_shape
InsetDrawable background = new InsetDrawable(
getResources().getDrawable(R.drawable.quantum_panel_shape), padding.left, 0,
padding.right, 0);
Rect bgPadding = new Rect();
background.getPadding(bgPadding);
mContainerView.setBackground(background);
mRevealView.setBackground(background.getConstantState().newDrawable());
protected void onUpdateBgPadding(Rect padding, Rect bgPadding) {
mAppsRecyclerView.updateBackgroundPadding(bgPadding);
mAdapter.updateBackgroundPadding(bgPadding);
mElevationController.updateBackgroundPadding(bgPadding);
// Hack: We are going to let the recycler view take the full width, so reset the padding on
// the container to zero after setting the background and apply the top-bottom padding to
// the content view instead so that the launcher transition clips correctly.
mContent.setPadding(0, padding.top, 0, padding.bottom);
mContainerView.setPadding(0, 0, 0, 0);
getContentView().setPadding(0, 0, 0, 0);
// Pad the recycler view by the background padding plus the start margin (for the section
// names)
int startInset = Math.max(mSectionNamesMargin, mAppsRecyclerView.getMaxScrollbarWidth());
int maxScrollBarWidth = mAppsRecyclerView.getMaxScrollbarWidth();
int startInset = Math.max(mSectionNamesMargin, maxScrollBarWidth);
int topBottomPadding = mRecyclerViewTopBottomPadding;
if (isRtl) {
mAppsRecyclerView.setPadding(padding.left + mAppsRecyclerView.getMaxScrollbarWidth(),
if (Utilities.isRtl(getResources())) {
mAppsRecyclerView.setPadding(padding.left + maxScrollBarWidth,
topBottomPadding, padding.right + startInset, topBottomPadding);
} else {
mAppsRecyclerView.setPadding(padding.left + startInset, topBottomPadding,
padding.right + mAppsRecyclerView.getMaxScrollbarWidth(), topBottomPadding);
padding.right + maxScrollBarWidth, topBottomPadding);
}
// Inset the search bar to fit its bounds above the container
if (mSearchBarView != null) {
Rect backgroundPadding = new Rect();
if (mSearchBarView.getBackground() != null) {
mSearchBarView.getBackground().getPadding(backgroundPadding);
}
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
mSearchBarContainerView.getLayoutParams();
lp.leftMargin = searchBarBounds.left - backgroundPadding.left;
lp.topMargin = searchBarBounds.top - backgroundPadding.top;
lp.rightMargin = (getMeasuredWidth() - searchBarBounds.right) - backgroundPadding.right;
mSearchBarContainerView.requestLayout();
}
MarginLayoutParams lp = (MarginLayoutParams) mSearchContainer.getLayoutParams();
lp.leftMargin = padding.left;
lp.rightMargin = padding.right;
mSearchContainer.setLayoutParams(lp);
}
@Override

View File

@@ -53,6 +53,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView
private AllAppsBackgroundDrawable mEmptySearchBackground;
private int mEmptySearchBackgroundTopOffset;
private HeaderElevationController mElevationController;
public AllAppsRecyclerView(Context context) {
this(context, null);
}
@@ -83,6 +85,10 @@ public class AllAppsRecyclerView extends BaseRecyclerView
mFastScrollHelper = new AllAppsFastScrollHelper(this, apps);
}
public void setElevationController(HeaderElevationController elevationController) {
mElevationController = elevationController;
}
/**
* Sets the number of apps per row in this recycler view.
*/
@@ -116,6 +122,9 @@ public class AllAppsRecyclerView extends BaseRecyclerView
mScrollbar.reattachThumbToScroll();
}
scrollToPosition(0);
if (mElevationController != null) {
mElevationController.reset();
}
}
/**

View File

@@ -15,65 +15,179 @@
*/
package com.android.launcher3.allapps;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Rect;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.ComponentKey;
import java.util.ArrayList;
import java.util.List;
/**
* An interface to a search box that AllApps can command.
*/
public abstract class AllAppsSearchBarController {
public abstract class AllAppsSearchBarController
implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener {
private static final boolean ALLOW_SINGLE_APP_LAUNCH = true;
protected AllAppsRecyclerView mAppsRecyclerView;
protected AlphabeticalAppsList mApps;
protected Callbacks mCb;
protected ExtendedEditText mInput;
protected DefaultAppSearchAlgorithm mSearchAlgorithm;
protected InputMethodManager mInputMethodManager;
/**
* Sets the references to the apps model and the search result callback.
*/
public final void initialize(AlphabeticalAppsList apps, Callbacks cb) {
public final void initialize(
AlphabeticalAppsList apps, ExtendedEditText input,
AllAppsRecyclerView recycleView, Callbacks cb) {
mApps = apps;
mCb = cb;
onInitialize();
mAppsRecyclerView = recycleView;
mInput = input;
mInput.addTextChangedListener(this);
mInput.setOnEditorActionListener(this);
mInput.setOnBackKeyListener(this);
mInputMethodManager = (InputMethodManager)
mInput.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
mSearchAlgorithm = onInitializeSearch();
}
/**
* To be overridden by subclasses. This method will get called when the controller is set,
* before getView().
* To be overridden by subclasses. This method will get called when the controller is set.
*/
protected abstract void onInitialize();
protected DefaultAppSearchAlgorithm onInitializeSearch() {
return null;
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Do nothing
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Do nothing
}
@Override
public void afterTextChanged(final Editable s) {
String query = s.toString();
if (query.isEmpty()) {
mSearchAlgorithm.cancel(true);
mCb.clearSearchResult();
} else {
mSearchAlgorithm.cancel(false);
mSearchAlgorithm.doSearch(query, mCb);
}
}
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
// Skip if we disallow app-launch-on-enter
if (!ALLOW_SINGLE_APP_LAUNCH) {
return false;
}
// Skip if it's not the right action
if (actionId != EditorInfo.IME_ACTION_SEARCH) {
return false;
}
// Skip if there are more than one icon
if (mApps.getNumFilteredApps() > 1) {
return false;
}
// 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);
switch (item.viewType) {
case AllAppsGridAdapter.ICON_VIEW_TYPE:
case AllAppsGridAdapter.SEARCH_MARKET_VIEW_TYPE:
mAppsRecyclerView.getChildAt(i).performClick();
mInputMethodManager.hideSoftInputFromWindow(mInput.getWindowToken(), 0);
return true;
}
}
return false;
}
@Override
public boolean onBackKey() {
// Only hide the search field if there is no query, or if there
// are no filtered results
String query = Utilities.trim(mInput.getEditableText().toString());
if (query.isEmpty() || mApps.hasNoFilteredResults()) {
reset();
return true;
}
return false;
}
/**
* Resets the search bar state.
*/
public void reset() {
unfocusSearchField();
mCb.clearSearchResult();
mInput.setText("");
mInputMethodManager.hideSoftInputFromWindow(mInput.getWindowToken(), 0);
}
protected void unfocusSearchField() {
View nextFocus = mInput.focusSearch(View.FOCUS_DOWN);
if (nextFocus != null) {
nextFocus.requestFocus();
}
}
/**
* Returns the search bar view.
* @param parent the parent to attach the search bar view to.
*/
public abstract View getView(ViewGroup parent);
public View getView(ViewGroup parent) {
return null;
}
/**
* Focuses the search field to handle key events.
*/
public abstract void focusSearchField();
public void focusSearchField() {
mInput.requestFocus();
mInputMethodManager.showSoftInput(mInput, InputMethodManager.SHOW_IMPLICIT);
}
/**
* Returns whether the search field is focused.
*/
public abstract boolean isSearchFieldFocused();
/**
* Resets the search bar state.
*/
public abstract void reset();
public boolean isSearchFieldFocused() {
return mInput.isFocused();
}
/**
* Returns whether the prediction bar should currently be visible depending on the state of
* the search bar.
*/
@Deprecated
public abstract boolean shouldShowPredictionBar();
public boolean shouldShowPredictionBar() {
return false;
}
/**
* Callback for getting search results.

View File

@@ -15,261 +15,12 @@
*/
package com.android.launcher3.allapps;
import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Thunk;
import java.util.List;
/**
* The default search controller.
*/
final class DefaultAppSearchController extends AllAppsSearchBarController
implements TextWatcher, TextView.OnEditorActionListener, View.OnClickListener {
public class DefaultAppSearchController extends AllAppsSearchBarController {
private static final boolean ALLOW_SINGLE_APP_LAUNCH = true;
private static final int FADE_IN_DURATION = 175;
private static final int FADE_OUT_DURATION = 100;
private static final int SEARCH_TRANSLATION_X_DP = 18;
private final Context mContext;
@Thunk final InputMethodManager mInputMethodManager;
private DefaultAppSearchAlgorithm mSearchManager;
private ViewGroup mContainerView;
private View mSearchView;
@Thunk View mSearchBarContainerView;
private View mSearchButtonView;
private View mDismissSearchButtonView;
@Thunk
ExtendedEditText mSearchBarEditView;
@Thunk AllAppsRecyclerView mAppsRecyclerView;
@Thunk Runnable mFocusRecyclerViewRunnable = new Runnable() {
@Override
public void run() {
mAppsRecyclerView.requestFocus();
}
};
public DefaultAppSearchController(Context context, ViewGroup containerView,
AllAppsRecyclerView appsRecyclerView) {
mContext = context;
mInputMethodManager = (InputMethodManager)
mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
mContainerView = containerView;
mAppsRecyclerView = appsRecyclerView;
}
@Override
public View getView(ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
mSearchView = inflater.inflate(R.layout.all_apps_search_bar, parent, false);
mSearchView.setOnClickListener(this);
mSearchButtonView = mSearchView.findViewById(R.id.search_button);
mSearchBarContainerView = mSearchView.findViewById(R.id.search_container);
mDismissSearchButtonView = mSearchBarContainerView.findViewById(R.id.dismiss_search_button);
mDismissSearchButtonView.setOnClickListener(this);
mSearchBarEditView = (ExtendedEditText)
mSearchBarContainerView.findViewById(R.id.search_box_input);
mSearchBarEditView.addTextChangedListener(this);
mSearchBarEditView.setOnEditorActionListener(this);
mSearchBarEditView.setOnBackKeyListener(
new ExtendedEditText.OnBackKeyListener() {
@Override
public boolean onBackKey() {
// Only hide the search field if there is no query, or if there
// are no filtered results
String query = Utilities.trim(
mSearchBarEditView.getEditableText().toString());
if (query.isEmpty() || mApps.hasNoFilteredResults()) {
hideSearchField(true, mFocusRecyclerViewRunnable);
return true;
}
return false;
}
});
return mSearchView;
}
@Override
public void focusSearchField() {
mSearchBarEditView.requestFocus();
showSearchField();
}
@Override
public boolean isSearchFieldFocused() {
return mSearchBarEditView.isFocused();
}
@Override
protected void onInitialize() {
mSearchManager = new DefaultAppSearchAlgorithm(mApps.getApps());
}
@Override
public void reset() {
hideSearchField(false, null);
}
@Override
public boolean shouldShowPredictionBar() {
return false;
}
@Override
public void onClick(View v) {
if (v == mSearchView) {
showSearchField();
} else if (v == mDismissSearchButtonView) {
hideSearchField(true, mFocusRecyclerViewRunnable);
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Do nothing
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Do nothing
}
@Override
public void afterTextChanged(final Editable s) {
String query = s.toString();
if (query.isEmpty()) {
mSearchManager.cancel(true);
mCb.clearSearchResult();
} else {
mSearchManager.cancel(false);
mSearchManager.doSearch(query, mCb);
}
}
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
// Skip if we disallow app-launch-on-enter
if (!ALLOW_SINGLE_APP_LAUNCH) {
return false;
}
// Skip if it's not the right action
if (actionId != EditorInfo.IME_ACTION_SEARCH) {
return false;
}
// Skip if there are more than one icon
if (mApps.getNumFilteredApps() > 1) {
return false;
}
// 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);
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;
}
/**
* Focuses the search field.
*/
private void showSearchField() {
// Show the search bar and focus the search
final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP,
mContext.getResources().getDisplayMetrics());
mSearchBarContainerView.setVisibility(View.VISIBLE);
mSearchBarContainerView.setAlpha(0f);
mSearchBarContainerView.setTranslationX(translationX);
mSearchBarContainerView.animate()
.alpha(1f)
.translationX(0)
.setDuration(FADE_IN_DURATION)
.withLayer()
.withEndAction(new Runnable() {
@Override
public void run() {
mSearchBarEditView.requestFocus();
mInputMethodManager.showSoftInput(mSearchBarEditView,
InputMethodManager.SHOW_IMPLICIT);
}
});
mSearchButtonView.animate()
.alpha(0f)
.translationX(-translationX)
.setDuration(FADE_OUT_DURATION)
.withLayer();
}
/**
* Unfocuses the search field.
*/
@Thunk void hideSearchField(boolean animated, final Runnable postAnimationRunnable) {
mSearchManager.cancel(true);
final boolean resetTextField = mSearchBarEditView.getText().toString().length() > 0;
final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP,
mContext.getResources().getDisplayMetrics());
if (animated) {
// Hide the search bar and focus the recycler view
mSearchBarContainerView.animate()
.alpha(0f)
.translationX(0)
.setDuration(FADE_IN_DURATION)
.withLayer()
.withEndAction(new Runnable() {
@Override
public void run() {
mSearchBarContainerView.setVisibility(View.INVISIBLE);
if (resetTextField) {
mSearchBarEditView.setText("");
}
mCb.clearSearchResult();
if (postAnimationRunnable != null) {
postAnimationRunnable.run();
}
}
});
mSearchButtonView.setTranslationX(-translationX);
mSearchButtonView.animate()
.alpha(1f)
.translationX(0)
.setDuration(FADE_OUT_DURATION)
.withLayer();
} else {
mSearchBarContainerView.setVisibility(View.INVISIBLE);
if (resetTextField) {
mSearchBarEditView.setText("");
}
mCb.clearSearchResult();
mSearchButtonView.setAlpha(1f);
mSearchButtonView.setTranslationX(0f);
if (postAnimationRunnable != null) {
postAnimationRunnable.run();
}
}
mInputMethodManager.hideSoftInputFromWindow(mContainerView.getWindowToken(), 0);
public DefaultAppSearchAlgorithm onInitializeSearch() {
return new DefaultAppSearchAlgorithm(mApps.getApps());
}
}

View File

@@ -0,0 +1,101 @@
package com.android.launcher3.allapps;
import android.annotation.TargetApi;
import android.content.res.Resources;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.widget.FrameLayout;
import com.android.launcher3.R;
/**
* Helper class for controlling the header elevation in response to RecyclerView scroll.
*/
public abstract class HeaderElevationController extends RecyclerView.OnScrollListener {
private int mCurrentY = 0;
public void reset() {
mCurrentY = 0;
onScroll(mCurrentY);
}
@Override
public final void onScrolled(RecyclerView recyclerView, int dx, int dy) {
mCurrentY += dy;
onScroll(mCurrentY);
}
public void updateBackgroundPadding(Rect bgPadding) { }
abstract void onScroll(int scrollY);
public static class ControllerV16 extends HeaderElevationController {
private final View mShadow;
private final float mScrollToElevation;
public ControllerV16(View header) {
Resources res = header.getContext().getResources();
mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation);
mShadow = new View(header.getContext());
mShadow.setBackground(new GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM, new int[] {0x1E000000, 0x00000000}));
mShadow.setAlpha(0);
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
res.getDimensionPixelSize(R.dimen.all_apps_header_shadow_height));
lp.topMargin = ((FrameLayout.LayoutParams) header.getLayoutParams()).height;
((ViewGroup) header.getParent()).addView(mShadow, lp);
}
@Override
public void onScroll(int scrollY) {
float elevationPct = (float) Math.min(scrollY, mScrollToElevation) /
mScrollToElevation;
mShadow.setAlpha(elevationPct);
}
@Override
public void updateBackgroundPadding(Rect bgPadding) {
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mShadow.getLayoutParams();
lp.leftMargin = bgPadding.left;
lp.rightMargin = bgPadding.right;
mShadow.requestLayout();
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static class ControllerVL extends HeaderElevationController {
private final View mHeader;
private final float mMaxElevation;
private final float mScrollToElevation;
public ControllerVL(View header) {
mHeader = header;
mHeader.setOutlineProvider(ViewOutlineProvider.BOUNDS);
Resources res = header.getContext().getResources();
mMaxElevation = res.getDimension(R.dimen.all_apps_header_max_elevation);
mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation);
}
@Override
public void onScroll(int scrollY) {
float elevationPct = Math.min(scrollY, mScrollToElevation) / mScrollToElevation;
float newElevation = mMaxElevation * elevationPct;
if (Float.compare(mHeader.getElevation(), newElevation) != 0) {
mHeader.setElevation(newElevation);
}
}
}
}

View File

@@ -65,7 +65,6 @@ public class WidgetsContainerView extends BaseContainerView
private IconCache mIconCache;
/* Recycler view related member variables */
private View mContent;
private WidgetsRecyclerView mView;
private WidgetsListAdapter mAdapter;
@@ -99,8 +98,7 @@ public class WidgetsContainerView extends BaseContainerView
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mContent = findViewById(R.id.content);
mView = (WidgetsRecyclerView) findViewById(R.id.widgets_list_view);
mView = (WidgetsRecyclerView) getContentView();
mView.setAdapter(mAdapter);
// This extends the layout space so that preloading happen for the {@link RecyclerView}
@@ -120,15 +118,6 @@ public class WidgetsContainerView extends BaseContainerView
// Returns views used for launcher transitions.
//
public View getContentView() {
return mView;
}
public View getRevealView() {
// TODO(hyunyoungs): temporarily use apps view transition.
return findViewById(R.id.widgets_reveal_view);
}
public void scrollToTop() {
mView.scrollToPosition(0);
}
@@ -338,21 +327,8 @@ public class WidgetsContainerView extends BaseContainerView
//
// Container rendering related.
//
@Override
protected void onUpdateBackgroundAndPaddings(Rect searchBarBounds, Rect padding) {
// Apply the top-bottom padding to the content itself so that the launcher transition is
// clipped correctly
mContent.setPadding(0, padding.top, 0, padding.bottom);
// TODO: Use quantum_panel_dark instead of quantum_panel_shape_dark.
InsetDrawable background = new InsetDrawable(
getResources().getDrawable(R.drawable.quantum_panel_shape_dark), padding.left, 0,
padding.right, 0);
Rect bgPadding = new Rect();
background.getPadding(bgPadding);
mView.setBackground(background);
getRevealView().setBackground(background.getConstantState().newDrawable());
protected void onUpdateBgPadding(Rect padding, Rect bgPadding) {
mView.updateBackgroundPadding(bgPadding);
}