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
17package com.android.tv.settings.connectivity;
18
19import android.app.Fragment;
20import android.app.FragmentManager;
21import android.app.FragmentTransaction;
22import android.content.Context;
23import android.os.Bundle;
24import android.text.TextUtils;
25import android.util.Log;
26
27import com.android.tv.settings.connectivity.setup.MessageWizardFragment;
28import com.android.tv.settings.connectivity.setup.PasswordInputWizardFragment;
29import com.android.tv.settings.connectivity.setup.SelectFromListWizardFragment;
30import com.android.tv.settings.connectivity.setup.SelectFromListWizardFragment.ListItem;
31import com.android.tv.settings.connectivity.setup.TextInputWizardFragment;
32import com.android.tv.settings.form.FormPage;
33import com.android.tv.settings.form.FormPageResultListener;
34
35import java.util.ArrayList;
36
37/**
38 * Displays form pages.
39 */
40public class FormPageDisplayer
41        implements TextInputWizardFragment.Listener, SelectFromListWizardFragment.Listener,
42            PasswordInputWizardFragment.Listener {
43
44    public static final int DISPLAY_TYPE_TEXT_INPUT = 1;
45    public static final int DISPLAY_TYPE_LIST_CHOICE = 2;
46    public static final int DISPLAY_TYPE_LOADING = 3;
47    // Minimum 8 characters
48    public static final int DISPLAY_TYPE_PSK_INPUT = 4;
49
50    private static final int PSK_MIN_LENGTH = 8;
51
52    public interface FormPageInfo {
53        /**
54         * @return the resource id of the title for this page.
55         */
56        int getTitleResourceId();
57
58        /**
59         * @return the resource id of the description for this page. 0 if no description.
60         */
61        int getDescriptionResourceId();
62
63        /**
64         * @return the input type for the editable text field for this page.
65         */
66        int getInputType();
67
68        /**
69         * @return the way this form page should be displayed.
70         */
71        int getDisplayType();
72
73        /**
74         * @return the default value for this input
75         */
76        int getDefaultPrefillResourceId();
77
78        /**
79         * @return the set of choices for this form page.
80         */
81        ArrayList<SelectFromListWizardFragment.ListItem> getChoices(Context context,
82                ArrayList<SelectFromListWizardFragment.ListItem> extraChoices);
83    }
84
85    public interface UserActivityListener {
86        void onUserActivity();
87    }
88
89    private static final String TAG = "FormPageDisplayer";
90    private static final boolean DEBUG = false;
91    private static final String RESULT_LIST_ITEM = "result_list_item";
92
93    private final Context mContext;
94    private final FragmentManager mFragmentManager;
95    private TextInputWizardFragment.Listener mTextInputWizardFragmentListener;
96    private PasswordInputWizardFragment.Listener mPasswordInputWizardFragmentListener;
97    private SelectFromListWizardFragment.Listener mSelectFromListWizardFragmentListener;
98    private final int mContentViewId;
99
100    public FormPageDisplayer(Context context, FragmentManager fragmentManager, int contentViewId) {
101        mContext = context;
102        mFragmentManager = fragmentManager;
103        mContentViewId = contentViewId;
104    }
105
106    @Override
107    public boolean onTextInputComplete(String text) {
108        if (mTextInputWizardFragmentListener != null) {
109            return mTextInputWizardFragmentListener.onTextInputComplete(text);
110        }
111        return true;
112    }
113
114    @Override
115    public boolean onPasswordInputComplete(String text, boolean obfuscate) {
116        if (mPasswordInputWizardFragmentListener != null) {
117            return mPasswordInputWizardFragmentListener.onPasswordInputComplete(text, obfuscate);
118        }
119        return true;
120    }
121
122    @Override
123    public void onListSelectionComplete(ListItem listItem) {
124        if (mSelectFromListWizardFragmentListener != null) {
125            mSelectFromListWizardFragmentListener.onListSelectionComplete(listItem);
126        }
127    }
128
129    @Override
130    public void onListFocusChanged(ListItem listItem) {
131        if (mSelectFromListWizardFragmentListener != null) {
132            mSelectFromListWizardFragmentListener.onListFocusChanged(listItem);
133        }
134    }
135
136    public Fragment displayPage(FormPageInfo formPageInfo, String titleArgument,
137            String descriptionArgument,
138            ArrayList<SelectFromListWizardFragment.ListItem> extraChoices,
139            FormPage previousFormPage, UserActivityListener userActivityListener,
140            boolean showProgress, FormPage currentFormPage,
141            FormPageResultListener formPageResultListener, boolean forward, boolean emptyAllowed) {
142        if (DEBUG) {
143            Log.d(TAG, "Displaying: " + currentFormPage.getTitle());
144        }
145        switch (formPageInfo.getDisplayType()) {
146            case DISPLAY_TYPE_LIST_CHOICE:
147                return displayList(formPageInfo, titleArgument, descriptionArgument, extraChoices,
148                        currentFormPage, formPageResultListener, previousFormPage,
149                        userActivityListener, forward);
150
151            case DISPLAY_TYPE_TEXT_INPUT:
152                return displayTextInput(formPageInfo, titleArgument, descriptionArgument,
153                        currentFormPage, formPageResultListener, previousFormPage, forward,
154                        emptyAllowed);
155
156            case DISPLAY_TYPE_PSK_INPUT:
157                return displayPskInput(formPageInfo, titleArgument, descriptionArgument,
158                        currentFormPage, formPageResultListener, previousFormPage, forward);
159
160            case DISPLAY_TYPE_LOADING:
161                return displayLoading(formPageInfo, titleArgument, showProgress, forward);
162
163            default:
164                return null;
165        }
166    }
167
168    public ListItem getListItem(FormPage formPage) {
169        return formPage.getData().getParcelable(RESULT_LIST_ITEM);
170    }
171
172    public void displayFragment(Fragment fragment, boolean forward) {
173        FragmentTransaction transaction = mFragmentManager.beginTransaction();
174        if (forward) {
175            transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
176        } else {
177            transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE);
178        }
179        transaction.replace(mContentViewId, fragment).commit();
180    }
181
182    private Fragment displayList(final FormPageInfo formPageInfo, String titleArgument,
183            String descriptionArgument,
184            ArrayList<SelectFromListWizardFragment.ListItem> extraChoices, final FormPage formPage,
185            final FormPageResultListener formPageResultListener, FormPage lastPage,
186            final UserActivityListener userActivityListener, boolean forward) {
187        Fragment fragment = SelectFromListWizardFragment.newInstance(
188                getTitle(formPageInfo, titleArgument),
189                getDescription(formPageInfo, descriptionArgument),
190                formPageInfo.getChoices(mContext, extraChoices), lastPage == null ? null
191                        : (ListItem) lastPage.getData().getParcelable(RESULT_LIST_ITEM));
192        displayFragment(fragment, forward);
193        mSelectFromListWizardFragmentListener = new SelectFromListWizardFragment.Listener() {
194            @Override
195            public void onListSelectionComplete(ListItem selection) {
196                Bundle result = new Bundle();
197                result.putParcelable(RESULT_LIST_ITEM, selection);
198                result.putString(FormPage.DATA_KEY_SUMMARY_STRING, selection.getName());
199                formPageResultListener.onBundlePageResult(formPage, result);
200            }
201
202            @Override
203            public void onListFocusChanged(ListItem listItem) {
204                if (userActivityListener != null) {
205                    userActivityListener.onUserActivity();
206                }
207            }
208        };
209        return fragment;
210    }
211
212    private Fragment displayTextInput(FormPageInfo formPageInfo, String titleArgument,
213            String descriptionArgument, final FormPage formPage,
214            final FormPageResultListener listener, FormPage lastPage, boolean forward,
215            final boolean emptyAllowed) {
216        final String prefill = lastPage != null && !TextUtils.isEmpty(lastPage.getDataSummary())
217                ? lastPage.getDataSummary() : getDefaultPrefill(formPageInfo);
218        Fragment fragment = TextInputWizardFragment.newInstance(
219                getTitle(formPageInfo, titleArgument),
220                getDescription(formPageInfo, descriptionArgument),
221                formPageInfo.getInputType(),
222                prefill);
223        displayFragment(fragment, forward);
224
225        mTextInputWizardFragmentListener = new TextInputWizardFragment.Listener() {
226            @Override
227            public boolean onTextInputComplete(String text) {
228                if (!TextUtils.isEmpty(text) || emptyAllowed) {
229                    Bundle result = new Bundle();
230                    result.putString(FormPage.DATA_KEY_SUMMARY_STRING, text);
231                    listener.onBundlePageResult(formPage, result);
232                    return true;
233                }
234                return false;
235            }
236        };
237        return fragment;
238    }
239
240    private Fragment displayPskInput(FormPageInfo formPageInfo, String titleArgument,
241            String descriptionArgument, final FormPage formPage,
242            final FormPageResultListener listener, FormPage lastPage, boolean forward) {
243        boolean obfuscate = lastPage != null
244                ? TextUtils.equals(
245                          lastPage.getDataSecondary(), PasswordInputWizardFragment.OPTION_OBFUSCATE)
246                : false;
247        Fragment fragment =
248                PasswordInputWizardFragment.newInstance(getTitle(formPageInfo, titleArgument),
249                        getDescription(formPageInfo, descriptionArgument),
250                        lastPage == null ? null : lastPage.getDataSummary(), obfuscate);
251        displayFragment(fragment, forward);
252
253        mPasswordInputWizardFragmentListener = new PasswordInputWizardFragment.Listener() {
254            @Override
255            public boolean onPasswordInputComplete(String text, boolean obfuscate) {
256                if (!TextUtils.isEmpty(text) && text.length() >= PSK_MIN_LENGTH) {
257                    Bundle result = new Bundle();
258                    result.putString(FormPage.DATA_KEY_SUMMARY_STRING, text);
259                    if (obfuscate) {
260                        result.putString(FormPage.DATA_KEY_SECONDARY_STRING,
261                                PasswordInputWizardFragment.OPTION_OBFUSCATE);
262                    }
263                    listener.onBundlePageResult(formPage, result);
264                    return true;
265                }
266                return false;
267            }
268        };
269        return fragment;
270    }
271
272    private Fragment displayLoading(FormPageInfo formPageInfo, String argument,
273            boolean showProgress, boolean forward) {
274        Fragment fragment = MessageWizardFragment.newInstance(
275                getTitle(formPageInfo, argument), showProgress);
276        displayFragment(fragment, forward);
277        return fragment;
278    }
279
280    private String getTitle(FormPageInfo formPageInfo, String argument) {
281        return mContext.getString(formPageInfo.getTitleResourceId(), argument);
282    }
283
284    private String getDescription(FormPageInfo formPageInfo, String argument) {
285        int descriptionResourceId = formPageInfo.getDescriptionResourceId();
286        return (descriptionResourceId != 0) ? mContext.getString(descriptionResourceId, argument)
287                : null;
288    }
289
290    private String getDefaultPrefill(FormPageInfo formPageInfo) {
291        int defaultPrefillResourceId = formPageInfo.getDefaultPrefillResourceId();
292        return (defaultPrefillResourceId != 0)
293                ? mContext.getString(defaultPrefillResourceId) : null;
294    }
295}
296