Merge "Unifying focus indicator handling for workspace and all-apps" into ub-launcher3-calgary
This commit is contained in:
@@ -1,26 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2011 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:state_focused="true">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/focused_background" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</selector>
|
||||
@@ -30,11 +30,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.android.launcher3.FocusIndicatorView
|
||||
android:id="@+id/focus_indicator"
|
||||
android:layout_width="52dp"
|
||||
android:layout_height="52dp" />
|
||||
|
||||
<!-- The workspace contains 5 screens of cells -->
|
||||
<!-- DO NOT CHANGE THE ID -->
|
||||
<com.android.launcher3.Workspace
|
||||
|
||||
@@ -31,11 +31,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.android.launcher3.FocusIndicatorView
|
||||
android:id="@+id/focus_indicator"
|
||||
android:layout_width="52dp"
|
||||
android:layout_height="52dp" />
|
||||
|
||||
<!-- The workspace contains 5 screens of cells -->
|
||||
<!-- DO NOT CHANGE THE ID -->
|
||||
<com.android.launcher3.Workspace
|
||||
|
||||
@@ -30,11 +30,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.android.launcher3.FocusIndicatorView
|
||||
android:id="@+id/focus_indicator"
|
||||
android:layout_width="52dp"
|
||||
android:layout_height="52dp" />
|
||||
|
||||
<!-- The workspace contains 5 screens of cells -->
|
||||
<!-- DO NOT CHANGE THE ID -->
|
||||
<com.android.launcher3.Workspace
|
||||
|
||||
@@ -24,6 +24,5 @@
|
||||
android:paddingTop="@dimen/all_apps_icon_top_bottom_padding"
|
||||
android:paddingBottom="@dimen/all_apps_icon_top_bottom_padding"
|
||||
android:focusable="true"
|
||||
android:background="@drawable/focusable_view_bg"
|
||||
launcher:iconDisplay="all_apps" />
|
||||
|
||||
|
||||
@@ -24,6 +24,5 @@
|
||||
android:paddingTop="@dimen/all_apps_prediction_icon_top_padding"
|
||||
android:paddingBottom="@dimen/all_apps_prediction_icon_bottom_padding"
|
||||
android:focusable="true"
|
||||
android:background="@drawable/focusable_view_bg"
|
||||
launcher:iconDisplay="all_apps" />
|
||||
|
||||
|
||||
@@ -22,27 +22,14 @@
|
||||
android:elevation="5dp"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/folder_content_wrapper"
|
||||
<com.android.launcher3.folder.FolderPagedView
|
||||
android:id="@+id/folder_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<!-- Actual size of the indicator doesn't matter as it is scaled to match the view size -->
|
||||
|
||||
<com.android.launcher3.FocusIndicatorView
|
||||
android:id="@+id/focus_indicator"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp" />
|
||||
|
||||
<com.android.launcher3.folder.FolderPagedView
|
||||
android:id="@+id/folder_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingRight="4dp"
|
||||
android:paddingTop="8dp"
|
||||
launcher:pageIndicator="@+id/folder_page_indicator" />
|
||||
</FrameLayout>
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingRight="4dp"
|
||||
android:paddingTop="8dp"
|
||||
launcher:pageIndicator="@+id/folder_page_indicator" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/folder_footer"
|
||||
|
||||
@@ -22,27 +22,14 @@
|
||||
android:elevation="5dp"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/folder_content_wrapper"
|
||||
<com.android.launcher3.folder.FolderPagedView
|
||||
android:id="@+id/folder_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<!-- Actual size of the indicator doesn't matter as it is scaled to match the view size -->
|
||||
|
||||
<com.android.launcher3.FocusIndicatorView
|
||||
android:id="@+id/focus_indicator"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp" />
|
||||
|
||||
<com.android.launcher3.folder.FolderPagedView
|
||||
android:id="@+id/folder_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="8dp"
|
||||
android:paddingTop="8dp"
|
||||
launcher:pageIndicator="@+id/folder_page_indicator" />
|
||||
</FrameLayout>
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="8dp"
|
||||
android:paddingTop="8dp"
|
||||
launcher:pageIndicator="@+id/folder_page_indicator" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/folder_footer"
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Pair;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.launcher3.util.Thunk;
|
||||
|
||||
public class FocusIndicatorView extends View implements View.OnFocusChangeListener {
|
||||
|
||||
// It can be any number >0. The view is resized using scaleX and scaleY.
|
||||
static final int DEFAULT_LAYOUT_SIZE = 100;
|
||||
|
||||
private static final float MIN_VISIBLE_ALPHA = 0.2f;
|
||||
private static final long ANIM_DURATION = 150;
|
||||
|
||||
private final int[] mIndicatorPos = new int[2];
|
||||
private final int[] mTargetViewPos = new int[2];
|
||||
|
||||
private Animator mCurrentAnimation;
|
||||
private ViewAnimState mTargetState;
|
||||
|
||||
private View mLastFocusedView;
|
||||
private boolean mInitiated;
|
||||
private final OnFocusChangeListener mHideIndicatorOnFocusListener;
|
||||
|
||||
private Pair<View, Boolean> mPendingCall;
|
||||
|
||||
public FocusIndicatorView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public FocusIndicatorView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setAlpha(0);
|
||||
setBackgroundColor(getResources().getColor(R.color.focused_background));
|
||||
|
||||
mHideIndicatorOnFocusListener = new OnFocusChangeListener() {
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
if (hasFocus) {
|
||||
endCurrentAnimation();
|
||||
setAlpha(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
|
||||
// Redraw if it is already showing. This avoids a bug where the height changes by a small
|
||||
// amount on connecting/disconnecting a bluetooth keyboard.
|
||||
if (mLastFocusedView != null) {
|
||||
mPendingCall = Pair.create(mLastFocusedView, Boolean.TRUE);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the alpha of this FocusIndicatorView to 0 when a view with this listener receives focus.
|
||||
*/
|
||||
public View.OnFocusChangeListener getHideIndicatorOnFocusListener() {
|
||||
return mHideIndicatorOnFocusListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
mPendingCall = null;
|
||||
if (!mInitiated && (getWidth() == 0)) {
|
||||
// View not yet laid out. Wait until the view is ready to be drawn, so that be can
|
||||
// get the location on screen.
|
||||
mPendingCall = Pair.create(v, hasFocus);
|
||||
invalidate();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mInitiated) {
|
||||
// The parent view should always the a parent of the target view.
|
||||
computeLocationRelativeToParent(this, (View) getParent(), mIndicatorPos);
|
||||
mInitiated = true;
|
||||
}
|
||||
|
||||
if (hasFocus) {
|
||||
int indicatorWidth = getWidth();
|
||||
int indicatorHeight = getHeight();
|
||||
|
||||
endCurrentAnimation();
|
||||
ViewAnimState nextState = new ViewAnimState();
|
||||
nextState.scaleX = v.getScaleX() * v.getWidth() / indicatorWidth;
|
||||
nextState.scaleY = v.getScaleY() * v.getHeight() / indicatorHeight;
|
||||
|
||||
computeLocationRelativeToParent(v, (View) getParent(), mTargetViewPos);
|
||||
nextState.x = mTargetViewPos[0] - mIndicatorPos[0] - (1 - nextState.scaleX) * indicatorWidth / 2;
|
||||
nextState.y = mTargetViewPos[1] - mIndicatorPos[1] - (1 - nextState.scaleY) * indicatorHeight / 2;
|
||||
|
||||
if (getAlpha() > MIN_VISIBLE_ALPHA) {
|
||||
mTargetState = nextState;
|
||||
mCurrentAnimation = new LauncherViewPropertyAnimator(this)
|
||||
.alpha(1)
|
||||
.translationX(mTargetState.x)
|
||||
.translationY(mTargetState.y)
|
||||
.scaleX(mTargetState.scaleX)
|
||||
.scaleY(mTargetState.scaleY);
|
||||
} else {
|
||||
applyState(nextState);
|
||||
mCurrentAnimation = LauncherAnimUtils.ofPropertyValuesHolder(this,
|
||||
PropertyValuesHolder.ofFloat(View.ALPHA, 1));
|
||||
}
|
||||
mLastFocusedView = v;
|
||||
} else {
|
||||
if (mLastFocusedView == v) {
|
||||
mLastFocusedView = null;
|
||||
endCurrentAnimation();
|
||||
mCurrentAnimation = LauncherAnimUtils.ofPropertyValuesHolder(this,
|
||||
PropertyValuesHolder.ofFloat(View.ALPHA, 0));
|
||||
}
|
||||
}
|
||||
if (mCurrentAnimation != null) {
|
||||
mCurrentAnimation.setDuration(ANIM_DURATION).start();
|
||||
}
|
||||
}
|
||||
|
||||
private void endCurrentAnimation() {
|
||||
if (mCurrentAnimation != null) {
|
||||
mCurrentAnimation.cancel();
|
||||
mCurrentAnimation = null;
|
||||
}
|
||||
if (mTargetState != null) {
|
||||
applyState(mTargetState);
|
||||
mTargetState = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void applyState(ViewAnimState state) {
|
||||
setTranslationX(state.x);
|
||||
setTranslationY(state.y);
|
||||
setScaleX(state.scaleX);
|
||||
setScaleY(state.scaleY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
if (mPendingCall != null) {
|
||||
onFocusChange(mPendingCall.first, mPendingCall.second);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the location of a view relative to {@param parent}, off-setting
|
||||
* any shift due to page view scroll.
|
||||
* @param pos an array of two integers in which to hold the coordinates
|
||||
*/
|
||||
private static void computeLocationRelativeToParent(View v, View parent, int[] pos) {
|
||||
pos[0] = pos[1] = 0;
|
||||
computeLocationRelativeToParentHelper(v, parent, pos);
|
||||
|
||||
// If a view is scaled, its position will also shift accordingly. For optimization, only
|
||||
// consider this for the last node.
|
||||
pos[0] += (1 - v.getScaleX()) * v.getWidth() / 2;
|
||||
pos[1] += (1 - v.getScaleY()) * v.getHeight() / 2;
|
||||
}
|
||||
|
||||
private static void computeLocationRelativeToParentHelper(View child,
|
||||
View commonParent, int[] shift) {
|
||||
View parent = (View) child.getParent();
|
||||
shift[0] += child.getLeft();
|
||||
shift[1] += child.getTop();
|
||||
if (parent instanceof PagedView) {
|
||||
PagedView page = (PagedView) parent;
|
||||
shift[0] -= page.getScrollForPage(page.indexOfChild(child));
|
||||
}
|
||||
|
||||
if (parent != commonParent) {
|
||||
computeLocationRelativeToParentHelper(parent, commonParent, shift);
|
||||
}
|
||||
}
|
||||
|
||||
@Thunk static final class ViewAnimState {
|
||||
float x, y, scaleX, scaleY;
|
||||
}
|
||||
}
|
||||
@@ -107,6 +107,7 @@ import com.android.launcher3.dragndrop.DragView;
|
||||
import com.android.launcher3.dynamicui.ExtractedColors;
|
||||
import com.android.launcher3.folder.Folder;
|
||||
import com.android.launcher3.folder.FolderIcon;
|
||||
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
|
||||
import com.android.launcher3.logging.LoggerUtils;
|
||||
import com.android.launcher3.logging.UserEventDispatcher;
|
||||
import com.android.launcher3.model.WidgetsModel;
|
||||
@@ -363,7 +364,7 @@ public class Launcher extends Activity
|
||||
|
||||
private UserEventDispatcher mUserEventDispatcher;
|
||||
|
||||
public FocusIndicatorView mFocusHandler;
|
||||
public ViewGroupFocusHelper mFocusHandler;
|
||||
private boolean mRotationEnabled = false;
|
||||
|
||||
@Thunk void setOrientation() {
|
||||
@@ -1340,8 +1341,9 @@ public class Launcher extends Activity
|
||||
*/
|
||||
private void setupViews() {
|
||||
mLauncherView = findViewById(R.id.launcher);
|
||||
mFocusHandler = (FocusIndicatorView) findViewById(R.id.focus_indicator);
|
||||
mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
|
||||
mFocusHandler = mDragLayer.getFocusIndicatorHelper();
|
||||
|
||||
mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
|
||||
mPageIndicator = (PageIndicatorLine) mDragLayer.findViewById(R.id.page_indicator);
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ import com.android.launcher3.LauncherTransitionable;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.keyboard.FocusedItemDecorator;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
@@ -311,6 +312,10 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
|
||||
mAppsRecyclerView.addItemDecoration(mItemDecoration);
|
||||
}
|
||||
|
||||
FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(mAppsRecyclerView);
|
||||
mAppsRecyclerView.addItemDecoration(focusedItemDecorator);
|
||||
mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
|
||||
|
||||
// Precalculate the prediction icon and normal icon sizes
|
||||
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
|
||||
final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(
|
||||
|
||||
@@ -33,6 +33,7 @@ import android.support.v7.widget.RecyclerView;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnFocusChangeListener;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
@@ -348,6 +349,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
|
||||
|
||||
private BindViewCallback mBindViewCallback;
|
||||
private AllAppsSearchBarController mSearchController;
|
||||
private OnFocusChangeListener mIconFocusListener;
|
||||
|
||||
// The text to show when there are no search results and no market search handler.
|
||||
private String mEmptySearchMessage;
|
||||
@@ -412,6 +414,10 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
|
||||
}
|
||||
}
|
||||
|
||||
public void setIconFocusListener(OnFocusChangeListener focusListener) {
|
||||
mIconFocusListener = focusListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the last search query that was made, used to show when there are no results and to also
|
||||
* seed the intent for searching the market.
|
||||
@@ -461,26 +467,17 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
|
||||
switch (viewType) {
|
||||
case SECTION_BREAK_VIEW_TYPE:
|
||||
return new ViewHolder(new View(parent.getContext()));
|
||||
case ICON_VIEW_TYPE: {
|
||||
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
|
||||
R.layout.all_apps_icon, parent, false);
|
||||
icon.setOnTouchListener(mTouchListener);
|
||||
icon.setOnClickListener(mIconClickListener);
|
||||
icon.setOnLongClickListener(mIconLongClickListener);
|
||||
icon.setLongPressTimeout(ViewConfiguration.get(parent.getContext())
|
||||
.getLongPressTimeout());
|
||||
icon.setFocusable(true);
|
||||
return new ViewHolder(icon);
|
||||
}
|
||||
case ICON_VIEW_TYPE:
|
||||
case PREDICTION_ICON_VIEW_TYPE: {
|
||||
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
|
||||
R.layout.all_apps_prediction_bar_icon, parent, false);
|
||||
viewType == ICON_VIEW_TYPE ? R.layout.all_apps_icon :
|
||||
R.layout.all_apps_prediction_bar_icon, parent, false);
|
||||
icon.setOnTouchListener(mTouchListener);
|
||||
icon.setOnClickListener(mIconClickListener);
|
||||
icon.setOnLongClickListener(mIconLongClickListener);
|
||||
icon.setLongPressTimeout(ViewConfiguration.get(parent.getContext())
|
||||
.getLongPressTimeout());
|
||||
icon.setFocusable(true);
|
||||
icon.setOnFocusChangeListener(mIconFocusListener);
|
||||
return new ViewHolder(icon);
|
||||
}
|
||||
case EMPTY_SEARCH_VIEW_TYPE:
|
||||
|
||||
@@ -61,6 +61,7 @@ import com.android.launcher3.allapps.AllAppsTransitionController;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.folder.Folder;
|
||||
import com.android.launcher3.folder.FolderIcon;
|
||||
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
|
||||
import com.android.launcher3.util.Thunk;
|
||||
import com.android.launcher3.util.TouchController;
|
||||
|
||||
@@ -111,6 +112,8 @@ public class DragLayer extends InsettableFrameLayout {
|
||||
|
||||
// Related to adjacent page hints
|
||||
private final Rect mScrollChildPosition = new Rect();
|
||||
private final ViewGroupFocusHelper mFocusIndicatorHelper;
|
||||
|
||||
private boolean mInScrollArea;
|
||||
private boolean mShowPageHints;
|
||||
private Drawable mLeftHoverDrawable;
|
||||
@@ -144,6 +147,7 @@ public class DragLayer extends InsettableFrameLayout {
|
||||
mLeftHoverDrawableActive = res.getDrawable(R.drawable.page_hover_left_active);
|
||||
mRightHoverDrawableActive = res.getDrawable(R.drawable.page_hover_right_active);
|
||||
mIsRtl = Utilities.isRtl(res);
|
||||
mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
|
||||
}
|
||||
|
||||
public void setup(Launcher launcher, DragController dragController,
|
||||
@@ -157,6 +161,10 @@ public class DragLayer extends InsettableFrameLayout {
|
||||
onAccessibilityStateChanged(isAccessibilityEnabled);
|
||||
}
|
||||
|
||||
public ViewGroupFocusHelper getFocusIndicatorHelper() {
|
||||
return mFocusIndicatorHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||
return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
|
||||
@@ -966,6 +974,7 @@ public class DragLayer extends InsettableFrameLayout {
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
mFocusIndicatorHelper.draw(canvas);
|
||||
super.dispatchDraw(canvas);
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
|
||||
private static final String TAG = "Launcher.Folder";
|
||||
|
||||
/**
|
||||
* We avoid measuring {@link #mContentWrapper} with a 0 width or height, as this
|
||||
* We avoid measuring {@link #mContent} with a 0 width or height, as this
|
||||
* results in CellLayout being measured as UNSPECIFIED, which it does not support.
|
||||
*/
|
||||
private static final int MIN_CONTENT_DIMEN = 5;
|
||||
@@ -147,7 +147,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
|
||||
@Thunk FolderIcon mFolderIcon;
|
||||
|
||||
@Thunk FolderPagedView mContent;
|
||||
@Thunk View mContentWrapper;
|
||||
public ExtendedEditText mFolderName;
|
||||
private PageIndicatorDots mPageIndicator;
|
||||
|
||||
@@ -226,7 +225,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mContentWrapper = findViewById(R.id.folder_content_wrapper);
|
||||
mContent = (FolderPagedView) findViewById(R.id.folder_content);
|
||||
mContent.setFolder(this);
|
||||
|
||||
@@ -562,8 +560,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
|
||||
reveal.setDuration(mMaterialExpandDuration);
|
||||
reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
|
||||
|
||||
mContentWrapper.setAlpha(0f);
|
||||
Animator iconsAlpha = ObjectAnimator.ofFloat(mContentWrapper, "alpha", 0f, 1f);
|
||||
mContent.setAlpha(0f);
|
||||
Animator iconsAlpha = ObjectAnimator.ofFloat(mContent, "alpha", 0f, 1f);
|
||||
iconsAlpha.setDuration(mMaterialExpandDuration);
|
||||
iconsAlpha.setStartDelay(mMaterialExpandStagger);
|
||||
iconsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
|
||||
@@ -581,12 +579,12 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
|
||||
|
||||
openFolderAnim = anim;
|
||||
|
||||
mContentWrapper.setLayerType(LAYER_TYPE_HARDWARE, null);
|
||||
mContent.setLayerType(LAYER_TYPE_HARDWARE, null);
|
||||
mFooter.setLayerType(LAYER_TYPE_HARDWARE, null);
|
||||
onCompleteRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mContentWrapper.setLayerType(LAYER_TYPE_NONE, null);
|
||||
mContent.setLayerType(LAYER_TYPE_NONE, null);
|
||||
mFooter.setLayerType(LAYER_TYPE_NONE, null);
|
||||
}
|
||||
};
|
||||
@@ -1122,7 +1120,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
|
||||
int contentAreaHeightSpec = MeasureSpec.makeMeasureSpec(contentHeight, MeasureSpec.EXACTLY);
|
||||
|
||||
mContent.setFixedSize(contentWidth, contentHeight);
|
||||
mContentWrapper.measure(contentAreaWidthSpec, contentAreaHeightSpec);
|
||||
mContent.measure(contentAreaWidthSpec, contentAreaHeightSpec);
|
||||
|
||||
if (mContent.getChildCount() > 0) {
|
||||
int cellIconGap = (mContent.getPageAt(0).getCellWidth()
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.launcher3.folder;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
@@ -30,7 +31,6 @@ import com.android.launcher3.BubbleTextView;
|
||||
import com.android.launcher3.CellLayout;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.FocusHelper.PagedFolderKeyEventListener;
|
||||
import com.android.launcher3.FocusIndicatorView;
|
||||
import com.android.launcher3.IconCache;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.ItemInfo;
|
||||
@@ -45,6 +45,7 @@ import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.Workspace.ItemOperator;
|
||||
import com.android.launcher3.dragndrop.DragController;
|
||||
import com.android.launcher3.pageindicators.PageIndicator;
|
||||
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
|
||||
import com.android.launcher3.util.Thunk;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -73,6 +74,7 @@ public class FolderPagedView extends PagedView {
|
||||
|
||||
private final LayoutInflater mInflater;
|
||||
private final IconCache mIconCache;
|
||||
private final ViewGroupFocusHelper mFocusIndicatorHelper;
|
||||
|
||||
@Thunk final HashMap<View, Runnable> mPendingAnimations = new HashMap<>();
|
||||
|
||||
@@ -90,7 +92,6 @@ public class FolderPagedView extends PagedView {
|
||||
private int mGridCountY;
|
||||
|
||||
private Folder mFolder;
|
||||
private FocusIndicatorView mFocusIndicatorView;
|
||||
private PagedFolderKeyEventListener mKeyListener;
|
||||
|
||||
private PageIndicator mPageIndicator;
|
||||
@@ -112,11 +113,11 @@ public class FolderPagedView extends PagedView {
|
||||
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
|
||||
|
||||
setEdgeGlowColor(getResources().getColor(R.color.folder_edge_effect_color));
|
||||
mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
|
||||
}
|
||||
|
||||
public void setFolder(Folder folder) {
|
||||
mFolder = folder;
|
||||
mFocusIndicatorView = (FocusIndicatorView) folder.findViewById(R.id.focus_indicator);
|
||||
mKeyListener = new PagedFolderKeyEventListener(folder);
|
||||
mPageIndicator = (PageIndicator) folder.findViewById(R.id.folder_page_indicator);
|
||||
}
|
||||
@@ -162,6 +163,12 @@ public class FolderPagedView extends PagedView {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
mFocusIndicatorHelper.draw(canvas);
|
||||
super.dispatchDraw(canvas);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds items to the layout.
|
||||
* @return list of items that could not be bound, probably because we hit the max size limit.
|
||||
@@ -226,7 +233,7 @@ public class FolderPagedView extends PagedView {
|
||||
textView.applyFromShortcutInfo(item, mIconCache);
|
||||
textView.setOnClickListener(mFolder);
|
||||
textView.setOnLongClickListener(mFolder);
|
||||
textView.setOnFocusChangeListener(mFocusIndicatorView);
|
||||
textView.setOnFocusChangeListener(mFocusIndicatorHelper);
|
||||
textView.setOnKeyListener(mKeyListener);
|
||||
|
||||
textView.setLayoutParams(new CellLayout.LayoutParams(
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.keyboard;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.animation.RectEvaluator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.animation.ValueAnimator.AnimatorUpdateListener;
|
||||
import android.annotation.TargetApi;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.util.Property;
|
||||
import android.view.View;
|
||||
import android.view.View.OnFocusChangeListener;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
|
||||
/**
|
||||
* A helper class to draw background of a focused view.
|
||||
*/
|
||||
@TargetApi(VERSION_CODES.LOLLIPOP)
|
||||
public abstract class FocusIndicatorHelper implements
|
||||
OnFocusChangeListener, AnimatorUpdateListener {
|
||||
|
||||
private static final float MIN_VISIBLE_ALPHA = 0.2f;
|
||||
private static final long ANIM_DURATION = 150;
|
||||
|
||||
public static final Property<FocusIndicatorHelper, Float> ALPHA =
|
||||
new Property<FocusIndicatorHelper, Float>(Float.TYPE, "alpha") {
|
||||
@Override
|
||||
public void set(FocusIndicatorHelper object, Float value) {
|
||||
object.setAlpha(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(FocusIndicatorHelper object) {
|
||||
return object.mAlpha;
|
||||
}
|
||||
};
|
||||
|
||||
public static final Property<FocusIndicatorHelper, Float> SHIFT =
|
||||
new Property<FocusIndicatorHelper, Float>(
|
||||
Float.TYPE, "shift") {
|
||||
|
||||
@Override
|
||||
public void set(FocusIndicatorHelper object, Float value) {
|
||||
object.mShift = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(FocusIndicatorHelper object) {
|
||||
return object.mShift;
|
||||
}
|
||||
};
|
||||
|
||||
private static final RectEvaluator RECT_EVALUATOR = new RectEvaluator(new Rect());
|
||||
private static final Rect sTempRect1 = new Rect();
|
||||
private static final Rect sTempRect2 = new Rect();
|
||||
|
||||
private final View mContainer;
|
||||
private final Paint mPaint;
|
||||
private final int mMaxAlpha;
|
||||
|
||||
private final Rect mDirtyRect = new Rect();
|
||||
private boolean mIsDirty = false;
|
||||
|
||||
private View mLastFocusedView;
|
||||
|
||||
private View mCurrentView;
|
||||
private View mTargetView;
|
||||
/**
|
||||
* The fraction indicating the position of the focusRect between {@link #mCurrentView}
|
||||
* & {@link #mTargetView}
|
||||
*/
|
||||
private float mShift;
|
||||
|
||||
private ObjectAnimator mCurrentAnimation;
|
||||
private float mAlpha;
|
||||
|
||||
public FocusIndicatorHelper(View container) {
|
||||
mContainer = container;
|
||||
|
||||
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
int color = container.getResources().getColor(R.color.focused_background);
|
||||
mMaxAlpha = Color.alpha(color);
|
||||
mPaint.setColor(0xFF000000 | color);
|
||||
|
||||
setAlpha(0);
|
||||
mShift = 0;
|
||||
}
|
||||
|
||||
protected void setAlpha(float alpha) {
|
||||
mAlpha = alpha;
|
||||
mPaint.setAlpha((int) (mAlpha * mMaxAlpha));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
invalidateDirty();
|
||||
}
|
||||
|
||||
protected void invalidateDirty() {
|
||||
if (mIsDirty) {
|
||||
mContainer.invalidate(mDirtyRect);
|
||||
mIsDirty = false;
|
||||
}
|
||||
|
||||
Rect newRect = getDrawRect();
|
||||
if (newRect != null) {
|
||||
mContainer.invalidate(newRect);
|
||||
}
|
||||
}
|
||||
|
||||
public void draw(Canvas c) {
|
||||
if (mAlpha > 0) {
|
||||
Rect newRect = getDrawRect();
|
||||
if (newRect != null) {
|
||||
mDirtyRect.set(newRect);
|
||||
c.drawRect(mDirtyRect, mPaint);
|
||||
mIsDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Rect getDrawRect() {
|
||||
if (mCurrentView != null) {
|
||||
viewToRect(mCurrentView, sTempRect1);
|
||||
|
||||
if (mShift > 0 && mTargetView != null) {
|
||||
viewToRect(mTargetView, sTempRect2);
|
||||
return RECT_EVALUATOR.evaluate(mShift, sTempRect1, sTempRect2);
|
||||
} else {
|
||||
return sTempRect1;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
if (hasFocus) {
|
||||
endCurrentAnimation();
|
||||
|
||||
if (mAlpha > MIN_VISIBLE_ALPHA) {
|
||||
mTargetView = v;
|
||||
|
||||
mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
|
||||
PropertyValuesHolder.ofFloat(ALPHA, 1),
|
||||
PropertyValuesHolder.ofFloat(SHIFT, 1));
|
||||
mCurrentAnimation.addListener(new ViewSetListener(v, true));
|
||||
} else {
|
||||
setCurrentView(v);
|
||||
|
||||
mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
|
||||
PropertyValuesHolder.ofFloat(ALPHA, 1));
|
||||
}
|
||||
|
||||
mLastFocusedView = v;
|
||||
} else {
|
||||
if (mLastFocusedView == v) {
|
||||
mLastFocusedView = null;
|
||||
endCurrentAnimation();
|
||||
mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
|
||||
PropertyValuesHolder.ofFloat(ALPHA, 0));
|
||||
mCurrentAnimation.addListener(new ViewSetListener(null, false));
|
||||
}
|
||||
}
|
||||
|
||||
// invalidate once
|
||||
invalidateDirty();
|
||||
|
||||
mLastFocusedView = hasFocus ? v : null;
|
||||
if (mCurrentAnimation != null) {
|
||||
mCurrentAnimation.addUpdateListener(this);
|
||||
mCurrentAnimation.setDuration(ANIM_DURATION).start();
|
||||
}
|
||||
}
|
||||
|
||||
protected void endCurrentAnimation() {
|
||||
if (mCurrentAnimation != null) {
|
||||
mCurrentAnimation.cancel();
|
||||
mCurrentAnimation = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected void setCurrentView(View v) {
|
||||
mCurrentView = v;
|
||||
mShift = 0;
|
||||
mTargetView = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the position of {@param v} relative to {@link #mContainer}.
|
||||
*/
|
||||
public abstract void viewToRect(View v, Rect outRect);
|
||||
|
||||
private class ViewSetListener extends AnimatorListenerAdapter {
|
||||
private final View mViewToSet;
|
||||
private final boolean mCallOnCancel;
|
||||
private boolean mCalled = false;
|
||||
|
||||
public ViewSetListener(View v, boolean callOnCancel) {
|
||||
mViewToSet = v;
|
||||
mCallOnCancel = callOnCancel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
if (!mCallOnCancel) {
|
||||
mCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (!mCalled) {
|
||||
setCurrentView(mViewToSet);
|
||||
mCalled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.keyboard;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.RecyclerView.ItemDecoration;
|
||||
import android.support.v7.widget.RecyclerView.State;
|
||||
import android.view.View;
|
||||
import android.view.View.OnFocusChangeListener;
|
||||
|
||||
/**
|
||||
* {@link ItemDecoration} for drawing and animating focused view background.
|
||||
*/
|
||||
public class FocusedItemDecorator extends ItemDecoration {
|
||||
|
||||
private FocusIndicatorHelper mHelper;
|
||||
|
||||
public FocusedItemDecorator(View container) {
|
||||
mHelper = new FocusIndicatorHelper(container) {
|
||||
|
||||
@Override
|
||||
public void viewToRect(View v, Rect outRect) {
|
||||
outRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public OnFocusChangeListener getFocusListener() {
|
||||
return mHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas c, RecyclerView parent, State state) {
|
||||
mHelper.draw(c);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.keyboard;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.view.View;
|
||||
import android.view.View.OnFocusChangeListener;
|
||||
|
||||
import com.android.launcher3.PagedView;
|
||||
|
||||
/**
|
||||
* {@link FocusIndicatorHelper} for a generic view group.
|
||||
*/
|
||||
public class ViewGroupFocusHelper extends FocusIndicatorHelper {
|
||||
|
||||
private final View mContainer;
|
||||
|
||||
public ViewGroupFocusHelper(View container) {
|
||||
super(container);
|
||||
mContainer = container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void viewToRect(View v, Rect outRect) {
|
||||
outRect.left = 0;
|
||||
outRect.top = 0;
|
||||
|
||||
computeLocationRelativeToContainer(v, outRect);
|
||||
|
||||
// If a view is scaled, its position will also shift accordingly. For optimization, only
|
||||
// consider this for the last node.
|
||||
outRect.left += (1 - v.getScaleX()) * v.getWidth() / 2;
|
||||
outRect.top += (1 - v.getScaleY()) * v.getHeight() / 2;
|
||||
|
||||
outRect.right = outRect.left + (int) (v.getScaleX() * v.getWidth());
|
||||
outRect.bottom = outRect.top + (int) (v.getScaleY() * v.getHeight());
|
||||
}
|
||||
|
||||
private void computeLocationRelativeToContainer(View child, Rect outRect) {
|
||||
View parent = (View) child.getParent();
|
||||
outRect.left += child.getLeft();
|
||||
outRect.top += child.getTop();
|
||||
|
||||
if (parent != mContainer) {
|
||||
if (parent instanceof PagedView) {
|
||||
PagedView page = (PagedView) parent;
|
||||
outRect.left -= page.getScrollForPage(page.indexOfChild(child));
|
||||
}
|
||||
|
||||
computeLocationRelativeToContainer(parent, outRect);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the alpha of this FocusIndicatorHelper to 0 when a view with this listener
|
||||
* receives focus.
|
||||
*/
|
||||
public View.OnFocusChangeListener getHideIndicatorOnFocusListener() {
|
||||
return new OnFocusChangeListener() {
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
if (hasFocus) {
|
||||
endCurrentAnimation();
|
||||
setCurrentView(null);
|
||||
setAlpha(0);
|
||||
invalidateDirty();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user