1 /*
2  * Copyright (C) 2014 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 com.android.tv.settings.dialog.old;
18 
19 import android.os.Bundle;
20 
21 import com.android.tv.settings.widget.ScrollAdapterView;
22 
23 import java.util.ArrayList;
24 
25 /**
26  * Subclass of ScrollAdapterFragment which handles actions.
27  * <p>
28  * Users should instantiate using {@link #newInstance(ArrayList, String)}. To learn when items are
29  * clicked, the activity should implement {@link ActionAdapter.Listener}. <br/>
30  * fragments need to call {@link #setListener(ActionAdapter.Listener)} to call their custom listener
31  */
32 public class BaseActionFragment extends BaseScrollAdapterFragment
33         implements ActionAdapter.Listener, ActionAdapter.OnFocusListener,
34         ActionAdapter.OnKeyListener {
35 
36     private final LiteFragment mFragment;
37 
38     /**
39      * Key for a string name for the fragment.
40      */
41     private static final String EXTRA_NAME = "name";
42 
43     /**
44      * Key for a parcelable array of actions.
45      */
46     private static final String EXTRA_ACTIONS = "actions";
47 
48     /**
49      * Key for the selected item index.
50      */
51     private static final String EXTRA_INDEX = "index";
52 
53     /**
54      * Optional name of the fragment.
55      */
56     private String mName;
57     private ActionAdapter mAdapter;
58     private boolean mAddedSavedActions;
59 
60     /**
61      * If {@code true}, select the first checked item after populating.
62      */
63     private boolean mSelectFirstChecked;
64 
65     private int mIndexToSelect;
66 
67     private ActionAdapter.Listener mListener = null;
68 
BaseActionFragment(LiteFragment fragment)69     public BaseActionFragment(LiteFragment fragment) {
70         super(fragment);
71         mFragment = fragment;
72         mIndexToSelect = -1;
73         mSelectFirstChecked = true;
74     }
75 
76     /**
77      * Creates a new action fragment with the given list of actions and a given name.
78      */
buildArgs(ArrayList<Action> actions, String name)79     public static Bundle buildArgs(ArrayList<Action> actions, String name) {
80         return buildArgs(actions, name, -1);
81     }
82 
83     /**
84      * Creates a new action fragment with the given list of actions and starting index.
85      */
buildArgs(ArrayList<Action> actions, int index)86     public static Bundle buildArgs(ArrayList<Action> actions, int index) {
87         return buildArgs(actions, null, index);
88     }
89 
90     /**
91      * Creates a new action fragment with the given list of actions, given name and starting index.
92      */
buildArgs(ArrayList<Action> actions, String name, int index)93     public static Bundle buildArgs(ArrayList<Action> actions, String name, int index) {
94         Bundle args = new Bundle();
95         args.putParcelableArrayList(EXTRA_ACTIONS, actions);
96         args.putString(EXTRA_NAME, name);
97         args.putInt(EXTRA_INDEX, index);
98         return args;
99     }
100 
onCreate(Bundle savedInstanceState)101     public void onCreate(Bundle savedInstanceState) {
102         mAdapter = new ActionAdapter(mFragment.getActivity());
103         mAddedSavedActions = false;
104         if (savedInstanceState != null) {
105             ArrayList<Action> actions = savedInstanceState.getParcelableArrayList(EXTRA_ACTIONS);
106             int savedIndex = savedInstanceState.getInt(EXTRA_INDEX, -1);
107             if (actions != null) {
108                 for (Action action : actions) {
109                     mAdapter.addAction(action);
110                 }
111                 if (savedIndex >= 0 && savedIndex < actions.size()) {
112                     mIndexToSelect = savedIndex;
113                 }
114                 mAddedSavedActions = true;
115             }
116         } else {
117             int startIndex = mFragment.getArguments().getInt(EXTRA_INDEX, -1);
118             if (startIndex != -1) {
119                 // When first launching action fragment and start index is not -1, set it to
120                 // mIndexToSelect.
121                 mIndexToSelect = startIndex;
122             }
123         }
124         mName = mFragment.getArguments().getString(EXTRA_NAME);
125         loadActionsFromArgumentsIfNecessary();
126         mAdapter.setListener(this);
127         mAdapter.setOnFocusListener(this);
128         mAdapter.setOnKeyListener(this);
129     }
130 
onResume()131     public void onResume() {
132         // ensure the list is built.
133         ScrollAdapterView sav = getScrollAdapterView();
134 
135         sav.addOnScrollListener(mAdapter);
136         if (getAdapter() != mAdapter) {
137             mAdapter.setScrollAdapterView(sav);
138             setAdapter(mAdapter);
139         }
140         if (mIndexToSelect != -1) {
141             getScrollAdapterView().setSelection(mIndexToSelect);
142             mIndexToSelect = -1; // reset this.
143         }
144     }
145 
onSaveInstanceState(Bundle outState)146     public void onSaveInstanceState(Bundle outState) {
147         if (hasCreatedView()) {
148             // Try to save instance state only if the view has already been created.
149             outState.putParcelableArrayList(EXTRA_ACTIONS, mAdapter.getActions());
150             outState.putInt(EXTRA_INDEX, getScrollAdapterView().getSelectedItemPosition());
151         }
152     }
153 
154     /**
155      * If the custom lister has been set using {@link #setListener(ActionAdapter.Listener)}, use it.
156      * <br/>
157      * If not, use the activity's default listener.
158      * <br/>
159      * Don't broadcast the click if the action is disabled or only displays info.
160      */
161     @Override
onActionClicked(Action action)162     public void onActionClicked(Action action) {
163         // eat events if action is disabled or only displays info
164         if (!action.isEnabled() || action.infoOnly()) {
165             return;
166         }
167 
168         if (mListener != null) {
169             mListener.onActionClicked(action);
170         } else if (mFragment.getActivity() instanceof ActionAdapter.Listener) {
171             ActionAdapter.Listener listener = (ActionAdapter.Listener) mFragment.getActivity();
172             listener.onActionClicked(action);
173         }
174     }
175 
176     @Override
onActionFocused(Action action)177     public void onActionFocused(Action action) {
178         if (mFragment.getActivity() instanceof ActionAdapter.OnFocusListener) {
179             ActionAdapter.OnFocusListener listener = (ActionAdapter.OnFocusListener) mFragment
180                     .getActivity();
181             listener.onActionFocused(action);
182         }
183     }
184 
185     @Override
onActionSelect(Action action)186     public void onActionSelect(Action action) {
187         if (mFragment.getActivity() instanceof ActionAdapter.OnKeyListener) {
188             ActionAdapter.OnKeyListener listener = (ActionAdapter.OnKeyListener) mFragment
189                     .getActivity();
190             listener.onActionSelect(action);
191         }
192     }
193 
194     @Override
onActionUnselect(Action action)195     public void onActionUnselect(Action action) {
196         if (mFragment.getActivity() instanceof ActionAdapter.OnKeyListener) {
197             ActionAdapter.OnKeyListener listener = (ActionAdapter.OnKeyListener) mFragment
198                     .getActivity();
199             listener.onActionUnselect(action);
200         }
201     }
202 
getName()203     public String getName() {
204         return mName;
205     }
206 
207     /**
208      * Fragments need to call this method in its {@link #onResume()} to set the
209      * custom listener. <br/>
210      * Activities do not need to call this method
211      *
212      * @param listener
213      */
setListener(ActionAdapter.Listener listener)214     public void setListener(ActionAdapter.Listener listener) {
215         mListener = listener;
216     }
217 
hasListener()218     public boolean hasListener() {
219         return mListener != null;
220     }
221 
222     /**
223      * Sets whether to not to select the first checked action on resume.
224      */
setSelectFirstChecked(boolean selectFirstChecked)225     public void setSelectFirstChecked(boolean selectFirstChecked) {
226         mSelectFirstChecked = selectFirstChecked;
227     }
228 
loadActionsFromArgumentsIfNecessary()229     private void loadActionsFromArgumentsIfNecessary() {
230         if (mFragment.getArguments() != null && !mAddedSavedActions) {
231             ArrayList<Action> actions = mFragment.getArguments()
232                     .getParcelableArrayList(EXTRA_ACTIONS);
233             if (actions != null) {
234                 final int size = actions.size();
235                 for (int index = 0; index < size; ++index) {
236                     if (mSelectFirstChecked && actions.get(index).isChecked()
237                             && mIndexToSelect == -1) {
238                         mIndexToSelect = index;
239                     }
240                     mAdapter.addAction(actions.get(index));
241                 }
242             }
243         }
244     }
245 }
246