blob: 4ae1be0ece2300cff0b14ab0302c53b6419d33b5 [file] [log] [blame]
/*
* 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());
}
}