Home | History | Annotate | Download | only in launcher3
      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 
     43     public FocusIndicatorView(Context context) {
     44         this(context, null);
     45     }
     46 
     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
     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
     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
    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      */
    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 
    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