• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 android.support.v17.leanback.widget;
18 
19 import android.content.Context;
20 import android.graphics.Rect;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 import android.util.AttributeSet;
24 import android.util.Log;
25 import android.view.View;
26 import android.view.ViewGroup;
27 import android.widget.FrameLayout;
28 
29 import java.util.ArrayList;
30 
31 /**
32  * Saves the focused grandchild position.
33  * Helps add persistent focus feature to various ViewGroups.
34  * @hide
35  */
36 class PersistentFocusWrapper extends FrameLayout {
37 
38     private static final String TAG = "PersistentFocusWrapper";
39     private static final boolean DEBUG = false;
40 
41     private int mSelectedPosition = -1;
42 
43     /**
44      * By default, focus is persisted when searching vertically
45      * but not horizontally.
46      */
47     private boolean mPersistFocusVertical = true;
48 
PersistentFocusWrapper(Context context, AttributeSet attrs)49     public PersistentFocusWrapper(Context context, AttributeSet attrs) {
50         super(context, attrs);
51     }
52 
PersistentFocusWrapper(Context context, AttributeSet attrs, int defStyle)53     public PersistentFocusWrapper(Context context, AttributeSet attrs, int defStyle) {
54         super(context, attrs, defStyle);
55     }
56 
getGrandChildCount()57     int getGrandChildCount() {
58         ViewGroup wrapper = (ViewGroup) getChildAt(0);
59         return wrapper == null ? 0 : wrapper.getChildCount();
60     }
61 
62     /**
63      * Clears the selected position and clears focus.
64      */
clearSelection()65     public void clearSelection() {
66         mSelectedPosition = -1;
67         if (hasFocus()) {
68             clearFocus();
69         }
70     }
71 
72     /**
73      * Persist focus when focus search direction is up or down.
74      */
persistFocusVertical()75     public void persistFocusVertical() {
76         mPersistFocusVertical = true;
77     }
78 
79     /**
80      * Persist focus when focus search direction is left or right.
81      */
persistFocusHorizontal()82     public void persistFocusHorizontal() {
83         mPersistFocusVertical = false;
84     }
85 
shouldPersistFocusFromDirection(int direction)86     private boolean shouldPersistFocusFromDirection(int direction) {
87         return ((mPersistFocusVertical && (direction == FOCUS_UP || direction == FOCUS_DOWN)) ||
88                 (!mPersistFocusVertical && (direction == FOCUS_LEFT || direction == FOCUS_RIGHT)));
89     }
90 
91     @Override
addFocusables(ArrayList<View> views, int direction, int focusableMode)92     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
93         if (DEBUG) Log.v(TAG, "addFocusables");
94         if (hasFocus() || getGrandChildCount() == 0 ||
95                 !shouldPersistFocusFromDirection(direction)) {
96             super.addFocusables(views, direction, focusableMode);
97         } else {
98             // Select a child in requestFocus
99             views.add(this);
100         }
101     }
102 
103     @Override
requestChildFocus(View child, View focused)104     public void requestChildFocus(View child, View focused) {
105         super.requestChildFocus(child, focused);
106         View view = focused;
107         while (view != null && view.getParent() != child) {
108             view = (View) view.getParent();
109         }
110         mSelectedPosition = view == null ? -1 : ((ViewGroup) child).indexOfChild(view);
111         if (DEBUG) Log.v(TAG, "requestChildFocus focused " + focused + " mSelectedPosition " + mSelectedPosition);
112     }
113 
114     @Override
requestFocus(int direction, Rect previouslyFocusedRect)115     public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
116         if (DEBUG) Log.v(TAG, "requestFocus mSelectedPosition " + mSelectedPosition);
117         ViewGroup wrapper = (ViewGroup) getChildAt(0);
118         if (wrapper != null && mSelectedPosition >= 0 && mSelectedPosition < getGrandChildCount()) {
119             if (wrapper.getChildAt(mSelectedPosition).requestFocus(
120                     direction, previouslyFocusedRect)) {
121                 return true;
122             }
123         }
124         return super.requestFocus(direction, previouslyFocusedRect);
125     }
126 
127     static class SavedState extends View.BaseSavedState {
128 
129         int mSelectedPosition;
130 
SavedState(Parcel in)131         SavedState(Parcel in) {
132             super(in);
133             mSelectedPosition = in.readInt();
134         }
135 
SavedState(Parcelable superState)136         SavedState(Parcelable superState) {
137             super(superState);
138         }
139 
140         @Override
writeToParcel(Parcel dest, int flags)141         public void writeToParcel(Parcel dest, int flags) {
142             super.writeToParcel(dest, flags);
143             dest.writeInt(mSelectedPosition);
144         }
145 
146         public static final Parcelable.Creator<SavedState> CREATOR
147                 = new Parcelable.Creator<SavedState>() {
148             @Override
149             public SavedState createFromParcel(Parcel in) {
150                 return new SavedState(in);
151             }
152 
153             @Override
154             public SavedState[] newArray(int size) {
155                 return new SavedState[size];
156             }
157         };
158     }
159 
160     @Override
onSaveInstanceState()161     protected Parcelable onSaveInstanceState() {
162         if (DEBUG) Log.v(TAG, "onSaveInstanceState");
163         SavedState savedState = new SavedState(super.onSaveInstanceState());
164         savedState.mSelectedPosition = mSelectedPosition;
165         return savedState;
166     }
167 
168     @Override
onRestoreInstanceState(Parcelable state)169     protected void onRestoreInstanceState(Parcelable state) {
170         if (!(state instanceof SavedState)) {
171             super.onRestoreInstanceState(state);
172             return;
173         }
174         SavedState savedState = (SavedState) state;
175         mSelectedPosition = ((SavedState) state).mSelectedPosition;
176         if (DEBUG) Log.v(TAG, "onRestoreInstanceState mSelectedPosition " + mSelectedPosition);
177         super.onRestoreInstanceState(savedState.getSuperState());
178     }
179 }
180