Refactoring all apps search to support external search bar.
- Adding support for an external search bar that can be used to search a container view. This adds a new interface AllAppsSearchController which manages the external search bar. Each controller will have its own search implementation which means that we no longer need a common AppSearchManager interface. - Removing elevation controller as we no longer have a builtin search bar in all apps - Refactoring container view insets so that they behave the same in all containers. - Refactoring apps view to ensure that we only update the number of columns with the available width - Cleaning up LauncherCallbacks interface Bug: 20127840 Bug: 21494973 Change-Id: I710b8e18196961d77d8a29f0c345531d480936fe
This commit is contained in:
parent
e89cf793ab
commit
ef7f874a88
|
@ -14,10 +14,7 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/quantum_panel_bg_color" />
|
||||
<corners
|
||||
android:topLeftRadius="2dp"
|
||||
android:topRightRadius="2dp" />
|
||||
</shape>
|
||||
<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" />
|
|
@ -1,33 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2015 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<com.android.launcher3.allapps.AllAppsContainerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/apps_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="@dimen/all_apps_container_inset"
|
||||
android:descendantFocusability="afterDescendants">
|
||||
<include
|
||||
layout="@layout/all_apps_reveal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center" />
|
||||
<include
|
||||
layout="@layout/all_apps_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center" />
|
||||
</com.android.launcher3.allapps.AllAppsContainerView>
|
|
@ -21,15 +21,37 @@
|
|||
android:id="@+id/apps_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:descendantFocusability="afterDescendants">
|
||||
<include
|
||||
layout="@layout/all_apps_reveal"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- Both android:focusable and android:focusableInTouchMode are needed for
|
||||
the view to get focus change events. -->
|
||||
<FrameLayout
|
||||
android:id="@+id/search_box_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center" />
|
||||
<include
|
||||
layout="@layout/all_apps_container"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true"
|
||||
android:visibility="gone" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center" />
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
<FrameLayout
|
||||
android:id="@+id/all_apps_reveal"
|
||||
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_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone" />
|
||||
</FrameLayout>
|
||||
</com.android.launcher3.allapps.AllAppsContainerView>
|
|
@ -14,95 +14,35 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<!-- Both android:focusable and android:focusableInTouchMode are needed for
|
||||
the view to get focus change events. -->
|
||||
<com.android.launcher3.allapps.AllAppsRecyclerViewContainerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/apps_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:elevation="15dp"
|
||||
android:focusableInTouchMode="true"
|
||||
android:visibility="gone" >
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true">
|
||||
|
||||
<com.android.launcher3.allapps.AllAppsRecyclerView
|
||||
android:id="@+id/apps_list_view"
|
||||
android:id="@+id/collection"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center_horizontal|top"
|
||||
android:layout_marginTop="@dimen/all_apps_search_bar_height"
|
||||
android:clipToPadding="false"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:focusable="true" />
|
||||
android:focusable="true"
|
||||
android:descendantFocusability="afterDescendants" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/prediction_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/all_apps_search_bar_height"
|
||||
android:paddingBottom="@dimen/all_apps_prediction_bar_bottom_padding"
|
||||
android:paddingTop="@dimen/all_apps_prediction_bar_top_bottom_padding"
|
||||
android:paddingBottom="@dimen/all_apps_prediction_bar_top_bottom_padding"
|
||||
android:orientation="horizontal"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:focusable="true"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:visibility="invisible" >
|
||||
</LinearLayout>
|
||||
|
||||
<!-- We always want the search bar on top, so it goes last. -->
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/all_apps_search_bar_height"
|
||||
android:background="@drawable/all_apps_search_bg" >
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/app_search_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="invisible" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/dismiss_search_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:contentDescription="@string/all_apps_button_label"
|
||||
android:paddingBottom="13dp"
|
||||
android:paddingTop="13dp"
|
||||
android:src="@drawable/ic_arrow_back_grey" />
|
||||
|
||||
<com.android.launcher3.allapps.AllAppsSearchEditView
|
||||
android:id="@+id/apps_search_box"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/transparent"
|
||||
android:focusableInTouchMode="true"
|
||||
android:gravity="fill_horizontal"
|
||||
android:hint="@string/all_apps_search_bar_hint"
|
||||
android:imeOptions="actionDone|flagNoExtractUi"
|
||||
android:maxLines="1"
|
||||
android:paddingBottom="16dp"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingTop="16dp"
|
||||
android:scrollHorizontally="true"
|
||||
android:singleLine="true"
|
||||
android:textColor="#4c4c4c"
|
||||
android:textColorHint="#9c9c9c"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/search_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:layout_marginRight="6dp"
|
||||
android:contentDescription="@string/all_apps_search_bar_hint"
|
||||
android:paddingBottom="13dp"
|
||||
android:paddingTop="13dp"
|
||||
android:src="@drawable/ic_search_grey" />
|
||||
</FrameLayout>
|
||||
|
||||
</com.android.launcher3.allapps.AllAppsRecyclerViewContainerView>
|
|
@ -1,24 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2015 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/apps_view_transition_overlay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:elevation="2dp"
|
||||
android:visibility="invisible"
|
||||
android:focusable="false" />
|
|
@ -0,0 +1,72 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2015 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/all_apps_search_bg" >
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/search_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/all_apps_search_bar_height"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="invisible" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/dismiss_search_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:contentDescription="@string/all_apps_button_label"
|
||||
android:paddingBottom="13dp"
|
||||
android:paddingTop="13dp"
|
||||
android:src="@drawable/ic_arrow_back_grey" />
|
||||
|
||||
<com.android.launcher3.allapps.AllAppsSearchEditView
|
||||
android:id="@+id/search_box"
|
||||
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="actionDone|flagNoExtractUi"
|
||||
android:maxLines="1"
|
||||
android:paddingLeft="8dp"
|
||||
android:scrollHorizontally="true"
|
||||
android:singleLine="true"
|
||||
android:textColor="#4c4c4c"
|
||||
android:textColorHint="#9c9c9c"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/search_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/all_apps_search_bar_height"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:layout_marginRight="6dp"
|
||||
android:contentDescription="@string/all_apps_search_bar_hint"
|
||||
android:paddingBottom="13dp"
|
||||
android:paddingTop="13dp"
|
||||
android:src="@drawable/ic_search_grey" />
|
||||
</FrameLayout>
|
|
@ -21,25 +21,29 @@
|
|||
android:id="@+id/widgets_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="@dimen/widget_container_inset"
|
||||
android:paddingBottom="@dimen/widget_container_inset"
|
||||
android:orientation="vertical"
|
||||
android:descendantFocusability="afterDescendants">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/widgets_reveal_view"
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:elevation="2dp"
|
||||
android:focusable="false"
|
||||
android:visibility="invisible" />
|
||||
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" />
|
||||
|
||||
<com.android.launcher3.widget.WidgetsRecyclerView
|
||||
android:id="@+id/widgets_list_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/quantum_panel_dark"
|
||||
android:layout_gravity="center"
|
||||
android:elevation="15dp"
|
||||
android:visibility="gone" />
|
||||
</FrameLayout>
|
||||
|
||||
</com.android.launcher3.widget.WidgetsContainerView>
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
<resources>
|
||||
<!-- All Apps -->
|
||||
<dimen name="all_apps_container_inset">18dp</dimen>
|
||||
<dimen name="all_apps_grid_view_start_margin">0dp</dimen>
|
||||
<dimen name="all_apps_grid_section_text_size">26sp</dimen>
|
||||
<dimen name="all_apps_icon_top_bottom_padding">12dp</dimen>
|
||||
|
|
|
@ -51,17 +51,18 @@
|
|||
|
||||
<!-- All Apps -->
|
||||
<!-- Note: This needs to match the fixed insets for the search box. -->
|
||||
<dimen name="container_fixed_bounds_inset">8dp</dimen>
|
||||
<dimen name="container_bounds_inset">8dp</dimen>
|
||||
<!-- Notes: container_bounds_inset - quantum_panel_outer_padding -->
|
||||
<dimen name="container_bounds_minus_quantum_panel_padding_inset">4dp</dimen>
|
||||
|
||||
<dimen name="all_apps_container_inset">8dp</dimen>
|
||||
<dimen name="all_apps_grid_view_start_margin">56dp</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">52dp</dimen>
|
||||
<dimen name="all_apps_search_bar_height">48dp</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>
|
||||
<dimen name="all_apps_prediction_bar_bottom_padding">16dp</dimen>
|
||||
<dimen name="all_apps_prediction_bar_top_bottom_padding">16dp</dimen>
|
||||
|
||||
<dimen name="all_apps_fast_scroll_bar_width">4dp</dimen>
|
||||
<dimen name="all_apps_fast_scroll_scrubber_touch_inset">-16dp</dimen>
|
||||
|
|
|
@ -19,16 +19,25 @@ package com.android.launcher3;
|
|||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
/**
|
||||
* A base container view, which supports resizing.
|
||||
*/
|
||||
public class BaseContainerView extends FrameLayout implements Insettable {
|
||||
public abstract class BaseContainerView extends LinearLayout implements Insettable {
|
||||
|
||||
protected Rect mInsets = new Rect();
|
||||
protected Rect mFixedBounds = new Rect();
|
||||
protected int mFixedBoundsContainerInset;
|
||||
// The window insets
|
||||
private 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 mSearchBarBounds = new Rect();
|
||||
// The bounds of the container
|
||||
protected Rect mContentBounds = new Rect();
|
||||
// The padding to apply to the container to achieve the bounds
|
||||
protected 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;
|
||||
|
||||
public BaseContainerView(Context context) {
|
||||
this(context, null);
|
||||
|
@ -40,62 +49,73 @@ public class BaseContainerView extends FrameLayout implements Insettable {
|
|||
|
||||
public BaseContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mFixedBoundsContainerInset = context.getResources().getDimensionPixelSize(
|
||||
R.dimen.container_fixed_bounds_inset);
|
||||
mContainerBoundsInset = getResources().getDimensionPixelSize(R.dimen.container_bounds_inset);
|
||||
}
|
||||
|
||||
@Override
|
||||
final public void setInsets(Rect insets) {
|
||||
mInsets.set(insets);
|
||||
onUpdateBackgrounds();
|
||||
onUpdatePaddings();
|
||||
updateBackgroundAndPaddings();
|
||||
}
|
||||
|
||||
protected void setHasSearchBar() {
|
||||
mHasSearchBar = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the fixed bounds for this container view.
|
||||
* Sets the search bar bounds for this container view to match.
|
||||
*/
|
||||
final public void setFixedBounds(Rect fixedBounds) {
|
||||
if (!fixedBounds.isEmpty() && !fixedBounds.equals(mFixedBounds)) {
|
||||
mFixedBounds.set(fixedBounds);
|
||||
if (Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) {
|
||||
mFixedBounds.top = mInsets.top;
|
||||
mFixedBounds.bottom = mInsets.bottom;
|
||||
}
|
||||
// To ensure that the child RecyclerView has the full width to handle touches right to
|
||||
// the edge of the screen, we only apply the top and bottom padding to the bounds
|
||||
mFixedBounds.top += mFixedBoundsContainerInset;
|
||||
mFixedBounds.bottom += mFixedBoundsContainerInset;
|
||||
onFixedBoundsUpdated();
|
||||
}
|
||||
final public void setSearchBarBounds(Rect bounds) {
|
||||
mSearchBarBounds.set(bounds);
|
||||
|
||||
// Post the updates since they can trigger a relayout, and this call can be triggered from
|
||||
// a layout pass itself.
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
onUpdateBackgrounds();
|
||||
onUpdatePaddings();
|
||||
updateBackgroundAndPaddings();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the UI in response to a change in the fixed bounds.
|
||||
* Update the backgrounds and padding in response to a change in the bounds or insets.
|
||||
*/
|
||||
protected void onFixedBoundsUpdated() {
|
||||
// Do nothing
|
||||
protected void updateBackgroundAndPaddings() {
|
||||
Rect padding;
|
||||
Rect searchBarBounds = new Rect(mSearchBarBounds);
|
||||
if (mSearchBarBounds.isEmpty()) {
|
||||
// Use the normal 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,
|
||||
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(mSearchBarBounds.left,
|
||||
(mHasSearchBar ? 0 : (mInsets.top + mContainerBoundsInset)),
|
||||
getMeasuredWidth() - mSearchBarBounds.right,
|
||||
mInsets.bottom + mContainerBoundsInset);
|
||||
}
|
||||
if (!padding.equals(mContentPadding) || !searchBarBounds.equals(mSearchBarBounds)) {
|
||||
mContentPadding.set(padding);
|
||||
mContentBounds.set(padding.left, padding.top,
|
||||
getMeasuredWidth() - padding.right,
|
||||
getMeasuredHeight() - padding.bottom);
|
||||
mSearchBarBounds.set(searchBarBounds);
|
||||
onUpdateBackgroundAndPaddings(mSearchBarBounds, padding);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the paddings in response to a change in the bounds or insets.
|
||||
* To be implemented by container views to update themselves when the bounds changes.
|
||||
*/
|
||||
protected void onUpdatePaddings() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the backgrounds in response to a change in the bounds or insets.
|
||||
*/
|
||||
protected void onUpdateBackgrounds() {
|
||||
// Do nothing
|
||||
}
|
||||
protected abstract void onUpdateBackgroundAndPaddings(Rect searchBarBounds, Rect padding);
|
||||
}
|
|
@ -89,9 +89,7 @@ public class BaseRecyclerView extends RecyclerView
|
|||
private int mLastY;
|
||||
private int mScrollbarWidth;
|
||||
private int mScrollbarInset;
|
||||
private Rect mBackgroundPadding = new Rect();
|
||||
|
||||
|
||||
protected Rect mBackgroundPadding = new Rect();
|
||||
|
||||
public BaseRecyclerView(Context context) {
|
||||
this(context, null);
|
||||
|
@ -230,6 +228,10 @@ public class BaseRecyclerView extends RecyclerView
|
|||
return false;
|
||||
}
|
||||
|
||||
public void updateBackgroundPadding(Rect padding) {
|
||||
mBackgroundPadding.set(padding);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
super.dispatchDraw(canvas);
|
||||
|
@ -340,9 +342,10 @@ public class BaseRecyclerView extends RecyclerView
|
|||
// Calculate the position for the fast scroller popup
|
||||
Rect bgBounds = mFastScrollerBg.getBounds();
|
||||
if (Utilities.isRtl(getResources())) {
|
||||
x = mBackgroundPadding.left + getScrollBarSize();
|
||||
x = mBackgroundPadding.left + (2 * getScrollbarWidth());
|
||||
} else {
|
||||
x = getWidth() - getPaddingRight() - getScrollBarSize() - bgBounds.width();
|
||||
x = getWidth() - mBackgroundPadding.right - (2 * getScrollbarWidth()) -
|
||||
bgBounds.width();
|
||||
}
|
||||
y = mLastY - (int) (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * bgBounds.height());
|
||||
y = Math.max(getPaddingTop(), Math.min(y, getHeight() - getPaddingBottom() -
|
||||
|
|
|
@ -223,26 +223,22 @@ public class DeviceProfile {
|
|||
folderCellHeightPx = cellHeightPx + edgeMarginPx;
|
||||
folderBackgroundOffset = -edgeMarginPx;
|
||||
folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
|
||||
|
||||
updateAppsViewNumCols(res, 0);
|
||||
}
|
||||
|
||||
public boolean updateAppsViewNumCols(Resources res, int containerWidth) {
|
||||
/**
|
||||
* @param recyclerViewWidth the available width of the AllAppsRecyclerView
|
||||
*/
|
||||
public void updateAppsViewNumCols(Resources res, int recyclerViewWidth) {
|
||||
int appsViewLeftMarginPx =
|
||||
res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
|
||||
int allAppsCellWidthGap =
|
||||
res.getDimensionPixelSize(R.dimen.all_apps_icon_width_gap);
|
||||
int availableAppsWidthPx = (containerWidth > 0) ? containerWidth : availableWidthPx;
|
||||
int availableAppsWidthPx = (recyclerViewWidth > 0) ? recyclerViewWidth : availableWidthPx;
|
||||
int numAppsCols = (availableAppsWidthPx - appsViewLeftMarginPx) /
|
||||
(allAppsIconSizePx + allAppsCellWidthGap);
|
||||
int numPredictiveAppCols = Math.max(inv.minAllAppsPredictionColumns, numAppsCols);
|
||||
if ((numAppsCols != allAppsNumCols) ||
|
||||
(numPredictiveAppCols != allAppsNumPredictiveCols)) {
|
||||
allAppsNumCols = numAppsCols;
|
||||
allAppsNumPredictiveCols = numPredictiveAppCols;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
allAppsNumCols = numAppsCols;
|
||||
allAppsNumPredictiveCols = numPredictiveAppCols;
|
||||
}
|
||||
|
||||
/** Returns the search bar top offset */
|
||||
|
|
|
@ -99,7 +99,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.AppSearchManager;
|
||||
import com.android.launcher3.allapps.AllAppsSearchBarController;
|
||||
import com.android.launcher3.compat.AppWidgetManagerCompat;
|
||||
import com.android.launcher3.compat.LauncherActivityInfoCompat;
|
||||
import com.android.launcher3.compat.LauncherAppsCompat;
|
||||
|
@ -138,9 +138,6 @@ public class Launcher extends Activity
|
|||
static final String TAG = "Launcher";
|
||||
static final boolean LOGD = false;
|
||||
|
||||
// Temporary flag
|
||||
static final boolean DISABLE_ALL_APPS_SEARCH_INTEGRATION = true;
|
||||
|
||||
static final boolean PROFILE_STARTUP = false;
|
||||
static final boolean DEBUG_WIDGETS = true;
|
||||
static final boolean DEBUG_STRICT_MODE = false;
|
||||
|
@ -573,32 +570,6 @@ public class Launcher extends Activity
|
|||
|
||||
public boolean setLauncherCallbacks(LauncherCallbacks callbacks) {
|
||||
mLauncherCallbacks = callbacks;
|
||||
mLauncherCallbacks.setLauncherAppsCallback(new Launcher.LauncherAppsCallbacks() {
|
||||
@Override
|
||||
public void onAllAppsBoundsChanged(Rect bounds) {
|
||||
if (LOGD) {
|
||||
Log.d(TAG, "onAllAppsBoundsChanged(Rect): " + bounds);
|
||||
}
|
||||
mAppsView.setFixedBounds(bounds);
|
||||
mWidgetsView.setFixedBounds(bounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismissAllApps() {
|
||||
if (!DISABLE_ALL_APPS_SEARCH_INTEGRATION) {
|
||||
// Dismiss All Apps if we aren't already paused/invisible
|
||||
if (!mPaused) {
|
||||
showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, true,
|
||||
null /* onCompleteRunnable */, false /* notifyLauncherCallbacks */);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSearchManager(AppSearchManager manager) {
|
||||
mAppsView.setSearchManager(manager);
|
||||
}
|
||||
});
|
||||
mLauncherCallbacks.setLauncherSearchCallback(new Launcher.LauncherSearchCallbacks() {
|
||||
private boolean mImportanceStored = false;
|
||||
private int mWorkspaceImportanceForAccessibility =
|
||||
|
@ -638,6 +609,14 @@ public class Launcher extends Activity
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the bounds of all the overlays to match the new fixed bounds.
|
||||
*/
|
||||
public void updateOverlayBounds(Rect newBounds) {
|
||||
mAppsView.setSearchBarBounds(newBounds);
|
||||
mWidgetsView.setSearchBarBounds(newBounds);
|
||||
}
|
||||
|
||||
/** To be overridden by subclasses to hint to Launcher that we have custom content */
|
||||
protected boolean hasCustomContentToLeft() {
|
||||
if (mLauncherCallbacks != null) {
|
||||
|
@ -1012,16 +991,6 @@ public class Launcher extends Activity
|
|||
}
|
||||
mOnResumeState = State.NONE;
|
||||
|
||||
// Restore the apps state if we are in all apps
|
||||
if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) {
|
||||
// Otherwise, notify the callbacks if we are in all apps mode
|
||||
if (mState == State.APPS) {
|
||||
if (mLauncherCallbacks != null) {
|
||||
mLauncherCallbacks.onAllAppsShown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Background was set to gradient in onPause(), restore to transparent if in all apps.
|
||||
setWorkspaceBackground(mState == State.WORKSPACE ? WORKSPACE_BACKGROUND_GRADIENT
|
||||
: WORKSPACE_BACKGROUND_TRANSPARENT);
|
||||
|
@ -1167,17 +1136,20 @@ public class Launcher extends Activity
|
|||
* Updates launcher to the available space that AllApps can take so as not to overlap with
|
||||
* any other views.
|
||||
*/
|
||||
@Deprecated
|
||||
public void onAllAppsBoundsChanged(Rect bounds);
|
||||
|
||||
/**
|
||||
* Called to dismiss all apps if it is showing.
|
||||
*/
|
||||
@Deprecated
|
||||
public void dismissAllApps();
|
||||
|
||||
/**
|
||||
* Sets the search manager to be used for app search.
|
||||
*/
|
||||
public void setSearchManager(AppSearchManager manager);
|
||||
@Deprecated
|
||||
public void setSearchManager(Object manager);
|
||||
}
|
||||
|
||||
public interface LauncherSearchCallbacks {
|
||||
|
@ -1463,14 +1435,14 @@ public class Launcher extends Activity
|
|||
mSearchDropTargetBar = (SearchDropTargetBar)
|
||||
mDragLayer.findViewById(R.id.search_drop_target_bar);
|
||||
|
||||
// Setup Apps
|
||||
// Setup Apps and Widgets
|
||||
mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view);
|
||||
if (isAllAppsSearchOverridden()) {
|
||||
mAppsView.hideHeaderBar();
|
||||
}
|
||||
|
||||
// Setup AppsCustomize
|
||||
mWidgetsView = (WidgetsContainerView) findViewById(R.id.widgets_view);
|
||||
if (mLauncherCallbacks != null && mLauncherCallbacks.getAllAppsSearchBarController() != null) {
|
||||
mAppsView.setSearchBarController(mLauncherCallbacks.getAllAppsSearchBarController());
|
||||
} else {
|
||||
mAppsView.setSearchBarController(mAppsView.newDefaultAppSearchController());
|
||||
}
|
||||
|
||||
// Setup the drag controller (drop targets have to be added in reverse order in priority)
|
||||
dragController.setDragScoller(mWorkspace);
|
||||
|
@ -2866,17 +2838,8 @@ public class Launcher extends Activity
|
|||
public void updateInteraction(Workspace.State fromState, Workspace.State toState) {
|
||||
// Only update the interacting state if we are transitioning to/from a view with an
|
||||
// overlay
|
||||
boolean fromStateWithOverlay;
|
||||
boolean toStateWithOverlay;
|
||||
if (Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION) {
|
||||
fromStateWithOverlay = fromState != Workspace.State.NORMAL;
|
||||
toStateWithOverlay = toState != Workspace.State.NORMAL;
|
||||
} else {
|
||||
fromStateWithOverlay = fromState != Workspace.State.NORMAL &&
|
||||
fromState != Workspace.State.NORMAL_HIDDEN;
|
||||
toStateWithOverlay = toState != Workspace.State.NORMAL &&
|
||||
toState != Workspace.State.NORMAL_HIDDEN;
|
||||
}
|
||||
boolean fromStateWithOverlay = fromState != Workspace.State.NORMAL;
|
||||
boolean toStateWithOverlay = toState != Workspace.State.NORMAL;
|
||||
if (toStateWithOverlay) {
|
||||
onInteractionBegin();
|
||||
} else if (fromStateWithOverlay) {
|
||||
|
@ -3320,21 +3283,19 @@ public class Launcher extends Activity
|
|||
}
|
||||
|
||||
public void showWorkspace(boolean animated) {
|
||||
showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, null,
|
||||
true);
|
||||
showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, null);
|
||||
}
|
||||
|
||||
public void showWorkspace(boolean animated, Runnable onCompleteRunnable) {
|
||||
showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated,
|
||||
onCompleteRunnable, true);
|
||||
onCompleteRunnable);
|
||||
}
|
||||
|
||||
protected void showWorkspace(int snapToPage, boolean animated) {
|
||||
showWorkspace(snapToPage, animated, null, true);
|
||||
showWorkspace(snapToPage, animated, null);
|
||||
}
|
||||
|
||||
void showWorkspace(int snapToPage, boolean animated, Runnable onCompleteRunnable,
|
||||
boolean notifyLauncherCallbacks) {
|
||||
void showWorkspace(int snapToPage, boolean animated, Runnable onCompleteRunnable) {
|
||||
boolean changed = mState != State.WORKSPACE ||
|
||||
mWorkspace.getState() != Workspace.State.NORMAL;
|
||||
if (changed) {
|
||||
|
@ -3366,12 +3327,6 @@ public class Launcher extends Activity
|
|||
// Send an accessibility event to announce the context change
|
||||
getWindow().getDecorView()
|
||||
.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
|
||||
if (notifyLauncherCallbacks) {
|
||||
// Dismiss all apps when the workspace is shown
|
||||
if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION && mLauncherCallbacks != null) {
|
||||
mLauncherCallbacks.onAllAppsHidden();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3431,10 +3386,7 @@ public class Launcher extends Activity
|
|||
}
|
||||
|
||||
if (toState == State.APPS) {
|
||||
mStateTransitionAnimation.startAnimationToAllApps(animated);
|
||||
if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION && mLauncherCallbacks != null) {
|
||||
mLauncherCallbacks.onAllAppsShown();
|
||||
}
|
||||
mStateTransitionAnimation.startAnimationToAllApps(mState, animated);
|
||||
} else {
|
||||
mStateTransitionAnimation.startAnimationToWidgets(animated);
|
||||
}
|
||||
|
@ -3458,9 +3410,10 @@ public class Launcher extends Activity
|
|||
* new state.
|
||||
*/
|
||||
public Animator startWorkspaceStateChangeAnimation(Workspace.State toState, int toPage,
|
||||
boolean animated, HashMap<View, Integer> layerViews) {
|
||||
boolean animated, boolean hasOverlaySearchBar, HashMap<View, Integer> layerViews) {
|
||||
Workspace.State fromState = mWorkspace.getState();
|
||||
Animator anim = mWorkspace.setStateWithAnimation(toState, toPage, animated, layerViews);
|
||||
Animator anim = mWorkspace.setStateWithAnimation(toState, toPage, animated,
|
||||
hasOverlaySearchBar, layerViews);
|
||||
updateInteraction(fromState, toState);
|
||||
return anim;
|
||||
}
|
||||
|
@ -3482,14 +3435,6 @@ public class Launcher extends Activity
|
|||
final Runnable onCompleteRunnable) {
|
||||
if (mState != State.APPS_SPRING_LOADED && mState != State.WIDGETS_SPRING_LOADED) return;
|
||||
|
||||
if (successfulDrop) {
|
||||
// We need to trigger all apps hidden to notify search to update itself before the
|
||||
// delayed call to showWorkspace below
|
||||
if (!Launcher.DISABLE_ALL_APPS_SEARCH_INTEGRATION && mLauncherCallbacks != null) {
|
||||
mLauncherCallbacks.onAllAppsHidden();
|
||||
}
|
||||
}
|
||||
|
||||
mHandler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -4473,20 +4418,6 @@ public class Launcher extends Activity
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the launcher callbacks overrides search in all apps.
|
||||
*/
|
||||
@Thunk boolean isAllAppsSearchOverridden() {
|
||||
if (DISABLE_ALL_APPS_SEARCH_INTEGRATION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mLauncherCallbacks != null) {
|
||||
return mLauncherCallbacks.overrideAllAppsSearch();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean shouldRunFirstRunActivity() {
|
||||
return !ActivityManager.isRunningInTestHarness() &&
|
||||
!mSharedPrefs.getBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, false);
|
||||
|
|
|
@ -7,6 +7,7 @@ import android.os.Bundle;
|
|||
import android.view.Menu;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import com.android.launcher3.allapps.AllAppsSearchBarController;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
|
@ -51,12 +52,9 @@ public interface LauncherCallbacks {
|
|||
public void onLauncherProviderChange();
|
||||
public void finishBindingItems(final boolean upgradePath);
|
||||
public void onClickAllAppsButton(View v);
|
||||
public void onAllAppsShown();
|
||||
public void onAllAppsHidden();
|
||||
public void bindAllApplications(ArrayList<AppInfo> apps);
|
||||
public void onClickFolderIcon(View v);
|
||||
public void onClickAppShortcut(View v);
|
||||
|
||||
@Deprecated
|
||||
public void onClickPagedViewIcon(View v);
|
||||
public void onClickWallpaperPicker(View v);
|
||||
|
@ -89,10 +87,11 @@ public interface LauncherCallbacks {
|
|||
public View getIntroScreen();
|
||||
public boolean shouldMoveToDefaultScreenOnHomeIntent();
|
||||
public boolean hasSettings();
|
||||
@Deprecated
|
||||
public ComponentName getWallpaperPickerComponent();
|
||||
public boolean overrideWallpaperDimensions();
|
||||
public boolean isLauncherPreinstalled();
|
||||
public boolean overrideAllAppsSearch();
|
||||
public AllAppsSearchBarController getAllAppsSearchBarController();
|
||||
public List<ComponentName> getPredictedApps();
|
||||
|
||||
/**
|
||||
|
@ -113,14 +112,6 @@ public interface LauncherCallbacks {
|
|||
public Launcher.LauncherOverlay setLauncherOverlayView(InsettableFrameLayout container,
|
||||
Launcher.LauncherOverlayCallbacks callbacks);
|
||||
|
||||
/**
|
||||
* Sets the callbacks to allow any extensions to callback to the launcher.
|
||||
*
|
||||
* @param callbacks A set of callbacks to the Launcher, is actually a LauncherAppsCallback, but
|
||||
* for implementation purposes is passed around as an object.
|
||||
*/
|
||||
public void setLauncherAppsCallback(Object callbacks);
|
||||
|
||||
/**
|
||||
* Sets the callbacks to allow reacting the actions of search overlays of the launcher.
|
||||
*
|
||||
|
|
|
@ -11,6 +11,7 @@ import android.os.Bundle;
|
|||
import android.view.Menu;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import com.android.launcher3.allapps.AllAppsSearchBarController;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
|
@ -124,14 +125,6 @@ public class LauncherExtension extends Launcher {
|
|||
public void onClickAllAppsButton(View v) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllAppsShown() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAllAppsHidden() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindAllApplications(ArrayList<AppInfo> apps) {
|
||||
}
|
||||
|
@ -255,8 +248,8 @@ public class LauncherExtension extends Launcher {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean overrideAllAppsSearch() {
|
||||
return false;
|
||||
public AllAppsSearchBarController getAllAppsSearchBarController() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -284,11 +277,6 @@ public class LauncherExtension extends Launcher {
|
|||
return mLauncherOverlay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLauncherAppsCallback(Object callbacks) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLauncherSearchCallback(Object callbacks) {
|
||||
// Do nothing
|
||||
|
|
|
@ -129,7 +129,7 @@ public class LauncherStateTransitionAnimation {
|
|||
/**
|
||||
* Starts an animation to the apps view.
|
||||
*/
|
||||
public void startAnimationToAllApps(final boolean animated) {
|
||||
public void startAnimationToAllApps(final Launcher.State fromState, final boolean animated) {
|
||||
final AllAppsContainerView toView = mLauncher.getAppsView();
|
||||
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
|
||||
private int[] mAllAppsToPanelDelta;
|
||||
|
@ -137,7 +137,6 @@ public class LauncherStateTransitionAnimation {
|
|||
@Override
|
||||
public void onRevealViewVisible(View revealView, View contentView,
|
||||
View allAppsButtonView) {
|
||||
toView.setBackground(null);
|
||||
// Get the y delta between the center of the page and the center of the all apps
|
||||
// button
|
||||
mAllAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
|
||||
|
@ -173,9 +172,9 @@ public class LauncherStateTransitionAnimation {
|
|||
};
|
||||
}
|
||||
};
|
||||
// Only animate the search bar if animating from spring loaded mode back to all apps
|
||||
startAnimationToOverlay(Workspace.State.NORMAL_HIDDEN, toView, toView.getContentView(),
|
||||
toView.getRevealView(), animated,
|
||||
!mLauncher.isAllAppsSearchOverridden() /* hideSearchBar */, cb);
|
||||
toView.getRevealView(), toView.getSearchBarView(), animated, true, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -188,7 +187,6 @@ public class LauncherStateTransitionAnimation {
|
|||
@Override
|
||||
public void onRevealViewVisible(View revealView, View contentView,
|
||||
View allAppsButtonView) {
|
||||
revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
|
||||
}
|
||||
@Override
|
||||
public float getMaterialRevealViewFinalAlpha(View revealView) {
|
||||
|
@ -200,8 +198,8 @@ public class LauncherStateTransitionAnimation {
|
|||
}
|
||||
};
|
||||
startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, toView,
|
||||
toView.getContentView(), toView.getRevealView(), animated, true /* hideSearchBar */,
|
||||
cb);
|
||||
toView.getContentView(), toView.getRevealView(), null, animated,
|
||||
true /* hideSearchBar */, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -217,10 +215,10 @@ public class LauncherStateTransitionAnimation {
|
|||
}
|
||||
|
||||
if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) {
|
||||
startAnimationToWorkspaceFromAllApps(fromState, toWorkspaceState, toWorkspacePage,
|
||||
startAnimationToWorkspaceFromAllApps(toWorkspaceState, toWorkspacePage,
|
||||
animated, onCompleteRunnable);
|
||||
} else {
|
||||
startAnimationToWorkspaceFromWidgets(fromState, toWorkspaceState, toWorkspacePage,
|
||||
startAnimationToWorkspaceFromWidgets(toWorkspaceState, toWorkspacePage,
|
||||
animated, onCompleteRunnable);
|
||||
}
|
||||
}
|
||||
|
@ -230,8 +228,9 @@ public class LauncherStateTransitionAnimation {
|
|||
*/
|
||||
@SuppressLint("NewApi")
|
||||
private void startAnimationToOverlay(final Workspace.State toWorkspaceState, final View toView,
|
||||
final View contentView, final View revealView, final boolean animated,
|
||||
final boolean hideSearchBar, final PrivateTransitionCallbacks pCb) {
|
||||
final View contentView, final View revealView, final View overlaySearchBarView,
|
||||
final boolean animated, final boolean hideSearchBar,
|
||||
final PrivateTransitionCallbacks pCb) {
|
||||
final Resources res = mLauncher.getResources();
|
||||
final boolean material = Utilities.isLmpOrAbove();
|
||||
final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
|
||||
|
@ -252,7 +251,7 @@ public class LauncherStateTransitionAnimation {
|
|||
// Create the workspace animation.
|
||||
// NOTE: this call apparently also sets the state for the workspace if !animated
|
||||
Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, -1,
|
||||
animated, layerViews);
|
||||
animated, overlaySearchBarView != null /* hasOverlaySearchBar */, layerViews);
|
||||
|
||||
if (animated && initialized) {
|
||||
mStateAnimation = LauncherAnimUtils.createAnimatorSet();
|
||||
|
@ -297,6 +296,15 @@ public class LauncherStateTransitionAnimation {
|
|||
layerViews.put(revealView, BUILD_AND_SET_LAYER);
|
||||
mStateAnimation.play(panelAlphaAndDrift);
|
||||
|
||||
if (overlaySearchBarView != null) {
|
||||
overlaySearchBarView.setAlpha(0f);
|
||||
ObjectAnimator searchBarAlpha = ObjectAnimator.ofFloat(overlaySearchBarView, "alpha", 0f, 1f);
|
||||
searchBarAlpha.setDuration(100);
|
||||
searchBarAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
|
||||
layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
|
||||
mStateAnimation.play(searchBarAlpha);
|
||||
}
|
||||
|
||||
// Setup the animation for the content view
|
||||
contentView.setVisibility(View.VISIBLE);
|
||||
contentView.setAlpha(0f);
|
||||
|
@ -426,9 +434,8 @@ public class LauncherStateTransitionAnimation {
|
|||
/**
|
||||
* Starts and animation to the workspace from the apps view.
|
||||
*/
|
||||
private void startAnimationToWorkspaceFromAllApps(final Launcher.State fromState,
|
||||
final Workspace.State toWorkspaceState, final int toWorkspacePage,
|
||||
final boolean animated, final Runnable onCompleteRunnable) {
|
||||
private void startAnimationToWorkspaceFromAllApps(final Workspace.State toWorkspaceState,
|
||||
final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) {
|
||||
AllAppsContainerView appsView = mLauncher.getAppsView();
|
||||
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
|
||||
int[] mAllAppsToPanelDelta;
|
||||
|
@ -479,24 +486,23 @@ public class LauncherStateTransitionAnimation {
|
|||
};
|
||||
}
|
||||
};
|
||||
// Only animate the search bar if animating to spring loaded mode from all apps
|
||||
startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, appsView,
|
||||
appsView.getContentView(), appsView.getRevealView(), animated, onCompleteRunnable,
|
||||
cb);
|
||||
appsView.getContentView(), appsView.getRevealView(), appsView.getSearchBarView(),
|
||||
animated, onCompleteRunnable, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts and animation to the workspace from the widgets view.
|
||||
*/
|
||||
private void startAnimationToWorkspaceFromWidgets(final Launcher.State fromState,
|
||||
final Workspace.State toWorkspaceState, final int toWorkspacePage,
|
||||
final boolean animated, final Runnable onCompleteRunnable) {
|
||||
private void startAnimationToWorkspaceFromWidgets(final Workspace.State toWorkspaceState,
|
||||
final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) {
|
||||
final WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
|
||||
final Resources res = mLauncher.getResources();
|
||||
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
|
||||
@Override
|
||||
public void onRevealViewVisible(View revealView, View contentView,
|
||||
View allAppsButtonView) {
|
||||
revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
|
||||
}
|
||||
@Override
|
||||
public float getMaterialRevealViewFinalYDrift(View revealView) {
|
||||
|
@ -518,7 +524,7 @@ public class LauncherStateTransitionAnimation {
|
|||
}
|
||||
};
|
||||
startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, widgetsView,
|
||||
widgetsView.getContentView(), widgetsView.getRevealView(), animated,
|
||||
widgetsView.getContentView(), widgetsView.getRevealView(), null, animated,
|
||||
onCompleteRunnable, cb);
|
||||
}
|
||||
|
||||
|
@ -527,8 +533,8 @@ public class LauncherStateTransitionAnimation {
|
|||
*/
|
||||
private void startAnimationToWorkspaceFromOverlay(final Workspace.State toWorkspaceState,
|
||||
final int toWorkspacePage, final View fromView, final View contentView,
|
||||
final View revealView, final boolean animated, final Runnable onCompleteRunnable,
|
||||
final PrivateTransitionCallbacks pCb) {
|
||||
final View revealView, final View overlaySearchBarView, final boolean animated,
|
||||
final Runnable onCompleteRunnable, final PrivateTransitionCallbacks pCb) {
|
||||
final Resources res = mLauncher.getResources();
|
||||
final boolean material = Utilities.isLmpOrAbove();
|
||||
final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
|
||||
|
@ -549,7 +555,8 @@ public class LauncherStateTransitionAnimation {
|
|||
// Create the workspace animation.
|
||||
// NOTE: this call apparently also sets the state for the workspace if !animated
|
||||
Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState,
|
||||
toWorkspacePage, animated, layerViews);
|
||||
toWorkspacePage, animated, overlaySearchBarView != null /* hasOverlaySearchBar */,
|
||||
layerViews);
|
||||
|
||||
if (animated && initialized) {
|
||||
mStateAnimation = LauncherAnimUtils.createAnimatorSet();
|
||||
|
@ -633,6 +640,16 @@ public class LauncherStateTransitionAnimation {
|
|||
itemsAlpha.setInterpolator(decelerateInterpolator);
|
||||
mStateAnimation.play(itemsAlpha);
|
||||
|
||||
if (overlaySearchBarView != null) {
|
||||
overlaySearchBarView.setAlpha(1f);
|
||||
ObjectAnimator searchAlpha = ObjectAnimator.ofFloat(overlaySearchBarView, "alpha", 1f, 0f);
|
||||
searchAlpha.setDuration(material ? 100 : 150);
|
||||
searchAlpha.setInterpolator(decelerateInterpolator);
|
||||
searchAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
|
||||
layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
|
||||
mStateAnimation.play(searchAlpha);
|
||||
}
|
||||
|
||||
if (material) {
|
||||
// Animate the all apps button
|
||||
float finalRadius = pCb.getMaterialRevealViewStartFinalRadius();
|
||||
|
@ -681,6 +698,9 @@ public class LauncherStateTransitionAnimation {
|
|||
contentView.setTranslationY(0);
|
||||
contentView.setAlpha(1);
|
||||
}
|
||||
if (overlaySearchBarView != null) {
|
||||
overlaySearchBarView.setAlpha(1f);
|
||||
}
|
||||
|
||||
// This can hold unnecessary references to views.
|
||||
mStateAnimation = null;
|
||||
|
|
|
@ -16,13 +16,10 @@
|
|||
|
||||
package com.android.launcher3;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* An interface to get callbacks during a launcher transition.
|
||||
*/
|
||||
public interface LauncherTransitionable {
|
||||
View getContent();
|
||||
void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace);
|
||||
void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace);
|
||||
void onLauncherTransitionStep(Launcher l, float t);
|
||||
|
|
|
@ -1998,10 +1998,10 @@ public class Workspace extends PagedView
|
|||
* to that new state.
|
||||
*/
|
||||
public Animator setStateWithAnimation(State toState, int toPage, boolean animated,
|
||||
HashMap<View, Integer> layerViews) {
|
||||
boolean hasOverlaySearchBar, HashMap<View, Integer> layerViews) {
|
||||
// Create the animation to the new state
|
||||
Animator workspaceAnim = mStateTransitionAnimation.getAnimationToState(getState(),
|
||||
toState, toPage, animated, layerViews);
|
||||
toState, toPage, animated, hasOverlaySearchBar, layerViews);
|
||||
|
||||
// Update the current state
|
||||
mState = toState;
|
||||
|
@ -2100,11 +2100,6 @@ public class Workspace extends PagedView
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getContent() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the drawable for the given text view.
|
||||
*/
|
||||
|
|
|
@ -121,6 +121,52 @@ class ZoomInInterpolator implements TimeInterpolator {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the transition states for convenience.
|
||||
*/
|
||||
class TransitionStates {
|
||||
|
||||
// Raw states
|
||||
final boolean oldStateIsNormal;
|
||||
final boolean oldStateIsSpringLoaded;
|
||||
final boolean oldStateIsNormalHidden;
|
||||
final boolean oldStateIsOverviewHidden;
|
||||
final boolean oldStateIsOverview;
|
||||
|
||||
final boolean stateIsNormal;
|
||||
final boolean stateIsSpringLoaded;
|
||||
final boolean stateIsNormalHidden;
|
||||
final boolean stateIsOverviewHidden;
|
||||
final boolean stateIsOverview;
|
||||
|
||||
// Convenience members
|
||||
final boolean workspaceToAllApps;
|
||||
final boolean overviewToAllApps;
|
||||
final boolean allAppsToWorkspace;
|
||||
final boolean workspaceToOverview;
|
||||
final boolean overviewToWorkspace;
|
||||
|
||||
public TransitionStates(final Workspace.State fromState, final Workspace.State toState) {
|
||||
oldStateIsNormal = (fromState == Workspace.State.NORMAL);
|
||||
oldStateIsSpringLoaded = (fromState == Workspace.State.SPRING_LOADED);
|
||||
oldStateIsNormalHidden = (fromState == Workspace.State.NORMAL_HIDDEN);
|
||||
oldStateIsOverviewHidden = (fromState == Workspace.State.OVERVIEW_HIDDEN);
|
||||
oldStateIsOverview = (fromState == Workspace.State.OVERVIEW);
|
||||
|
||||
stateIsNormal = (toState == Workspace.State.NORMAL);
|
||||
stateIsSpringLoaded = (toState == Workspace.State.SPRING_LOADED);
|
||||
stateIsNormalHidden = (toState == Workspace.State.NORMAL_HIDDEN);
|
||||
stateIsOverviewHidden = (toState == Workspace.State.OVERVIEW_HIDDEN);
|
||||
stateIsOverview = (toState == Workspace.State.OVERVIEW);
|
||||
|
||||
workspaceToOverview = (oldStateIsNormal && stateIsOverview);
|
||||
workspaceToAllApps = (oldStateIsNormal && stateIsNormalHidden);
|
||||
overviewToWorkspace = (oldStateIsOverview && stateIsNormal);
|
||||
overviewToAllApps = (oldStateIsOverview && stateIsOverviewHidden);
|
||||
allAppsToWorkspace = (stateIsNormalHidden && stateIsNormal);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages the animations between each of the workspace states.
|
||||
*/
|
||||
|
@ -175,9 +221,17 @@ public class WorkspaceStateTransitionAnimation {
|
|||
}
|
||||
|
||||
public AnimatorSet getAnimationToState(Workspace.State fromState, Workspace.State toState,
|
||||
int toPage, boolean animated,
|
||||
HashMap<View, Integer> layerViews) {
|
||||
getAnimation(fromState, toState, toPage, animated, layerViews);
|
||||
int toPage, boolean animated, boolean hasOverlaySearchBar,
|
||||
HashMap<View, Integer> layerViews) {
|
||||
AccessibilityManager am = (AccessibilityManager)
|
||||
mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE);
|
||||
final boolean accessibilityEnabled = am.isEnabled();
|
||||
TransitionStates states = new TransitionStates(fromState, toState);
|
||||
int duration = getAnimationDuration(states);
|
||||
animateWorkspace(states, toPage, animated, duration, layerViews,
|
||||
accessibilityEnabled);
|
||||
animateSearchBar(states, animated, duration, hasOverlaySearchBar, layerViews,
|
||||
accessibilityEnabled);
|
||||
return mStateAnimator;
|
||||
}
|
||||
|
||||
|
@ -185,16 +239,38 @@ public class WorkspaceStateTransitionAnimation {
|
|||
return mNewScale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reinitializes the arrays that we need for the animations on each page.
|
||||
*/
|
||||
private void reinitializeAnimationArrays() {
|
||||
final int childCount = mWorkspace.getChildCount();
|
||||
if (mLastChildCount == childCount) return;
|
||||
|
||||
mOldBackgroundAlphas = new float[childCount];
|
||||
mOldAlphas = new float[childCount];
|
||||
mNewBackgroundAlphas = new float[childCount];
|
||||
mNewAlphas = new float[childCount];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the proper animation duration for a transition.
|
||||
*/
|
||||
private int getAnimationDuration(TransitionStates states) {
|
||||
if (states.workspaceToAllApps || states.overviewToAllApps) {
|
||||
return mAllAppsTransitionTime;
|
||||
} else if (states.workspaceToOverview || states.overviewToWorkspace) {
|
||||
return mOverviewTransitionTime;
|
||||
} else {
|
||||
return mOverlayTransitionTime;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a transition animation for the workspace.
|
||||
*/
|
||||
private void getAnimation(final Workspace.State fromState, final Workspace.State toState,
|
||||
int toPage, final boolean animated,
|
||||
final HashMap<View, Integer> layerViews) {
|
||||
AccessibilityManager am = (AccessibilityManager)
|
||||
mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE);
|
||||
final boolean accessibilityEnabled = am.isEnabled();
|
||||
|
||||
private void animateWorkspace(final TransitionStates states, int toPage, final boolean animated,
|
||||
final int duration, final HashMap<View, Integer> layerViews,
|
||||
final boolean accessibilityEnabled) {
|
||||
// Reinitialize animation arrays for the current workspace state
|
||||
reinitializeAnimationArrays();
|
||||
|
||||
|
@ -205,32 +281,12 @@ public class WorkspaceStateTransitionAnimation {
|
|||
}
|
||||
|
||||
// Update the workspace state
|
||||
final boolean oldStateIsNormal = (fromState == Workspace.State.NORMAL);
|
||||
final boolean oldStateIsSpringLoaded = (fromState == Workspace.State.SPRING_LOADED);
|
||||
final boolean oldStateIsNormalHidden = (fromState == Workspace.State.NORMAL_HIDDEN);
|
||||
final boolean oldStateIsOverviewHidden = (fromState == Workspace.State.OVERVIEW_HIDDEN);
|
||||
final boolean oldStateIsOverview = (fromState == Workspace.State.OVERVIEW);
|
||||
|
||||
final boolean stateIsNormal = (toState == Workspace.State.NORMAL);
|
||||
final boolean stateIsSpringLoaded = (toState == Workspace.State.SPRING_LOADED);
|
||||
final boolean stateIsNormalHidden = (toState == Workspace.State.NORMAL_HIDDEN);
|
||||
final boolean stateIsOverviewHidden = (toState == Workspace.State.OVERVIEW_HIDDEN);
|
||||
final boolean stateIsOverview = (toState == Workspace.State.OVERVIEW);
|
||||
|
||||
final boolean workspaceToAllApps = (oldStateIsNormal && stateIsNormalHidden);
|
||||
final boolean overviewToAllApps = (oldStateIsOverview && stateIsOverviewHidden);
|
||||
final boolean allAppsToWorkspace = (stateIsNormalHidden && stateIsNormal);
|
||||
final boolean workspaceToOverview = (oldStateIsNormal && stateIsOverview);
|
||||
final boolean overviewToWorkspace = (oldStateIsOverview && stateIsNormal);
|
||||
|
||||
float finalBackgroundAlpha = (stateIsSpringLoaded || stateIsOverview) ? 1.0f : 0f;
|
||||
float finalHotseatAndPageIndicatorAlpha = (stateIsNormal || stateIsSpringLoaded) ? 1f : 0f;
|
||||
float finalOverviewPanelAlpha = stateIsOverview ? 1f : 0f;
|
||||
// We keep the search bar visible on the workspace and in AllApps now
|
||||
boolean showSearchBar = stateIsNormal ||
|
||||
(mLauncher.isAllAppsSearchOverridden() && stateIsNormalHidden);
|
||||
float finalSearchBarAlpha = showSearchBar ? 1f : 0f;
|
||||
float finalWorkspaceTranslationY = stateIsOverview || stateIsOverviewHidden ?
|
||||
float finalBackgroundAlpha = (states.stateIsSpringLoaded || states.stateIsOverview) ?
|
||||
1.0f : 0f;
|
||||
float finalHotseatAndPageIndicatorAlpha = (states.stateIsNormal || states.stateIsSpringLoaded) ?
|
||||
1f : 0f;
|
||||
float finalOverviewPanelAlpha = states.stateIsOverview ? 1f : 0f;
|
||||
float finalWorkspaceTranslationY = states.stateIsOverview || states.stateIsOverviewHidden ?
|
||||
mWorkspace.getOverviewModeTranslationY() : 0;
|
||||
|
||||
final int childCount = mWorkspace.getChildCount();
|
||||
|
@ -238,29 +294,20 @@ public class WorkspaceStateTransitionAnimation {
|
|||
|
||||
mNewScale = 1.0f;
|
||||
|
||||
if (oldStateIsOverview) {
|
||||
if (states.oldStateIsOverview) {
|
||||
mWorkspace.disableFreeScroll();
|
||||
} else if (stateIsOverview) {
|
||||
} else if (states.stateIsOverview) {
|
||||
mWorkspace.enableFreeScroll();
|
||||
}
|
||||
|
||||
if (!stateIsNormal) {
|
||||
if (stateIsSpringLoaded) {
|
||||
if (!states.stateIsNormal) {
|
||||
if (states.stateIsSpringLoaded) {
|
||||
mNewScale = mSpringLoadedShrinkFactor;
|
||||
} else if (stateIsOverview || stateIsOverviewHidden) {
|
||||
} else if (states.stateIsOverview || states.stateIsOverviewHidden) {
|
||||
mNewScale = mOverviewModeShrinkFactor;
|
||||
}
|
||||
}
|
||||
|
||||
final int duration;
|
||||
if (workspaceToAllApps || overviewToAllApps) {
|
||||
duration = mAllAppsTransitionTime;
|
||||
} else if (workspaceToOverview || overviewToWorkspace) {
|
||||
duration = mOverviewTransitionTime;
|
||||
} else {
|
||||
duration = mOverlayTransitionTime;
|
||||
}
|
||||
|
||||
if (toPage == SCROLL_TO_CURRENT_PAGE) {
|
||||
toPage = mWorkspace.getPageNearestToCenterOfScreen();
|
||||
}
|
||||
|
@ -271,9 +318,9 @@ public class WorkspaceStateTransitionAnimation {
|
|||
boolean isCurrentPage = (i == toPage);
|
||||
float initialAlpha = cl.getShortcutsAndWidgets().getAlpha();
|
||||
float finalAlpha;
|
||||
if (stateIsNormalHidden || stateIsOverviewHidden) {
|
||||
if (states.stateIsNormalHidden || states.stateIsOverviewHidden) {
|
||||
finalAlpha = 0f;
|
||||
} else if (stateIsNormal && mWorkspaceFadeInAdjacentScreens) {
|
||||
} else if (states.stateIsNormal && mWorkspaceFadeInAdjacentScreens) {
|
||||
finalAlpha = (i == toPage || i < customPageCount) ? 1f : 0f;
|
||||
} else {
|
||||
finalAlpha = 1f;
|
||||
|
@ -282,8 +329,8 @@ public class WorkspaceStateTransitionAnimation {
|
|||
// If we are animating to/from the small state, then hide the side pages and fade the
|
||||
// current page in
|
||||
if (!mWorkspace.isSwitchingState()) {
|
||||
if (workspaceToAllApps || allAppsToWorkspace) {
|
||||
if (allAppsToWorkspace && isCurrentPage) {
|
||||
if (states.workspaceToAllApps || states.allAppsToWorkspace) {
|
||||
if (states.allAppsToWorkspace && isCurrentPage) {
|
||||
initialAlpha = 0f;
|
||||
} else if (!isCurrentPage) {
|
||||
initialAlpha = finalAlpha = 0f;
|
||||
|
@ -303,7 +350,6 @@ public class WorkspaceStateTransitionAnimation {
|
|||
}
|
||||
}
|
||||
|
||||
final View searchBar = mLauncher.getOrCreateQsbBar();
|
||||
final ViewGroup overviewPanel = mLauncher.getOverviewPanel();
|
||||
final View hotseat = mLauncher.getHotseat();
|
||||
final View pageIndicator = mWorkspace.getPageIndicator();
|
||||
|
@ -345,7 +391,7 @@ public class WorkspaceStateTransitionAnimation {
|
|||
}
|
||||
}
|
||||
}
|
||||
Animator pageIndicatorAlpha = null;
|
||||
Animator pageIndicatorAlpha;
|
||||
if (pageIndicator != null) {
|
||||
pageIndicatorAlpha = new LauncherViewPropertyAnimator(pageIndicator)
|
||||
.alpha(finalHotseatAndPageIndicatorAlpha).withLayer();
|
||||
|
@ -380,11 +426,11 @@ public class WorkspaceStateTransitionAnimation {
|
|||
overviewPanelAlpha.withLayer();
|
||||
}
|
||||
|
||||
if (workspaceToOverview) {
|
||||
if (states.workspaceToOverview) {
|
||||
pageIndicatorAlpha.setInterpolator(new DecelerateInterpolator(2));
|
||||
hotseatAlpha.setInterpolator(new DecelerateInterpolator(2));
|
||||
overviewPanelAlpha.setInterpolator(null);
|
||||
} else if (overviewToWorkspace) {
|
||||
} else if (states.overviewToWorkspace) {
|
||||
pageIndicatorAlpha.setInterpolator(null);
|
||||
hotseatAlpha.setInterpolator(null);
|
||||
overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2));
|
||||
|
@ -394,26 +440,6 @@ public class WorkspaceStateTransitionAnimation {
|
|||
pageIndicatorAlpha.setDuration(duration);
|
||||
hotseatAlpha.setDuration(duration);
|
||||
|
||||
// TODO: This should really be coordinated with the SearchDropTargetBar, otherwise the
|
||||
// bar has no idea that it is hidden, and this has no idea what state the bar is
|
||||
// actually in.
|
||||
if (searchBar != null) {
|
||||
LauncherViewPropertyAnimator searchBarAlpha = new LauncherViewPropertyAnimator(searchBar)
|
||||
.alpha(finalSearchBarAlpha);
|
||||
searchBarAlpha.addListener(new AlphaUpdateListener(searchBar, accessibilityEnabled));
|
||||
searchBar.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
if (layerViews != null) {
|
||||
// If layerViews is not null, we add these views, and indicate that
|
||||
// the caller can manage layer state.
|
||||
layerViews.put(searchBar, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
|
||||
} else {
|
||||
// Otherwise let the animator handle layer management.
|
||||
searchBarAlpha.withLayer();
|
||||
}
|
||||
searchBarAlpha.setDuration(duration);
|
||||
mStateAnimator.play(searchBarAlpha);
|
||||
}
|
||||
|
||||
mStateAnimator.play(overviewPanelAlpha);
|
||||
mStateAnimator.play(hotseatAlpha);
|
||||
mStateAnimator.play(pageIndicatorAlpha);
|
||||
|
@ -437,10 +463,6 @@ public class WorkspaceStateTransitionAnimation {
|
|||
pageIndicator.setAlpha(finalHotseatAndPageIndicatorAlpha);
|
||||
AlphaUpdateListener.updateVisibility(pageIndicator, accessibilityEnabled);
|
||||
}
|
||||
if (searchBar != null) {
|
||||
searchBar.setAlpha(finalSearchBarAlpha);
|
||||
AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled);
|
||||
}
|
||||
mWorkspace.updateCustomContentVisibility();
|
||||
mWorkspace.setScaleX(mNewScale);
|
||||
mWorkspace.setScaleY(mNewScale);
|
||||
|
@ -452,7 +474,7 @@ public class WorkspaceStateTransitionAnimation {
|
|||
}
|
||||
}
|
||||
|
||||
if (stateIsNormal) {
|
||||
if (states.stateIsNormal) {
|
||||
animateBackgroundGradient(0f, animated);
|
||||
} else {
|
||||
animateBackgroundGradient(mWorkspaceScrimAlpha, animated);
|
||||
|
@ -460,16 +482,69 @@ public class WorkspaceStateTransitionAnimation {
|
|||
}
|
||||
|
||||
/**
|
||||
* Reinitializes the arrays that we need for the animations on each page.
|
||||
* Coordinates with the workspace animation to animate the search bar.
|
||||
*
|
||||
* TODO: This should really be coordinated with the SearchDropTargetBar, otherwise the
|
||||
* bar has no idea that it is hidden, and this has no idea what state the bar is
|
||||
* actually in.
|
||||
*/
|
||||
private void reinitializeAnimationArrays() {
|
||||
final int childCount = mWorkspace.getChildCount();
|
||||
if (mLastChildCount == childCount) return;
|
||||
private void animateSearchBar(TransitionStates states, boolean animated, int duration,
|
||||
boolean hasOverlaySearchBar, final HashMap<View, Integer> layerViews,
|
||||
final boolean accessibilityEnabled) {
|
||||
|
||||
mOldBackgroundAlphas = new float[childCount];
|
||||
mOldAlphas = new float[childCount];
|
||||
mNewBackgroundAlphas = new float[childCount];
|
||||
mNewAlphas = new float[childCount];
|
||||
// The search bar is only visible in the workspace
|
||||
final View searchBar = mLauncher.getOrCreateQsbBar();
|
||||
if (searchBar != null) {
|
||||
final boolean searchBarWillBeShown = states.stateIsNormal;
|
||||
final float finalSearchBarAlpha = searchBarWillBeShown ? 1f : 0f;
|
||||
if (animated) {
|
||||
if (hasOverlaySearchBar) {
|
||||
// If there is an overlay search bar, then we will coordinate with it.
|
||||
mStateAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
// If we are transitioning to a visible search bar, show it immediately
|
||||
// and let the overlay search bar has faded out
|
||||
if (searchBarWillBeShown) {
|
||||
searchBar.setAlpha(finalSearchBarAlpha);
|
||||
AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
// If we are transitioning to a hidden search bar, hide it only after
|
||||
// the overlay search bar has faded in
|
||||
if (!searchBarWillBeShown) {
|
||||
searchBar.setAlpha(finalSearchBarAlpha);
|
||||
AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Otherwise, we can just do the normal animation
|
||||
LauncherViewPropertyAnimator searchBarAlpha =
|
||||
new LauncherViewPropertyAnimator(searchBar).alpha(finalSearchBarAlpha);
|
||||
searchBarAlpha.addListener(new AlphaUpdateListener(searchBar,
|
||||
accessibilityEnabled));
|
||||
searchBar.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
if (layerViews != null) {
|
||||
// If layerViews is not null, we add these views, and indicate that
|
||||
// the caller can manage layer state.
|
||||
layerViews.put(searchBar, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
|
||||
} else {
|
||||
// Otherwise let the animator handle layer management.
|
||||
searchBarAlpha.withLayer();
|
||||
}
|
||||
searchBarAlpha.setDuration(duration);
|
||||
mStateAnimator.play(searchBarAlpha);
|
||||
}
|
||||
} else {
|
||||
// Set the search bar state immediately
|
||||
searchBar.setAlpha(finalSearchBarAlpha);
|
||||
AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,17 +20,15 @@ import android.annotation.TargetApi;
|
|||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.graphics.drawable.InsetDrawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.text.Selection;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.method.TextKeyListener;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -39,11 +37,8 @@ import android.view.View;
|
|||
import android.view.ViewConfiguration;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import android.widget.LinearLayout;
|
||||
import com.android.launcher3.AppInfo;
|
||||
import com.android.launcher3.BaseContainerView;
|
||||
import com.android.launcher3.BubbleTextView;
|
||||
|
@ -54,7 +49,6 @@ import com.android.launcher3.DeviceProfile;
|
|||
import com.android.launcher3.DragSource;
|
||||
import com.android.launcher3.DropTarget;
|
||||
import com.android.launcher3.Folder;
|
||||
import com.android.launcher3.Insettable;
|
||||
import com.android.launcher3.ItemInfo;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherTransitionable;
|
||||
|
@ -62,131 +56,24 @@ import com.android.launcher3.R;
|
|||
import com.android.launcher3.Stats;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.allapps.AppSearchManager.AppSearchResultCallback;
|
||||
import com.android.launcher3.util.Thunk;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Interface for controlling the header elevation in response to RecyclerView scroll.
|
||||
*/
|
||||
interface HeaderElevationController {
|
||||
void onScroll(int scrollY);
|
||||
void updateBackgroundPadding(Drawable bg);
|
||||
void disable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of the header elevation mechanism for pre-L devices. It simulates elevation
|
||||
* by drawing a gradient under the header bar.
|
||||
*/
|
||||
final class HeaderElevationControllerV16 implements HeaderElevationController {
|
||||
|
||||
private final View mShadow;
|
||||
private final float mScrollToElevation;
|
||||
private final Rect mTmpRect = new Rect();
|
||||
|
||||
public HeaderElevationControllerV16(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[] {0x44000000, 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(Drawable bg) {
|
||||
bg.getPadding(mTmpRect);
|
||||
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mShadow.getLayoutParams();
|
||||
lp.leftMargin = mTmpRect.left;
|
||||
lp.rightMargin = mTmpRect.right;
|
||||
mShadow.requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
ViewGroup parent = (ViewGroup) mShadow.getParent();
|
||||
if (parent != null) {
|
||||
parent.removeView(mShadow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of the header elevation mechanism for L+ devices, which makes use of the native
|
||||
* view elevation.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
final class HeaderElevationControllerVL implements HeaderElevationController {
|
||||
|
||||
private final View mHeader;
|
||||
private final float mMaxElevation;
|
||||
private final float mScrollToElevation;
|
||||
|
||||
public HeaderElevationControllerVL(View header) {
|
||||
mHeader = header;
|
||||
|
||||
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 = (float) Math.min(scrollY, mScrollToElevation) /
|
||||
mScrollToElevation;
|
||||
float newElevation = mMaxElevation * elevationPct;
|
||||
if (Float.compare(mHeader.getElevation(), newElevation) != 0) {
|
||||
mHeader.setElevation(newElevation);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBackgroundPadding(Drawable bg) {
|
||||
// Do nothing, the background padding on the header view is already applied
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() { }
|
||||
}
|
||||
|
||||
/**
|
||||
* The all apps view container.
|
||||
*/
|
||||
public class AllAppsContainerView extends BaseContainerView implements DragSource, Insettable,
|
||||
TextWatcher, TextView.OnEditorActionListener, LauncherTransitionable,
|
||||
AlphabeticalAppsList.AdapterChangedCallback, AllAppsGridAdapter.PredictionBarSpacerCallbacks,
|
||||
View.OnTouchListener, View.OnClickListener, View.OnLongClickListener,
|
||||
ViewTreeObserver.OnPreDrawListener, AppSearchResultCallback, Stats.LaunchSourceProvider {
|
||||
public class AllAppsContainerView extends BaseContainerView implements DragSource,
|
||||
LauncherTransitionable, AlphabeticalAppsList.AdapterChangedCallback,
|
||||
AllAppsGridAdapter.PredictionBarSpacerCallbacks, View.OnTouchListener,
|
||||
View.OnLongClickListener, ViewTreeObserver.OnPreDrawListener,
|
||||
AllAppsSearchBarController.Callbacks, Stats.LaunchSourceProvider {
|
||||
|
||||
public static final boolean GRID_MERGE_SECTIONS = true;
|
||||
|
||||
private static final boolean ALLOW_SINGLE_APP_LAUNCH = true;
|
||||
private static final boolean DYNAMIC_HEADER_ELEVATION = true;
|
||||
private static final boolean DISMISS_SEARCH_ON_BACK = 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;
|
||||
|
||||
@Thunk Launcher mLauncher;
|
||||
@Thunk AlphabeticalAppsList mApps;
|
||||
private LayoutInflater mLayoutInflater;
|
||||
|
@ -194,16 +81,14 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
|
|||
private RecyclerView.LayoutManager mLayoutManager;
|
||||
private RecyclerView.ItemDecoration mItemDecoration;
|
||||
|
||||
@Thunk FrameLayout mContentView;
|
||||
@Thunk View mContent;
|
||||
@Thunk View mContainerView;
|
||||
@Thunk View mRevealView;
|
||||
@Thunk AllAppsRecyclerView mAppsRecyclerView;
|
||||
@Thunk ViewGroup mPredictionBarView;
|
||||
private View mHeaderView;
|
||||
@Thunk View mSearchBarContainerView;
|
||||
private View mSearchButtonView;
|
||||
private View mDismissSearchButtonView;
|
||||
@Thunk AllAppsSearchEditView mSearchBarEditView;
|
||||
|
||||
private HeaderElevationController mElevationController;
|
||||
@Thunk AllAppsSearchBarController mSearchBarController;
|
||||
private ViewGroup mSearchBarContainerView;
|
||||
private View mSearchBarView;
|
||||
|
||||
private int mNumAppsPerRow;
|
||||
private int mNumPredictedAppsPerRow;
|
||||
|
@ -213,18 +98,16 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
|
|||
private final Point mIconLastTouchPos = new Point();
|
||||
// This coordinate is used to proxy click and long-click events to the prediction bar icons
|
||||
private final Point mPredictionIconTouchDownPos = new Point();
|
||||
private int mContentMarginStart;
|
||||
// Normal container insets
|
||||
private int mContainerInset;
|
||||
private int mPredictionBarHeight;
|
||||
private int mLastRecyclerViewScrollPos = -1;
|
||||
@Thunk boolean mFocusPredictionBarOnFirstBind;
|
||||
|
||||
private SpannableStringBuilder mSearchQueryBuilder = null;
|
||||
|
||||
private CheckLongPressHelper mPredictionIconCheckForLongPress;
|
||||
private View mPredictionIconUnderTouch;
|
||||
|
||||
private AppSearchManager mSearchManager;
|
||||
|
||||
public AllAppsContainerView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
@ -238,30 +121,24 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
|
|||
Resources res = context.getResources();
|
||||
|
||||
mLauncher = (Launcher) context;
|
||||
mLayoutInflater = LayoutInflater.from(context);
|
||||
DeviceProfile grid = mLauncher.getDeviceProfile();
|
||||
|
||||
mContainerInset = res.getDimensionPixelSize(R.dimen.all_apps_container_inset);
|
||||
mPredictionBarHeight = (int) (grid.allAppsIconSizePx + grid.iconDrawablePaddingOriginalPx +
|
||||
Utilities.calculateTextHeight(grid.allAppsIconTextSizePx) +
|
||||
2 * res.getDimensionPixelSize(R.dimen.all_apps_icon_top_bottom_padding) +
|
||||
res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_bottom_padding));
|
||||
2 * res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_top_bottom_padding));
|
||||
|
||||
mLayoutInflater = LayoutInflater.from(context);
|
||||
|
||||
mNumAppsPerRow = grid.allAppsNumCols;
|
||||
mNumPredictedAppsPerRow = grid.allAppsNumPredictiveCols;
|
||||
mApps = new AlphabeticalAppsList(context, mNumAppsPerRow, mNumPredictedAppsPerRow);
|
||||
mApps = new AlphabeticalAppsList(context);
|
||||
mApps.setAdapterChangedCallback(this);
|
||||
mAdapter = new AllAppsGridAdapter(context, mApps, mNumAppsPerRow, this, this, mLauncher, this);
|
||||
mAdapter = new AllAppsGridAdapter(context, mApps, this, this, mLauncher, this);
|
||||
mAdapter.setEmptySearchText(res.getString(R.string.all_apps_loading_message));
|
||||
mAdapter.setNumAppsPerRow(mNumAppsPerRow);
|
||||
mAdapter.setPredictionRowHeight(mPredictionBarHeight);
|
||||
mApps.setAdapter(mAdapter);
|
||||
mLayoutManager = mAdapter.getLayoutManager();
|
||||
mItemDecoration = mAdapter.getItemDecoration();
|
||||
mContentMarginStart = mAdapter.getContentMarginStart();
|
||||
|
||||
mApps.setAdapter(mAdapter);
|
||||
mSearchManager = mApps.newSimpleAppSearchManager();
|
||||
mSearchQueryBuilder = new SpannableStringBuilder();
|
||||
Selection.setSelection(mSearchQueryBuilder, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -285,11 +162,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
|
|||
mApps.addApps(apps);
|
||||
}
|
||||
|
||||
public void setSearchManager(AppSearchManager searchManager) {
|
||||
mSearchManager.cancel(true);
|
||||
mSearchManager = searchManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates existing apps in the list
|
||||
*/
|
||||
|
@ -305,13 +177,23 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
|
|||
}
|
||||
|
||||
/**
|
||||
* Hides the header bar
|
||||
* Sets the search bar that shows above the a-z list.
|
||||
*/
|
||||
public void hideHeaderBar() {
|
||||
mHeaderView.setVisibility(View.GONE);
|
||||
mElevationController.disable();
|
||||
onUpdateBackgrounds();
|
||||
onUpdatePaddings();
|
||||
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, this);
|
||||
|
||||
// Add the new search view to the layout
|
||||
View searchBarView = searchController.getView(mSearchBarContainerView);
|
||||
mSearchBarContainerView.addView(searchBarView);
|
||||
mSearchBarContainerView.setVisibility(View.VISIBLE);
|
||||
mSearchBarView = searchBarView;
|
||||
setHasSearchBar();
|
||||
|
||||
updateBackgroundAndPaddings();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -325,28 +207,43 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
|
|||
* Returns the content view used for the launcher transitions.
|
||||
*/
|
||||
public View getContentView() {
|
||||
return mContentView;
|
||||
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 findViewById(R.id.apps_view_transition_overlay);
|
||||
return mRevealView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an new instance of the default app search controller.
|
||||
*/
|
||||
public AllAppsSearchBarController newDefaultAppSearchController() {
|
||||
return new DefaultAppSearchController(getContext(), this, mAppsRecyclerView);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
boolean isRtl = Utilities.isRtl(getResources());
|
||||
mAdapter.setRtl(isRtl);
|
||||
mContent = findViewById(R.id.content);
|
||||
|
||||
// Work around the search box getting first focus and showing the cursor by
|
||||
// proxying the focus from the content view to the recycler view directly
|
||||
mContentView = (FrameLayout) findViewById(R.id.apps_list);
|
||||
mContentView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
||||
// 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() {
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
if (v == mContentView && hasFocus) {
|
||||
if (hasFocus) {
|
||||
if (!mApps.getPredictedApps().isEmpty()) {
|
||||
// If the prediction bar is going to be bound, then defer focusing until
|
||||
// it is first bound
|
||||
|
@ -360,52 +257,16 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
|
|||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
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);
|
||||
|
||||
// Fix the header view elevation if not dynamically calculating it
|
||||
mHeaderView = findViewById(R.id.header);
|
||||
mHeaderView.setOnClickListener(this);
|
||||
|
||||
mElevationController = Utilities.isLmpOrAbove() ?
|
||||
new HeaderElevationControllerVL(mHeaderView) :
|
||||
new HeaderElevationControllerV16(mHeaderView);
|
||||
if (!DYNAMIC_HEADER_ELEVATION) {
|
||||
mElevationController.onScroll(getResources()
|
||||
.getDimensionPixelSize(R.dimen.all_apps_header_scroll_to_elevation));
|
||||
}
|
||||
|
||||
// Fix the prediction bar size
|
||||
mPredictionBarView = (ViewGroup) findViewById(R.id.prediction_bar);
|
||||
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPredictionBarView.getLayoutParams();
|
||||
lp.height = mPredictionBarHeight;
|
||||
|
||||
mSearchButtonView = mHeaderView.findViewById(R.id.search_button);
|
||||
mSearchBarContainerView = findViewById(R.id.app_search_container);
|
||||
mDismissSearchButtonView = mSearchBarContainerView.findViewById(R.id.dismiss_search_button);
|
||||
mDismissSearchButtonView.setOnClickListener(this);
|
||||
mSearchBarEditView = (AllAppsSearchEditView) findViewById(R.id.apps_search_box);
|
||||
if (mSearchBarEditView != null) {
|
||||
mSearchBarEditView.addTextChangedListener(this);
|
||||
mSearchBarEditView.setOnEditorActionListener(this);
|
||||
if (DISMISS_SEARCH_ON_BACK) {
|
||||
mSearchBarEditView.setOnBackKeyListener(
|
||||
new AllAppsSearchEditView.OnBackKeyListener() {
|
||||
@Override
|
||||
public void 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, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);
|
||||
// Load the all apps recycler view
|
||||
mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.collection);
|
||||
mAppsRecyclerView.setApps(mApps);
|
||||
mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow);
|
||||
mAppsRecyclerView.setPredictionBarHeight(mPredictionBarHeight);
|
||||
mAppsRecyclerView.setLayoutManager(mLayoutManager);
|
||||
mAppsRecyclerView.setAdapter(mAdapter);
|
||||
|
@ -413,8 +274,18 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
|
|||
if (mItemDecoration != null) {
|
||||
mAppsRecyclerView.addItemDecoration(mItemDecoration);
|
||||
}
|
||||
onUpdateBackgrounds();
|
||||
onUpdatePaddings();
|
||||
|
||||
// Fix the prediction bar height
|
||||
mPredictionBarView = (ViewGroup) findViewById(R.id.prediction_bar);
|
||||
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPredictionBarView.getLayoutParams();
|
||||
lp.height = mPredictionBarHeight;
|
||||
|
||||
updateBackgroundAndPaddings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBoundsChanged(Rect newBounds) {
|
||||
mLauncher.updateOverlayBounds(newBounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -422,6 +293,12 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
|
|||
updatePredictionBarVisibility();
|
||||
|
||||
List<AppInfo> predictedApps = mApps.getPredictedApps();
|
||||
|
||||
// Remove extra prediction icons
|
||||
while (mPredictionBarView.getChildCount() > mNumPredictedAppsPerRow) {
|
||||
mPredictionBarView.removeViewAt(mPredictionBarView.getChildCount() - 1);
|
||||
}
|
||||
|
||||
int childCount = mPredictionBarView.getChildCount();
|
||||
for (int i = 0; i < mNumPredictedAppsPerRow; i++) {
|
||||
BubbleTextView icon;
|
||||
|
@ -455,97 +332,113 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onFixedBoundsUpdated() {
|
||||
// Update the number of items in the grid
|
||||
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);
|
||||
DeviceProfile grid = mLauncher.getDeviceProfile();
|
||||
if (grid.updateAppsViewNumCols(getContext().getResources(), mFixedBounds.width())) {
|
||||
grid.updateAppsViewNumCols(getResources(), availableWidth);
|
||||
if (mNumAppsPerRow != grid.allAppsNumCols ||
|
||||
mNumPredictedAppsPerRow != grid.allAppsNumPredictiveCols) {
|
||||
mNumAppsPerRow = grid.allAppsNumCols;
|
||||
mNumPredictedAppsPerRow = grid.allAppsNumPredictiveCols;
|
||||
mAppsRecyclerView.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow);
|
||||
mAdapter.setNumAppsPerRow(mNumAppsPerRow);
|
||||
mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow);
|
||||
}
|
||||
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the padding of the Apps view and children. To ensure that the RecyclerView has the
|
||||
* full width to handle touches right to the edge of the screen, we only apply the top and
|
||||
* bottom padding to the AppsContainerView and then the left/right padding on the RecyclerView
|
||||
* itself. In particular, the left/right padding is applied to the background of the view,
|
||||
* and then additionally inset by the start margin.
|
||||
* Update the background and padding of the Apps view and children. Instead of insetting the
|
||||
* container view, we inset the background and padding of the recycler view to allow for the
|
||||
* recycler view to handle touch events (for fast scrolling) all the way to the edge.
|
||||
*/
|
||||
@Override
|
||||
protected void onUpdatePaddings() {
|
||||
protected void onUpdateBackgroundAndPaddings(Rect searchBarBounds, Rect padding) {
|
||||
boolean isRtl = Utilities.isRtl(getResources());
|
||||
boolean hasSearchBar = (mSearchBarEditView != null) &&
|
||||
(mSearchBarEditView.getVisibility() == View.VISIBLE);
|
||||
|
||||
// Set the background on the container, but let the recyclerView extend the full screen,
|
||||
// so that the fast-scroller works on the edge as well.
|
||||
mContentView.setPadding(0, 0, 0, 0);
|
||||
|
||||
if (mFixedBounds.isEmpty()) {
|
||||
// If there are no fixed bounds, then use the default padding and insets
|
||||
setPadding(mInsets.left, mContainerInset + mInsets.top, mInsets.right,
|
||||
mContainerInset + mInsets.bottom);
|
||||
} else {
|
||||
// If there are fixed bounds, then we update the padding to reflect the fixed bounds.
|
||||
setPadding(mFixedBounds.left, mFixedBounds.top, getMeasuredWidth() - mFixedBounds.right,
|
||||
mFixedBounds.bottom);
|
||||
}
|
||||
|
||||
// Update the apps recycler view, inset it by the container inset as well
|
||||
DeviceProfile grid = mLauncher.getDeviceProfile();
|
||||
int startMargin = grid.isPhone ? mContentMarginStart : 0;
|
||||
int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset;
|
||||
if (isRtl) {
|
||||
mAppsRecyclerView.setPadding(inset + mAppsRecyclerView.getScrollbarWidth(), 0,
|
||||
inset + startMargin, 0);
|
||||
} else {
|
||||
mAppsRecyclerView.setPadding(inset + startMargin, 0,
|
||||
inset + mAppsRecyclerView.getScrollbarWidth(), 0);
|
||||
}
|
||||
|
||||
// Update the header bar
|
||||
if (hasSearchBar) {
|
||||
FrameLayout.LayoutParams lp =
|
||||
(FrameLayout.LayoutParams) mHeaderView.getLayoutParams();
|
||||
lp.leftMargin = lp.rightMargin = inset;
|
||||
mHeaderView.requestLayout();
|
||||
}
|
||||
|
||||
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPredictionBarView.getLayoutParams();
|
||||
lp.leftMargin = inset + mAppsRecyclerView.getScrollbarWidth();
|
||||
lp.rightMargin = inset + mAppsRecyclerView.getScrollbarWidth();
|
||||
mPredictionBarView.requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the background of the Apps view and children.
|
||||
*/
|
||||
@Override
|
||||
protected void onUpdateBackgrounds() {
|
||||
int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset;
|
||||
|
||||
// Update the background of the reveal view and list to be inset with the fixed bound
|
||||
// insets instead of the default insets
|
||||
// TODO: Use quantum_panel instead of quantum_panel_shape.
|
||||
InsetDrawable background = new InsetDrawable(
|
||||
getContext().getResources().getDrawable(R.drawable.quantum_panel_shape),
|
||||
inset, 0, inset, 0);
|
||||
mContentView.setBackground(background);
|
||||
mAppsRecyclerView.updateBackgroundPadding(background);
|
||||
mAdapter.updateBackgroundPadding(background);
|
||||
mElevationController.updateBackgroundPadding(background);
|
||||
getRevealView().setBackground(background.getConstantState().newDrawable());
|
||||
getResources().getDrawable(R.drawable.quantum_panel_shape), padding.left, 0,
|
||||
padding.right, 0);
|
||||
mContainerView.setBackground(background);
|
||||
mRevealView.setBackground(background.getConstantState().newDrawable());
|
||||
mAppsRecyclerView.updateBackgroundPadding(padding);
|
||||
mAdapter.updateBackgroundPadding(padding);
|
||||
|
||||
// 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);
|
||||
|
||||
// Pad the recycler view by the background padding plus the start margin (for the section
|
||||
// names)
|
||||
DeviceProfile grid = mLauncher.getDeviceProfile();
|
||||
int startMargin = grid.isPhone ? getResources().getDimensionPixelSize(
|
||||
R.dimen.all_apps_grid_view_start_margin) : mAppsRecyclerView.getScrollbarWidth();
|
||||
if (isRtl) {
|
||||
mAppsRecyclerView.setPadding(padding.left + mAppsRecyclerView.getScrollbarWidth(), 0,
|
||||
padding.right + startMargin, 0);
|
||||
} else {
|
||||
mAppsRecyclerView.setPadding(padding.left + startMargin, 0,
|
||||
padding.right + mAppsRecyclerView.getScrollbarWidth(), 0);
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
// Update the prediction bar insets as well
|
||||
mPredictionBarView = (ViewGroup) findViewById(R.id.prediction_bar);
|
||||
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPredictionBarView.getLayoutParams();
|
||||
lp.leftMargin = padding.left + mAppsRecyclerView.getScrollbarWidth();
|
||||
lp.rightMargin = padding.right + mAppsRecyclerView.getScrollbarWidth();
|
||||
mPredictionBarView.requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
synchronizeToRecyclerViewScrollPosition(mAppsRecyclerView.getScrollPosition());
|
||||
if (mNumAppsPerRow > 0) {
|
||||
// Update the position of the prediction bar to match the scroll of the all apps list
|
||||
synchronizeToRecyclerViewScrollPosition(mAppsRecyclerView.getScrollPosition());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.dispatchKeyEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
return handleTouchEvent(ev);
|
||||
|
@ -569,15 +462,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v == mHeaderView) {
|
||||
showSearchField();
|
||||
} else if (v == mDismissSearchButtonView) {
|
||||
hideSearchField(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
// Return early if this is not initiated from a touch
|
||||
|
@ -660,70 +544,11 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
|
|||
}
|
||||
}
|
||||
|
||||
@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 queryText = s.toString();
|
||||
if (queryText.isEmpty()) {
|
||||
mSearchManager.cancel(true);
|
||||
mApps.setOrderedFilter(null);
|
||||
} else {
|
||||
String formatStr = getResources().getString(R.string.all_apps_no_search_results);
|
||||
mAdapter.setEmptySearchText(String.format(formatStr, queryText));
|
||||
|
||||
mSearchManager.cancel(false);
|
||||
mSearchManager.doSearch(queryText, this);
|
||||
}
|
||||
scrollToTop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSearchResult(ArrayList<ComponentName> apps) {
|
||||
if (apps != null) {
|
||||
mApps.setOrderedFilter(apps);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if (ALLOW_SINGLE_APP_LAUNCH && actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
// Skip the quick-launch if there isn't exactly one item
|
||||
if (mApps.getSize() != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
AlphabeticalAppsList.AdapterItem item = items.get(i);
|
||||
if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) {
|
||||
mAppsRecyclerView.getChildAt(i).performClick();
|
||||
getInputMethodManager().hideSoftInputFromWindow(getWindowToken(), 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdapterItemsChanged() {
|
||||
updatePredictionBarVisibility();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getContent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
|
||||
// Register for a pre-draw listener to synchronize the recycler view scroll to other views
|
||||
|
@ -745,14 +570,12 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
|
|||
|
||||
@Override
|
||||
public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
|
||||
if (mSearchBarEditView != null) {
|
||||
if (toWorkspace) {
|
||||
hideSearchField(false, false);
|
||||
}
|
||||
}
|
||||
if (toWorkspace) {
|
||||
getViewTreeObserver().removeOnPreDrawListener(this);
|
||||
mLastRecyclerViewScrollPos = -1;
|
||||
|
||||
// Reset the search bar after transitioning home
|
||||
mSearchBarController.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -763,9 +586,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
|
|||
private void synchronizeToRecyclerViewScrollPosition(int scrollY) {
|
||||
if (mLastRecyclerViewScrollPos != scrollY) {
|
||||
mLastRecyclerViewScrollPos = scrollY;
|
||||
if (DYNAMIC_HEADER_ELEVATION) {
|
||||
mElevationController.onScroll(scrollY);
|
||||
}
|
||||
|
||||
// Scroll the prediction bar with the contents of the recycler view
|
||||
mPredictionBarView.setTranslationY(-scrollY + mAppsRecyclerView.getPaddingTop());
|
||||
|
@ -806,9 +626,9 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
|
|||
}
|
||||
}
|
||||
|
||||
if (!mFixedBounds.isEmpty()) {
|
||||
if (!mContentBounds.isEmpty()) {
|
||||
// Outset the fixed bounds and check if the touch is outside all apps
|
||||
Rect tmpRect = new Rect(mFixedBounds);
|
||||
Rect tmpRect = new Rect(mContentBounds);
|
||||
tmpRect.inset(-grid.allAppsIconSizePx / 2, 0);
|
||||
if (ev.getX() < tmpRect.left || ev.getX() > tmpRect.right) {
|
||||
mBoundsCheckLastTouchDownPos.set(x, y);
|
||||
|
@ -874,6 +694,29 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSearchResult(String query, ArrayList<ComponentName> apps) {
|
||||
if (apps != null) {
|
||||
if (apps.isEmpty()) {
|
||||
String formatStr = getResources().getString(R.string.all_apps_no_search_results);
|
||||
mAdapter.setEmptySearchText(String.format(formatStr, query));
|
||||
} else {
|
||||
mAppsRecyclerView.scrollToTop();
|
||||
}
|
||||
mApps.setOrderedFilter(apps);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearSearchResult() {
|
||||
mApps.setOrderedFilter(null);
|
||||
|
||||
// Clear the search query
|
||||
mSearchQueryBuilder.clear();
|
||||
mSearchQueryBuilder.clearSpans();
|
||||
Selection.setSelection(mSearchQueryBuilder, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillInLaunchSourceData(Bundle sourceData) {
|
||||
// Since the other cases are caught by the AllAppsRecyclerView LaunchSourceProvider, we just
|
||||
|
@ -889,11 +732,11 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
|
|||
private View findPredictedAppAtCoordinate(int x, int y) {
|
||||
Rect hitRect = new Rect();
|
||||
|
||||
// Ensure we aren't hitting the search bar
|
||||
// Ensure that are touching in the recycler view
|
||||
int[] coord = {x, y};
|
||||
Utilities.mapCoordInSelfToDescendent(mHeaderView, this, coord);
|
||||
mHeaderView.getHitRect(hitRect);
|
||||
if (hitRect.contains(coord[0], coord[1])) {
|
||||
Utilities.mapCoordInSelfToDescendent(mAppsRecyclerView, this, coord);
|
||||
mAppsRecyclerView.getHitRect(hitRect);
|
||||
if (!hitRect.contains(coord[0], coord[1])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -914,95 +757,13 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the search field.
|
||||
*/
|
||||
private void showSearchField() {
|
||||
mSearchManager.connect();
|
||||
|
||||
// Show the search bar and focus the search
|
||||
final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP,
|
||||
getContext().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();
|
||||
getInputMethodManager().showSoftInput(mSearchBarEditView,
|
||||
InputMethodManager.SHOW_IMPLICIT);
|
||||
}
|
||||
});
|
||||
mSearchButtonView.animate()
|
||||
.alpha(0f)
|
||||
.translationX(-translationX)
|
||||
.setDuration(FADE_OUT_DURATION)
|
||||
.withLayer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the search field.
|
||||
*/
|
||||
@Thunk void hideSearchField(boolean animated, final boolean returnFocusToRecyclerView) {
|
||||
mSearchManager.cancel(true);
|
||||
|
||||
final boolean resetTextField = mSearchBarEditView.getText().toString().length() > 0;
|
||||
final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP,
|
||||
getContext().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("");
|
||||
}
|
||||
mApps.setOrderedFilter(null);
|
||||
if (returnFocusToRecyclerView) {
|
||||
mAppsRecyclerView.requestFocus();
|
||||
}
|
||||
}
|
||||
});
|
||||
mSearchButtonView.setTranslationX(-translationX);
|
||||
mSearchButtonView.animate()
|
||||
.alpha(1f)
|
||||
.translationX(0)
|
||||
.setDuration(FADE_OUT_DURATION)
|
||||
.withLayer();
|
||||
} else {
|
||||
mSearchBarContainerView.setVisibility(View.INVISIBLE);
|
||||
if (resetTextField) {
|
||||
mSearchBarEditView.setText("");
|
||||
}
|
||||
mApps.setOrderedFilter(null);
|
||||
mSearchButtonView.setAlpha(1f);
|
||||
mSearchButtonView.setTranslationX(0f);
|
||||
if (returnFocusToRecyclerView) {
|
||||
mAppsRecyclerView.requestFocus();
|
||||
}
|
||||
}
|
||||
getInputMethodManager().hideSoftInputFromWindow(getWindowToken(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the visibility of the prediction bar.
|
||||
* @return whether the prediction bar is visible
|
||||
*/
|
||||
private boolean updatePredictionBarVisibility() {
|
||||
boolean showPredictionBar = !mApps.getPredictedApps().isEmpty() && (!mApps.hasFilter() ||
|
||||
mSearchBarEditView.getEditableText().toString().isEmpty());
|
||||
boolean showPredictionBar = !mApps.getPredictedApps().isEmpty() &&
|
||||
(!mApps.hasFilter() || mSearchBarController.shouldShowPredictionBar());
|
||||
if (showPredictionBar) {
|
||||
mPredictionBarView.setVisibility(View.VISIBLE);
|
||||
} else if (!showPredictionBar) {
|
||||
|
@ -1010,11 +771,4 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
|
|||
}
|
||||
return showPredictionBar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an input method manager.
|
||||
*/
|
||||
@Thunk InputMethodManager getInputMethodManager() {
|
||||
return (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,6 +109,7 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
|
|||
*/
|
||||
public class GridItemDecoration extends RecyclerView.ItemDecoration {
|
||||
|
||||
private static final boolean DEBUG_SECTION_MARGIN = false;
|
||||
private static final boolean FADE_OUT_SECTIONS = false;
|
||||
|
||||
private HashMap<String, PointF> mCachedSectionBounds = new HashMap<>();
|
||||
|
@ -121,10 +122,17 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
|
|||
|
||||
@Override
|
||||
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
|
||||
if (mApps.hasFilter()) {
|
||||
if (mApps.hasFilter() || mAppsPerRow == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG_SECTION_MARGIN) {
|
||||
Paint p = new Paint();
|
||||
p.setColor(0x33ff0000);
|
||||
c.drawRect(mBackgroundPadding.left, 0, mBackgroundPadding.left + mStartMargin,
|
||||
parent.getMeasuredHeight(), p);
|
||||
}
|
||||
|
||||
DeviceProfile grid = mLauncher.getDeviceProfile();
|
||||
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
|
||||
boolean hasDrawnPredictedAppsDivider = false;
|
||||
|
@ -171,8 +179,8 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
|
|||
|
||||
// Calculate where to draw the section
|
||||
int sectionBaseline = (int) (viewTopOffset + sectionBounds.y);
|
||||
int x = mIsRtl ? parent.getWidth() - mPaddingStart - mStartMargin :
|
||||
mPaddingStart;
|
||||
int x = mIsRtl ? parent.getWidth() - mBackgroundPadding.left - mStartMargin :
|
||||
mBackgroundPadding.left;
|
||||
x += (int) ((mStartMargin - sectionBounds.x) / 2f);
|
||||
int y = child.getTop() + sectionBaseline;
|
||||
|
||||
|
@ -301,23 +309,20 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
|
|||
private String mEmptySearchText;
|
||||
|
||||
// Section drawing
|
||||
@Thunk int mPaddingStart;
|
||||
@Thunk int mStartMargin;
|
||||
@Thunk int mSectionHeaderOffset;
|
||||
@Thunk Paint mSectionTextPaint;
|
||||
@Thunk Paint mPredictedAppsDividerPaint;
|
||||
|
||||
public AllAppsGridAdapter(Context context, AlphabeticalAppsList apps, int appsPerRow,
|
||||
public AllAppsGridAdapter(Context context, AlphabeticalAppsList apps,
|
||||
PredictionBarSpacerCallbacks pbCb, View.OnTouchListener touchListener,
|
||||
View.OnClickListener iconClickListener, View.OnLongClickListener iconLongClickListener) {
|
||||
Resources res = context.getResources();
|
||||
mHandler = new Handler();
|
||||
mApps = apps;
|
||||
mAppsPerRow = appsPerRow;
|
||||
mPredictionBarCb = pbCb;
|
||||
mGridSizer = new GridSpanSizer();
|
||||
mGridLayoutMgr = new GridLayoutManager(context, appsPerRow, GridLayoutManager.VERTICAL,
|
||||
false);
|
||||
mGridLayoutMgr = new GridLayoutManager(context, 1, GridLayoutManager.VERTICAL, false);
|
||||
mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
|
||||
mItemDecoration = new GridItemDecoration(context);
|
||||
mLayoutInflater = LayoutInflater.from(context);
|
||||
|
@ -326,7 +331,6 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
|
|||
mIconLongClickListener = iconLongClickListener;
|
||||
mStartMargin = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
|
||||
mSectionHeaderOffset = res.getDimensionPixelSize(R.dimen.all_apps_grid_section_y_offset);
|
||||
mPaddingStart = res.getDimensionPixelSize(R.dimen.all_apps_container_inset);
|
||||
|
||||
mSectionTextPaint = new Paint();
|
||||
mSectionTextPaint.setTextSize(res.getDimensionPixelSize(
|
||||
|
@ -339,7 +343,7 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
|
|||
mPredictedAppsDividerPaint.setColor(0x1E000000);
|
||||
mPredictedAppsDividerPaint.setAntiAlias(true);
|
||||
mPredictionBarBottomPadding =
|
||||
res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_bottom_padding);
|
||||
res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_top_bottom_padding);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -375,8 +379,8 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
|
|||
* Notifies the adapter of the background padding so that it can draw things correctly in the
|
||||
* item decorator.
|
||||
*/
|
||||
public void updateBackgroundPadding(Drawable background) {
|
||||
background.getPadding(mBackgroundPadding);
|
||||
public void updateBackgroundPadding(Rect padding) {
|
||||
mBackgroundPadding.set(padding);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -394,13 +398,6 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
|
|||
return mItemDecoration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the left padding for the recycler view.
|
||||
*/
|
||||
public int getContentMarginStart() {
|
||||
return mStartMargin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
switch (viewType) {
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
package com.android.launcher3.allapps;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
|
@ -40,11 +38,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView
|
|||
private AlphabeticalAppsList mApps;
|
||||
private int mNumAppsPerRow;
|
||||
private int mNumPredictedAppsPerRow;
|
||||
|
||||
private int mPredictionBarHeight;
|
||||
private int mScrollbarMinHeight;
|
||||
|
||||
private Rect mBackgroundPadding = new Rect();
|
||||
|
||||
private Launcher mLauncher;
|
||||
|
||||
|
@ -89,10 +83,6 @@ public class AllAppsRecyclerView extends BaseRecyclerView
|
|||
pool.setMaxRecycledViews(AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE, approxRows);
|
||||
}
|
||||
|
||||
public void updateBackgroundPadding(Drawable background) {
|
||||
background.getPadding(mBackgroundPadding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the prediction bar height.
|
||||
*/
|
||||
|
@ -124,6 +114,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView
|
|||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
|
||||
// Bind event handlers
|
||||
addOnItemTouchListener(this);
|
||||
}
|
||||
|
||||
|
@ -227,8 +219,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView
|
|||
public void updateVerticalScrollbarBounds() {
|
||||
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
|
||||
|
||||
// Skip early if there are no items.
|
||||
if (items.isEmpty()) {
|
||||
// Skip early if there are no items or we haven't been measured
|
||||
if (items.isEmpty() || mNumAppsPerRow == 0) {
|
||||
verticalScrollbarBounds.setEmpty();
|
||||
return;
|
||||
}
|
||||
|
@ -242,8 +234,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView
|
|||
int height = getHeight() - getPaddingTop() - getPaddingBottom();
|
||||
int totalScrollHeight = rowCount * scrollPosState.rowHeight + predictionBarHeight;
|
||||
if (totalScrollHeight > height) {
|
||||
int scrollbarHeight = Math.max(mScrollbarMinHeight,
|
||||
(int) (height / ((float) totalScrollHeight / height)));
|
||||
int scrollbarHeight = (int) (height / ((float) totalScrollHeight / height));
|
||||
|
||||
// Calculate the position and size of the scroll bar
|
||||
if (Utilities.isRtl(getResources())) {
|
||||
|
@ -277,8 +268,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView
|
|||
stateOut.rowTopOffset = -1;
|
||||
stateOut.rowHeight = -1;
|
||||
|
||||
// Return early if there are no items
|
||||
if (items.isEmpty()) {
|
||||
// Return early if there are no items or we haven't been measured
|
||||
if (items.isEmpty() || mNumAppsPerRow == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.graphics.Rect;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* An interface to a search box that AllApps can command.
|
||||
*/
|
||||
public abstract class AllAppsSearchBarController {
|
||||
|
||||
protected AlphabeticalAppsList mApps;
|
||||
protected Callbacks mCb;
|
||||
|
||||
/**
|
||||
* Sets the references to the apps model and the search result callback.
|
||||
*/
|
||||
public final void initialize(AlphabeticalAppsList apps, Callbacks cb) {
|
||||
mApps = apps;
|
||||
mCb = cb;
|
||||
onInitialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* To be overridden by subclasses. This method will get called when the controller is set,
|
||||
* before getView().
|
||||
*/
|
||||
protected abstract void onInitialize();
|
||||
|
||||
/**
|
||||
* Returns the search bar view.
|
||||
* @param parent the parent to attach the search bar view to.
|
||||
*/
|
||||
public abstract View getView(ViewGroup parent);
|
||||
|
||||
/**
|
||||
* Focuses the search field to handle key events.
|
||||
*/
|
||||
public abstract void focusSearchField();
|
||||
|
||||
/**
|
||||
* Returns whether the search field is focused.
|
||||
*/
|
||||
public abstract boolean isSearchFieldFocused();
|
||||
|
||||
/**
|
||||
* Resets the search bar state.
|
||||
*/
|
||||
public abstract void reset();
|
||||
|
||||
/**
|
||||
* Returns whether the prediction bar should currently be visible depending on the state of
|
||||
* the search bar.
|
||||
*/
|
||||
public abstract boolean shouldShowPredictionBar();
|
||||
|
||||
/**
|
||||
* Callback for getting search results.
|
||||
*/
|
||||
public interface Callbacks {
|
||||
|
||||
/**
|
||||
* Called when the bounds of the search bar has changed.
|
||||
*/
|
||||
void onBoundsChanged(Rect newBounds);
|
||||
|
||||
/**
|
||||
* Called when the search is complete.
|
||||
*
|
||||
* @param apps sorted list of matching components or null if in case of failure.
|
||||
*/
|
||||
void onSearchResult(String query, ArrayList<ComponentName> apps);
|
||||
|
||||
/**
|
||||
* Called when the search results should be cleared.
|
||||
*/
|
||||
void clearSearchResult();
|
||||
}
|
||||
}
|
|
@ -235,11 +235,10 @@ public class AlphabeticalAppsList {
|
|||
private int mNumAppsPerRow;
|
||||
private int mNumPredictedAppsPerRow;
|
||||
|
||||
public AlphabeticalAppsList(Context context, int numAppsPerRow, int numPredictedAppsPerRow) {
|
||||
public AlphabeticalAppsList(Context context) {
|
||||
mLauncher = (Launcher) context;
|
||||
mIndexer = new AlphabeticIndexCompat(context);
|
||||
mAppNameComparator = new AppNameComparator(context);
|
||||
setNumAppsPerRow(numAppsPerRow, numPredictedAppsPerRow);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -249,10 +248,6 @@ public class AlphabeticalAppsList {
|
|||
mAdapterChangedCallback = cb;
|
||||
}
|
||||
|
||||
public SimpleAppSearchManagerImpl newSimpleAppSearchManager() {
|
||||
return new SimpleAppSearchManagerImpl(mApps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of apps per row. Used only for AppsContainerView.SECTIONED_GRID_COALESCED.
|
||||
*/
|
||||
|
@ -269,7 +264,7 @@ public class AlphabeticalAppsList {
|
|||
mNumAppsPerRow = numAppsPerRow;
|
||||
mNumPredictedAppsPerRow = numPredictedAppsPerRow;
|
||||
|
||||
onAppsUpdated();
|
||||
updateAdapterItems();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -279,6 +274,13 @@ public class AlphabeticalAppsList {
|
|||
mAdapter = adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the apps.
|
||||
*/
|
||||
public List<AppInfo> getApps() {
|
||||
return mApps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sections of all the current filtered applications.
|
||||
*/
|
||||
|
@ -597,6 +599,11 @@ public class AlphabeticalAppsList {
|
|||
* Merges multiple sections to reduce visual raggedness.
|
||||
*/
|
||||
private void mergeSections() {
|
||||
// Ignore merging until we have a valid row size
|
||||
if (mNumAppsPerRow == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Go through each section and try and merge some of the sections
|
||||
if (AllAppsContainerView.GRID_MERGE_SECTIONS && !hasFilter()) {
|
||||
int sectionAppCount = 0;
|
||||
|
|
|
@ -1,59 +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;
|
||||
|
||||
import android.content.ComponentName;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Interface for handling app search.
|
||||
*/
|
||||
public interface AppSearchManager {
|
||||
|
||||
/**
|
||||
* Called when the search is about to be used. This method is optional for making a query but
|
||||
* calling this appropriately can improve the initial response time.
|
||||
*/
|
||||
void connect();
|
||||
|
||||
/**
|
||||
* Cancels all pending search requests.
|
||||
*
|
||||
* @param interruptActiveRequests if true, any active requests which are already executing will
|
||||
* be invalidated, and the corresponding results will not be sent. The client should usually
|
||||
* set this to true, before beginning a new search session.
|
||||
*/
|
||||
void cancel(boolean interruptActiveRequests);
|
||||
|
||||
/**
|
||||
* Performs a search
|
||||
*/
|
||||
void doSearch(String query, AppSearchResultCallback callback);
|
||||
|
||||
/**
|
||||
* Callback for getting search results.
|
||||
*/
|
||||
public interface AppSearchResultCallback {
|
||||
|
||||
/**
|
||||
* Called when the search is complete.
|
||||
*
|
||||
* @param apps sorted list of matching components or null if in case of failure.
|
||||
*/
|
||||
void onSearchResult(ArrayList<ComponentName> apps);
|
||||
}
|
||||
}
|
|
@ -17,7 +17,6 @@ package com.android.launcher3.allapps;
|
|||
|
||||
import android.content.ComponentName;
|
||||
import android.os.Handler;
|
||||
|
||||
import com.android.launcher3.AppInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -25,39 +24,33 @@ import java.util.List;
|
|||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* An {@link AppSearchManager} which does label matching on the UI thread.
|
||||
* The default search implementation.
|
||||
*/
|
||||
public class SimpleAppSearchManagerImpl implements AppSearchManager {
|
||||
public class DefaultAppSearchAlgorithm {
|
||||
|
||||
private static final Pattern SPLIT_PATTERN = Pattern.compile("[\\s|\\p{javaSpaceChar}]+");
|
||||
|
||||
private final List<AppInfo> mApps;
|
||||
private final Handler mResultHandler;
|
||||
|
||||
public SimpleAppSearchManagerImpl(List<AppInfo> apps) {
|
||||
public DefaultAppSearchAlgorithm(List<AppInfo> apps) {
|
||||
mApps = apps;
|
||||
mResultHandler = new Handler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect() {
|
||||
// No op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel(boolean interruptActiveRequests) {
|
||||
if (interruptActiveRequests) {
|
||||
mResultHandler.removeCallbacksAndMessages(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doSearch(String query, final AppSearchResultCallback callback) {
|
||||
public void doSearch(final String query,
|
||||
final AllAppsSearchBarController.Callbacks callback) {
|
||||
// Do an intersection of the words in the query and each title, and filter out all the
|
||||
// apps that don't match all of the words in the query.
|
||||
final String queryTextLower = query.toLowerCase();
|
||||
final String[] queryWords = SPLIT_PATTERN.split(queryTextLower);
|
||||
final ArrayList<ComponentName> result = new ArrayList<ComponentName>();
|
||||
final ArrayList<ComponentName> result = new ArrayList<>();
|
||||
int total = mApps.size();
|
||||
|
||||
for (int i = 0; i < total; i++) {
|
||||
|
@ -70,7 +63,7 @@ public class SimpleAppSearchManagerImpl implements AppSearchManager {
|
|||
|
||||
@Override
|
||||
public void run() {
|
||||
callback.onSearchResult(result);
|
||||
callback.onSearchResult(query, result);
|
||||
}
|
||||
});
|
||||
}
|
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
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.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 {
|
||||
|
||||
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 AllAppsSearchEditView mSearchBarEditView;
|
||||
@Thunk AllAppsRecyclerView mAppsRecyclerView;
|
||||
private 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 = (AllAppsSearchEditView)
|
||||
mSearchBarContainerView.findViewById(R.id.search_box);
|
||||
mSearchBarEditView.addTextChangedListener(this);
|
||||
mSearchBarEditView.setOnEditorActionListener(this);
|
||||
mSearchBarEditView.setOnBackKeyListener(
|
||||
new AllAppsSearchEditView.OnBackKeyListener() {
|
||||
@Override
|
||||
public void 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 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() {
|
||||
// Keep showing the prediction bar if the input query is empty
|
||||
return mSearchBarEditView.getEditableText().toString().isEmpty();
|
||||
}
|
||||
|
||||
@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_DONE) {
|
||||
return false;
|
||||
}
|
||||
// Skip if there isn't exactly one item
|
||||
if (mApps.getSize() != 1) {
|
||||
return false;
|
||||
}
|
||||
// If there is exactly one icon, then quick-launch it
|
||||
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
AlphabeticalAppsList.AdapterItem item = items.get(i);
|
||||
if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) {
|
||||
mAppsRecyclerView.getChildAt(i).performClick();
|
||||
mInputMethodManager.hideSoftInputFromWindow(
|
||||
mContainerView.getWindowToken(), 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -66,6 +66,7 @@ public class WidgetsContainerView extends BaseContainerView
|
|||
private IconCache mIconCache;
|
||||
|
||||
/* Recycler view related member variables */
|
||||
private View mContent;
|
||||
private WidgetsRecyclerView mView;
|
||||
private WidgetsListAdapter mAdapter;
|
||||
|
||||
|
@ -98,6 +99,7 @@ public class WidgetsContainerView extends BaseContainerView
|
|||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
mContent = findViewById(R.id.content);
|
||||
mView = (WidgetsRecyclerView) findViewById(R.id.widgets_list_view);
|
||||
mView.setAdapter(mAdapter);
|
||||
|
||||
|
@ -112,7 +114,6 @@ public class WidgetsContainerView extends BaseContainerView
|
|||
});
|
||||
mPadding.set(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
|
||||
getPaddingBottom());
|
||||
onUpdatePaddings();
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -335,33 +336,18 @@ public class WidgetsContainerView extends BaseContainerView
|
|||
//
|
||||
|
||||
@Override
|
||||
protected void onUpdatePaddings() {
|
||||
if (mFixedBounds.isEmpty()) {
|
||||
// If there are no fixed bounds, then use the default padding and insets
|
||||
setPadding(mPadding.left + mInsets.left, mPadding.top + mInsets.top,
|
||||
mPadding.right + mInsets.right, mPadding.bottom + mInsets.bottom);
|
||||
} else {
|
||||
// If there are fixed bounds, then we update the padding to reflect the fixed bounds.
|
||||
setPadding(mFixedBounds.left, mFixedBounds.top, getMeasuredWidth() - mFixedBounds.right,
|
||||
mFixedBounds.bottom);
|
||||
}
|
||||
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);
|
||||
|
||||
int inset = mFixedBounds.isEmpty() ? mView.getScrollbarWidth() : mFixedBoundsContainerInset;
|
||||
mView.setPadding(inset + mView.getScrollbarWidth(), inset,
|
||||
inset, inset);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUpdateBackgrounds() {
|
||||
InsetDrawable background;
|
||||
// Update the background of the reveal view and list to be inset with the fixed bound
|
||||
// insets instead of the default insets
|
||||
// TODO: Use quantum_panel instead of quantum_panel_shape.
|
||||
int inset = mFixedBounds.isEmpty() ? mView.getScrollbarWidth() : mFixedBoundsContainerInset;
|
||||
background = new InsetDrawable(
|
||||
getContext().getResources().getDrawable(R.drawable.quantum_panel_shape),
|
||||
inset, 0, inset, 0);
|
||||
mView.updateBackgroundPadding(background);
|
||||
// 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);
|
||||
mView.setBackground(background);
|
||||
getRevealView().setBackground(background.getConstantState().newDrawable());
|
||||
mView.updateBackgroundPadding(padding);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,11 +18,9 @@ package com.android.launcher3.widget;
|
|||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.launcher3.BaseRecyclerView;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.model.WidgetsModel;
|
||||
|
@ -35,7 +33,6 @@ public class WidgetsRecyclerView extends BaseRecyclerView {
|
|||
|
||||
private static final String TAG = "WidgetsRecyclerView";
|
||||
private WidgetsModel mWidgets;
|
||||
private Rect mBackgroundPadding = new Rect();
|
||||
|
||||
public WidgetsRecyclerView(Context context) {
|
||||
this(context, null);
|
||||
|
@ -61,10 +58,6 @@ public class WidgetsRecyclerView extends BaseRecyclerView {
|
|||
addOnItemTouchListener(this);
|
||||
}
|
||||
|
||||
public void updateBackgroundPadding(Drawable background) {
|
||||
background.getPadding(mBackgroundPadding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the widget model in this view, used to determine the fast scroll position.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue