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 17package com.android.launcher3; 18 19import android.content.Context; 20import android.graphics.Canvas; 21import android.util.AttributeSet; 22import android.util.Pair; 23import android.view.View; 24import android.view.ViewParent; 25 26public 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