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