• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.launcher3;
18 
19 import android.content.Context;
20 import android.graphics.Canvas;
21 import android.util.AttributeSet;
22 import android.util.Pair;
23 import android.view.View;
24 import android.view.ViewParent;
25 
26 public class FocusIndicatorView extends View implements View.OnFocusChangeListener {
27 
28     // It can be any number >0. The view is resized using scaleX and scaleY.
29     static final int DEFAULT_LAYOUT_SIZE = 100;
30     private static final float MIN_VISIBLE_ALPHA = 0.2f;
31 
32     private static final int[] sTempPos = new int[2];
33     private static final int[] sTempShift = new int[2];
34 
35     private final int[] mIndicatorPos = new int[2];
36     private final int[] mTargetViewPos = new int[2];
37 
38     private View mLastFocusedView;
39     private boolean mInitiated;
40 
41     private Pair<View, Boolean> mPendingCall;
42 
FocusIndicatorView(Context context)43     public FocusIndicatorView(Context context) {
44         this(context, null);
45     }
46 
FocusIndicatorView(Context context, AttributeSet attrs)47     public FocusIndicatorView(Context context, AttributeSet attrs) {
48         super(context, attrs);
49         setAlpha(0);
50         setBackgroundColor(getResources().getColor(R.color.focused_background));
51     }
52 
53     @Override
onSizeChanged(int w, int h, int oldw, int oldh)54     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
55         super.onSizeChanged(w, h, oldw, oldh);
56 
57         // Redraw if it is already showing. This avoids a bug where the height changes by a small
58         // amount on connecting/disconnecting a bluetooth keyboard.
59         if (mLastFocusedView != null) {
60             mPendingCall = Pair.create(mLastFocusedView, Boolean.TRUE);
61             invalidate();
62         }
63     }
64 
65     @Override
onFocusChange(View v, boolean hasFocus)66     public void onFocusChange(View v, boolean hasFocus) {
67         mPendingCall = null;
68         if (!mInitiated && (getWidth() == 0)) {
69             // View not yet laid out. Wait until the view is ready to be drawn, so that be can
70             // get the location on screen.
71             mPendingCall = Pair.create(v, hasFocus);
72             invalidate();
73             return;
74         }
75 
76         if (!mInitiated) {
77             getLocationRelativeToParentPagedView(this, mIndicatorPos);
78             mInitiated = true;
79         }
80 
81         if (hasFocus) {
82             int indicatorWidth = getWidth();
83             int indicatorHeight = getHeight();
84 
85             float scaleX = v.getScaleX() * v.getWidth() / indicatorWidth;
86             float scaleY = v.getScaleY() * v.getHeight() / indicatorHeight;
87 
88             getLocationRelativeToParentPagedView(v, mTargetViewPos);
89             float x = mTargetViewPos[0] - mIndicatorPos[0] - (1 - scaleX) * indicatorWidth / 2;
90             float y = mTargetViewPos[1] - mIndicatorPos[1] - (1 - scaleY) * indicatorHeight / 2;
91 
92             if (getAlpha() > MIN_VISIBLE_ALPHA) {
93                 animate()
94                 .translationX(x)
95                 .translationY(y)
96                 .scaleX(scaleX)
97                 .scaleY(scaleY)
98                 .alpha(1);
99             } else {
100                 setTranslationX(x);
101                 setTranslationY(y);
102                 setScaleX(scaleX);
103                 setScaleY(scaleY);
104                 animate().alpha(1);
105             }
106             mLastFocusedView = v;
107         } else {
108             if (mLastFocusedView == v) {
109                 mLastFocusedView = null;
110                 animate().alpha(0);
111             }
112         }
113     }
114 
115     @Override
onDraw(Canvas canvas)116     protected void onDraw(Canvas canvas) {
117         if (mPendingCall != null) {
118             onFocusChange(mPendingCall.first, mPendingCall.second);
119         }
120     }
121 
122     /**
123      * Gets the location of a view relative in the window, off-setting any shift due to
124      * page view scroll
125      */
getLocationRelativeToParentPagedView(View v, int[] pos)126     private static void getLocationRelativeToParentPagedView(View v, int[] pos) {
127         getPagedViewScrollShift(v, sTempShift);
128         v.getLocationInWindow(sTempPos);
129         pos[0] = sTempPos[0] + sTempShift[0];
130         pos[1] = sTempPos[1] + sTempShift[1];
131     }
132 
getPagedViewScrollShift(View child, int[] shift)133     private static void getPagedViewScrollShift(View child, int[] shift) {
134         ViewParent parent = child.getParent();
135         if (parent instanceof PagedView) {
136             View parentView = (View) parent;
137             child.getLocationInWindow(sTempPos);
138             shift[0] = parentView.getPaddingLeft() - sTempPos[0];
139             shift[1] = -(int) child.getTranslationY();
140         } else if (parent instanceof View) {
141             getPagedViewScrollShift((View) parent, shift);
142         } else {
143             shift[0] = shift[1] = 0;
144         }
145     }
146 }
147