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 
146     @Override
onSaveInstanceState(Bundle outState)147     public void onSaveInstanceState(Bundle outState) {
148         super.onSaveInstanceState(outState);
149         if (hasCreatedView()) {
150             // Try to save instance state only if the view has already been created.
151             outState.putParcelableArrayList(EXTRA_ACTIONS, mAdapter.getActions());
152             outState.putInt(EXTRA_INDEX, getScrollAdapterView().getSelectedItemPosition());
153         }
154     }
155 
156     /**
157      * If the custom lister has been set using {@link #setListener(ActionAdapter.Listener)}, use it.
158      * <br/>
159      * If not, use the activity's default listener.
160      * <br/>
161      * Don't broadcast the click if the action is disabled or only displays info.
162      */
163     @Override
onActionClicked(Action action)164     public void onActionClicked(Action action) {
165         // eat events if action is disabled or only displays info
166         if (!action.isEnabled() || action.infoOnly()) {
167             return;
168         }
169 
170         if (mListener != null) {
171             mListener.onActionClicked(action);
172         } else if (mFragment.getActivity() instanceof ActionAdapter.Listener) {
173             ActionAdapter.Listener listener = (ActionAdapter.Listener) mFragment.getActivity();
174             listener.onActionClicked(action);
175         }
176     }
177 
178     @Override
onActionFocused(Action action)179     public void onActionFocused(Action action) {
180         if (mFragment.getActivity() instanceof ActionAdapter.OnFocusListener) {
181             ActionAdapter.OnFocusListener listener = (ActionAdapter.OnFocusListener) mFragment
182                     .getActivity();
183             listener.onActionFocused(action);
184         }
185     }
186 
187     @Override
onActionSelect(Action action)188     public void onActionSelect(Action action) {
189         if (mFragment.getActivity() instanceof ActionAdapter.OnKeyListener) {
190             ActionAdapter.OnKeyListener listener = (ActionAdapter.OnKeyListener) mFragment
191                     .getActivity();
192             listener.onActionSelect(action);
193         }
194     }
195 
196     @Override
onActionUnselect(Action action)197     public void onActionUnselect(Action action) {
198         if (mFragment.getActivity() instanceof ActionAdapter.OnKeyListener) {
199             ActionAdapter.OnKeyListener listener = (ActionAdapter.OnKeyListener) mFragment
200                     .getActivity();
201             listener.onActionUnselect(action);
202         }
203     }
204 
getName()205     public String getName() {
206         return mName;
207     }
208 
209     /**
210      * Fragments need to call this method in its {@link #onResume()} to set the
211      * custom listener. <br/>
212      * Activities do not need to call this method
213      *
214      * @param listener
215      */
setListener(ActionAdapter.Listener listener)216     public void setListener(ActionAdapter.Listener listener) {
217         mListener = listener;
218     }
219 
hasListener()220     public boolean hasListener() {
221         return mListener != null;
222     }
223 
224     /**
225      * Sets whether to not to select the first checked action on resume.
226      */
setSelectFirstChecked(boolean selectFirstChecked)227     public void setSelectFirstChecked(boolean selectFirstChecked) {
228         mSelectFirstChecked = selectFirstChecked;
229     }
230 
loadActionsFromArgumentsIfNecessary()231     private void loadActionsFromArgumentsIfNecessary() {
232         if (mFragment.getArguments() != null && !mAddedSavedActions) {
233             ArrayList<Action> actions = mFragment.getArguments()
234                     .getParcelableArrayList(EXTRA_ACTIONS);
235             if (actions != null) {
236                 final int size = actions.size();
237                 for (int index = 0; index < size; ++index) {
238                     if (mSelectFirstChecked && actions.get(index).isChecked()
239                             && mIndexToSelect == -1) {
240                         mIndexToSelect = index;
241                     }
242                     mAdapter.addAction(actions.get(index));
243                 }
244             }
245         }
246     }
247 }
248