| /* |
| * Copyright (C) 2015 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 android.support.v17.leanback.widget; |
| |
| import android.content.Context; |
| import android.graphics.Rect; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| import android.util.AttributeSet; |
| import android.util.Log; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.widget.FrameLayout; |
| |
| import java.util.ArrayList; |
| |
| /** |
| * Saves the focused grandchild position. |
| * Helps add persistent focus feature to various ViewGroups. |
| * @hide |
| */ |
| class PersistentFocusWrapper extends FrameLayout { |
| |
| private static final String TAG = "PersistentFocusWrapper"; |
| private static final boolean DEBUG = false; |
| |
| private int mSelectedPosition = -1; |
| |
| /** |
| * By default, focus is persisted when searching vertically |
| * but not horizontally. |
| */ |
| private boolean mPersistFocusVertical = true; |
| |
| public PersistentFocusWrapper(Context context, AttributeSet attrs) { |
| super(context, attrs); |
| } |
| |
| public PersistentFocusWrapper(Context context, AttributeSet attrs, int defStyle) { |
| super(context, attrs, defStyle); |
| } |
| |
| int getGrandChildCount() { |
| ViewGroup wrapper = (ViewGroup) getChildAt(0); |
| return wrapper == null ? 0 : wrapper.getChildCount(); |
| } |
| |
| /** |
| * Clears the selected position and clears focus. |
| */ |
| public void clearSelection() { |
| mSelectedPosition = -1; |
| if (hasFocus()) { |
| clearFocus(); |
| } |
| } |
| |
| /** |
| * Persist focus when focus search direction is up or down. |
| */ |
| public void persistFocusVertical() { |
| mPersistFocusVertical = true; |
| } |
| |
| /** |
| * Persist focus when focus search direction is left or right. |
| */ |
| public void persistFocusHorizontal() { |
| mPersistFocusVertical = false; |
| } |
| |
| private boolean shouldPersistFocusFromDirection(int direction) { |
| return ((mPersistFocusVertical && (direction == FOCUS_UP || direction == FOCUS_DOWN)) || |
| (!mPersistFocusVertical && (direction == FOCUS_LEFT || direction == FOCUS_RIGHT))); |
| } |
| |
| @Override |
| public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { |
| if (DEBUG) Log.v(TAG, "addFocusables"); |
| if (hasFocus() || getGrandChildCount() == 0 || |
| !shouldPersistFocusFromDirection(direction)) { |
| super.addFocusables(views, direction, focusableMode); |
| } else { |
| // Select a child in requestFocus |
| views.add(this); |
| } |
| } |
| |
| @Override |
| public void requestChildFocus(View child, View focused) { |
| super.requestChildFocus(child, focused); |
| View view = focused; |
| while (view != null && view.getParent() != child) { |
| view = (View) view.getParent(); |
| } |
| mSelectedPosition = view == null ? -1 : ((ViewGroup) child).indexOfChild(view); |
| if (DEBUG) Log.v(TAG, "requestChildFocus focused " + focused + " mSelectedPosition " + mSelectedPosition); |
| } |
| |
| @Override |
| public boolean requestFocus(int direction, Rect previouslyFocusedRect) { |
| if (DEBUG) Log.v(TAG, "requestFocus mSelectedPosition " + mSelectedPosition); |
| ViewGroup wrapper = (ViewGroup) getChildAt(0); |
| if (wrapper != null && mSelectedPosition >= 0 && mSelectedPosition < getGrandChildCount()) { |
| if (wrapper.getChildAt(mSelectedPosition).requestFocus( |
| direction, previouslyFocusedRect)) { |
| return true; |
| } |
| } |
| return super.requestFocus(direction, previouslyFocusedRect); |
| } |
| |
| static class SavedState extends View.BaseSavedState { |
| |
| int mSelectedPosition; |
| |
| SavedState(Parcel in) { |
| super(in); |
| mSelectedPosition = in.readInt(); |
| } |
| |
| SavedState(Parcelable superState) { |
| super(superState); |
| } |
| |
| @Override |
| public void writeToParcel(Parcel dest, int flags) { |
| super.writeToParcel(dest, flags); |
| dest.writeInt(mSelectedPosition); |
| } |
| |
| public static final Parcelable.Creator<SavedState> CREATOR |
| = new Parcelable.Creator<SavedState>() { |
| @Override |
| public SavedState createFromParcel(Parcel in) { |
| return new SavedState(in); |
| } |
| |
| @Override |
| public SavedState[] newArray(int size) { |
| return new SavedState[size]; |
| } |
| }; |
| } |
| |
| @Override |
| protected Parcelable onSaveInstanceState() { |
| if (DEBUG) Log.v(TAG, "onSaveInstanceState"); |
| SavedState savedState = new SavedState(super.onSaveInstanceState()); |
| savedState.mSelectedPosition = mSelectedPosition; |
| return savedState; |
| } |
| |
| @Override |
| protected void onRestoreInstanceState(Parcelable state) { |
| SavedState savedState = (SavedState) state; |
| mSelectedPosition = ((SavedState) state).mSelectedPosition; |
| if (DEBUG) Log.v(TAG, "onRestoreInstanceState mSelectedPosition " + mSelectedPosition); |
| super.onRestoreInstanceState(savedState.getSuperState()); |
| } |
| } |