summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
diff options
context:
space:
mode:
authorSunny Goyal <sunnygoyal@google.com>2016-05-09 20:43:21 -0700
committerSunny Goyal <sunnygoyal@google.com>2016-06-08 15:00:09 -0700
commit3333b0ced8e6743c41909f6f6b916f1f9ec5a004 (patch)
tree7fd735821e3393a0dfe8691087f58ebbaa9dafae /src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
parentab06999a70c773ffbc0aadaf938e0e90f8ca09b2 (diff)
downloadandroid_packages_apps_Trebuchet-3333b0ced8e6743c41909f6f6b916f1f9ec5a004.tar.gz
android_packages_apps_Trebuchet-3333b0ced8e6743c41909f6f6b916f1f9ec5a004.tar.bz2
android_packages_apps_Trebuchet-3333b0ced8e6743c41909f6f6b916f1f9ec5a004.zip
Unifying focus indicator handling for workspace and all-apps
Adding an abstract FocusIndicatorHelper based on FocusIndicatorView which draws the background instead of using a dummy view. Change-Id: Id560195323d2ddad8fcd77ba675cf3f4fd4a94ab
Diffstat (limited to 'src/com/android/launcher3/keyboard/FocusIndicatorHelper.java')
-rw-r--r--src/com/android/launcher3/keyboard/FocusIndicatorHelper.java242
1 files changed, 242 insertions, 0 deletions
diff --git a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
new file mode 100644
index 000000000..7672f5a79
--- /dev/null
+++ b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
@@ -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;
+ }
+ }
+ }
+}