SetupWizardActivity.java revision 25f0c8089eecfcded7f41c66453bd874ba92219a
1/*
2 * Copyright (C) 2013 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.inputmethod.latin.setup;
18
19import android.app.Activity;
20import android.content.ContentResolver;
21import android.content.Intent;
22import android.content.res.Resources;
23import android.media.MediaPlayer;
24import android.net.Uri;
25import android.os.Bundle;
26import android.os.Message;
27import android.provider.Settings;
28import android.util.Log;
29import android.view.View;
30import android.view.inputmethod.InputMethodInfo;
31import android.widget.ImageView;
32import android.widget.TextView;
33import android.widget.VideoView;
34
35import com.android.inputmethod.compat.TextViewCompatUtils;
36import com.android.inputmethod.compat.ViewCompatUtils;
37import com.android.inputmethod.latin.CollectionUtils;
38import com.android.inputmethod.latin.R;
39import com.android.inputmethod.latin.RichInputMethodManager;
40import com.android.inputmethod.latin.SettingsActivity;
41import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
42
43import java.util.ArrayList;
44
45// TODO: Use Fragment to implement welcome screen and setup steps.
46public final class SetupWizardActivity extends Activity implements View.OnClickListener {
47    static final String TAG = SetupWizardActivity.class.getSimpleName();
48
49    private static final boolean ENABLE_WELCOME_VIDEO = true;
50
51    private View mSetupWizard;
52    private View mWelcomeScreen;
53    private View mSetupScreen;
54    private Uri mWelcomeVideoUri;
55    private VideoView mWelcomeVideoView;
56    private ImageView mWelcomeImageView;
57    private View mActionStart;
58    private View mActionNext;
59    private TextView mStep1Bullet;
60    private TextView mActionFinish;
61    private SetupStepGroup mSetupStepGroup;
62    private static final String STATE_STEP = "step";
63    private int mStepNumber;
64    private boolean mNeedsToAdjustStepNumberToSystemState;
65    private static final int STEP_WELCOME = 0;
66    private static final int STEP_1 = 1;
67    private static final int STEP_2 = 2;
68    private static final int STEP_3 = 3;
69    private static final int STEP_LAUNCHING_IME_SETTINGS = 4;
70    private static final int STEP_BACK_FROM_IME_SETTINGS = 5;
71
72    final SettingsPoolingHandler mHandler = new SettingsPoolingHandler(this);
73
74    static final class SettingsPoolingHandler
75            extends StaticInnerHandlerWrapper<SetupWizardActivity> {
76        private static final int MSG_POLLING_IME_SETTINGS = 0;
77        private static final long IME_SETTINGS_POLLING_INTERVAL = 200;
78
79        public SettingsPoolingHandler(final SetupWizardActivity outerInstance) {
80            super(outerInstance);
81        }
82
83        @Override
84        public void handleMessage(final Message msg) {
85            final SetupWizardActivity setupWizardActivity = getOuterInstance();
86            if (setupWizardActivity == null) {
87                return;
88            }
89            switch (msg.what) {
90            case MSG_POLLING_IME_SETTINGS:
91                if (SetupActivity.isThisImeEnabled(setupWizardActivity)) {
92                    setupWizardActivity.invokeSetupWizardOfThisIme();
93                    return;
94                }
95                startPollingImeSettings();
96                break;
97            }
98        }
99
100        public void startPollingImeSettings() {
101            sendMessageDelayed(obtainMessage(MSG_POLLING_IME_SETTINGS),
102                    IME_SETTINGS_POLLING_INTERVAL);
103        }
104
105        public void cancelPollingImeSettings() {
106            removeMessages(MSG_POLLING_IME_SETTINGS);
107        }
108    }
109
110    @Override
111    protected void onCreate(final Bundle savedInstanceState) {
112        setTheme(android.R.style.Theme_Translucent_NoTitleBar);
113        super.onCreate(savedInstanceState);
114
115        setContentView(R.layout.setup_wizard);
116        mSetupWizard = findViewById(R.id.setup_wizard);
117
118        RichInputMethodManager.init(this);
119
120        if (savedInstanceState == null) {
121            mStepNumber = determineSetupStepNumberFromLauncher();
122        } else {
123            mStepNumber = savedInstanceState.getInt(STATE_STEP);
124        }
125
126        final String applicationName = getResources().getString(getApplicationInfo().labelRes);
127        mWelcomeScreen = findViewById(R.id.setup_welcome_screen);
128        final TextView welcomeTitle = (TextView)findViewById(R.id.setup_welcome_title);
129        welcomeTitle.setText(getString(R.string.setup_welcome_title, applicationName));
130
131        mSetupScreen = findViewById(R.id.setup_steps_screen);
132        final TextView stepsTitle = (TextView)findViewById(R.id.setup_title);
133        stepsTitle.setText(getString(R.string.setup_steps_title, applicationName));
134
135        final SetupStepIndicatorView indicatorView =
136                (SetupStepIndicatorView)findViewById(R.id.setup_step_indicator);
137        mSetupStepGroup = new SetupStepGroup(indicatorView);
138
139        mStep1Bullet = (TextView)findViewById(R.id.setup_step1_bullet);
140        mStep1Bullet.setOnClickListener(this);
141        final SetupStep step1 = new SetupStep(STEP_1, applicationName,
142                mStep1Bullet, findViewById(R.id.setup_step1),
143                R.string.setup_step1_title, R.string.setup_step1_instruction,
144                R.string.setup_step1_finished_instruction, R.drawable.ic_setup_step1,
145                R.string.setup_step1_action);
146        step1.setAction(new Runnable() {
147            @Override
148            public void run() {
149                invokeLanguageAndInputSettings();
150                mHandler.startPollingImeSettings();
151            }
152        });
153        mSetupStepGroup.addStep(step1);
154
155        final SetupStep step2 = new SetupStep(STEP_2, applicationName,
156                (TextView)findViewById(R.id.setup_step2_bullet), findViewById(R.id.setup_step2),
157                R.string.setup_step2_title, R.string.setup_step2_instruction,
158                0 /* finishedInstruction */, R.drawable.ic_setup_step2,
159                R.string.setup_step2_action);
160        step2.setAction(new Runnable() {
161            @Override
162            public void run() {
163                invokeInputMethodPicker();
164            }
165        });
166        mSetupStepGroup.addStep(step2);
167
168        final SetupStep step3 = new SetupStep(STEP_3, applicationName,
169                (TextView)findViewById(R.id.setup_step3_bullet), findViewById(R.id.setup_step3),
170                R.string.setup_step3_title, R.string.setup_step3_instruction,
171                0 /* finishedInstruction */, R.drawable.ic_setup_step3,
172                R.string.setup_step3_action);
173        step3.setAction(new Runnable() {
174            @Override
175            public void run() {
176                invokeSubtypeEnablerOfThisIme();
177            }
178        });
179        mSetupStepGroup.addStep(step3);
180
181        mWelcomeVideoUri = new Uri.Builder()
182                .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
183                .authority(getPackageName())
184                .path(Integer.toString(R.raw.setup_welcome_video))
185                .build();
186        final VideoView welcomeVideoView = (VideoView)findViewById(R.id.setup_welcome_video);
187        welcomeVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
188            @Override
189            public void onPrepared(final MediaPlayer mp) {
190                // Now VideoView has been laid-out and ready to play, remove background of it to
191                // reveal the video.
192                welcomeVideoView.setBackgroundResource(0);
193                mp.setLooping(true);
194            }
195        });
196        welcomeVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
197            @Override
198            public boolean onError(final MediaPlayer mp, final int what, final int extra) {
199                Log.e(TAG, "Playing welcome video causes error: what=" + what + " extra=" + extra);
200                hideWelcomeVideoAndShowWelcomeImage();
201                return true;
202            }
203        });
204        mWelcomeVideoView = welcomeVideoView;
205        mWelcomeImageView = (ImageView)findViewById(R.id.setup_welcome_image);
206
207        mActionStart = findViewById(R.id.setup_start_label);
208        mActionStart.setOnClickListener(this);
209        mActionNext = findViewById(R.id.setup_next);
210        mActionNext.setOnClickListener(this);
211        mActionFinish = (TextView)findViewById(R.id.setup_finish);
212        TextViewCompatUtils.setCompoundDrawablesRelativeWithIntrinsicBounds(mActionFinish,
213                getResources().getDrawable(R.drawable.ic_setup_finish), null, null, null);
214        mActionFinish.setOnClickListener(this);
215    }
216
217    @Override
218    public void onClick(final View v) {
219        if (v == mActionFinish) {
220            finish();
221            return;
222        }
223        final int currentStep = determineSetupStepNumber();
224        final int nextStep;
225        if (v == mActionStart) {
226            nextStep = STEP_1;
227        } else if (v == mActionNext) {
228            nextStep = mStepNumber + 1;
229        } else if (v == mStep1Bullet && currentStep == STEP_2) {
230            nextStep = STEP_1;
231        } else {
232            nextStep = mStepNumber;
233        }
234        if (mStepNumber != nextStep) {
235            mStepNumber = nextStep;
236            updateSetupStepView();
237        }
238    }
239
240    void invokeSetupWizardOfThisIme() {
241        final Intent intent = new Intent();
242        intent.setClass(this, SetupWizardActivity.class);
243        intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
244                | Intent.FLAG_ACTIVITY_SINGLE_TOP
245                | Intent.FLAG_ACTIVITY_CLEAR_TOP);
246        startActivity(intent);
247        mNeedsToAdjustStepNumberToSystemState = true;
248    }
249
250    private void invokeSettingsOfThisIme() {
251        final Intent intent = new Intent();
252        intent.setClass(this, SettingsActivity.class);
253        intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
254                | Intent.FLAG_ACTIVITY_CLEAR_TOP);
255        startActivity(intent);
256    }
257
258    void invokeLanguageAndInputSettings() {
259        final Intent intent = new Intent();
260        intent.setAction(Settings.ACTION_INPUT_METHOD_SETTINGS);
261        intent.addCategory(Intent.CATEGORY_DEFAULT);
262        startActivity(intent);
263        mNeedsToAdjustStepNumberToSystemState = true;
264    }
265
266    void invokeInputMethodPicker() {
267        // Invoke input method picker.
268        RichInputMethodManager.getInstance().getInputMethodManager()
269                .showInputMethodPicker();
270        mNeedsToAdjustStepNumberToSystemState = true;
271    }
272
273    void invokeSubtypeEnablerOfThisIme() {
274        final InputMethodInfo imi =
275                RichInputMethodManager.getInstance().getInputMethodInfoOfThisIme();
276        final Intent intent = new Intent();
277        intent.setAction(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS);
278        intent.addCategory(Intent.CATEGORY_DEFAULT);
279        intent.putExtra(Settings.EXTRA_INPUT_METHOD_ID, imi.getId());
280        startActivity(intent);
281    }
282
283    private int determineSetupStepNumberFromLauncher() {
284        final int stepNumber = determineSetupStepNumber();
285        if (stepNumber == STEP_1) {
286            return STEP_WELCOME;
287        }
288        if (stepNumber == STEP_3) {
289            return STEP_LAUNCHING_IME_SETTINGS;
290        }
291        return stepNumber;
292    }
293
294    private int determineSetupStepNumber() {
295        mHandler.cancelPollingImeSettings();
296        if (!SetupActivity.isThisImeEnabled(this)) {
297            return STEP_1;
298        }
299        if (!SetupActivity.isThisImeCurrent(this)) {
300            return STEP_2;
301        }
302        return STEP_3;
303    }
304
305    @Override
306    protected void onSaveInstanceState(final Bundle outState) {
307        super.onSaveInstanceState(outState);
308        outState.putInt(STATE_STEP, mStepNumber);
309    }
310
311    @Override
312    protected void onRestoreInstanceState(final Bundle savedInstanceState) {
313        super.onRestoreInstanceState(savedInstanceState);
314        mStepNumber = savedInstanceState.getInt(STATE_STEP);
315    }
316
317    private static boolean isInSetupSteps(final int stepNumber) {
318        return stepNumber >= STEP_1 && stepNumber <= STEP_3;
319    }
320
321    @Override
322    protected void onRestart() {
323        super.onRestart();
324        // Probably the setup wizard has been invoked from "Recent" menu. The setup step number
325        // needs to be adjusted to system state, because the state (IME is enabled and/or current)
326        // may have been changed.
327        if (isInSetupSteps(mStepNumber)) {
328            mStepNumber = determineSetupStepNumber();
329        }
330    }
331
332    @Override
333    protected void onResume() {
334        super.onResume();
335        if (mStepNumber == STEP_LAUNCHING_IME_SETTINGS) {
336            // Prevent white screen flashing while launching settings activity.
337            mSetupWizard.setVisibility(View.INVISIBLE);
338            invokeSettingsOfThisIme();
339            mStepNumber = STEP_BACK_FROM_IME_SETTINGS;
340            return;
341        }
342        if (mStepNumber == STEP_BACK_FROM_IME_SETTINGS) {
343            finish();
344            return;
345        }
346        updateSetupStepView();
347    }
348
349    @Override
350    public void onBackPressed() {
351        if (mStepNumber == STEP_1) {
352            mStepNumber = STEP_WELCOME;
353            updateSetupStepView();
354            return;
355        }
356        super.onBackPressed();
357    }
358
359    void hideWelcomeVideoAndShowWelcomeImage() {
360        mWelcomeVideoView.setVisibility(View.GONE);
361        mWelcomeImageView.setImageResource(R.raw.setup_welcome_image);
362        mWelcomeImageView.setVisibility(View.VISIBLE);
363    }
364
365    private void showAndStartWelcomeVideo() {
366        mWelcomeVideoView.setVisibility(View.VISIBLE);
367        mWelcomeVideoView.setVideoURI(mWelcomeVideoUri);
368        mWelcomeVideoView.start();
369    }
370
371    private void hideAndStopWelcomeVideo() {
372        mWelcomeVideoView.stopPlayback();
373        mWelcomeVideoView.setVisibility(View.GONE);
374    }
375
376    @Override
377    protected void onPause() {
378        hideAndStopWelcomeVideo();
379        super.onPause();
380    }
381
382    @Override
383    public void onWindowFocusChanged(final boolean hasFocus) {
384        super.onWindowFocusChanged(hasFocus);
385        if (hasFocus && mNeedsToAdjustStepNumberToSystemState) {
386            mNeedsToAdjustStepNumberToSystemState = false;
387            mStepNumber = determineSetupStepNumber();
388            updateSetupStepView();
389        }
390    }
391
392    private void updateSetupStepView() {
393        mSetupWizard.setVisibility(View.VISIBLE);
394        final boolean welcomeScreen = (mStepNumber == STEP_WELCOME);
395        mWelcomeScreen.setVisibility(welcomeScreen ? View.VISIBLE : View.GONE);
396        mSetupScreen.setVisibility(welcomeScreen ? View.GONE : View.VISIBLE);
397        if (welcomeScreen) {
398            if (ENABLE_WELCOME_VIDEO) {
399                showAndStartWelcomeVideo();
400            } else {
401                hideWelcomeVideoAndShowWelcomeImage();
402            }
403            return;
404        }
405        hideAndStopWelcomeVideo();
406        final boolean isStepActionAlreadyDone = mStepNumber < determineSetupStepNumber();
407        mSetupStepGroup.enableStep(mStepNumber, isStepActionAlreadyDone);
408        mActionNext.setVisibility(isStepActionAlreadyDone ? View.VISIBLE : View.GONE);
409        mActionFinish.setVisibility((mStepNumber == STEP_3) ? View.VISIBLE : View.GONE);
410    }
411
412    static final class SetupStep implements View.OnClickListener {
413        public final int mStepNo;
414        private final View mStepView;
415        private final TextView mBulletView;
416        private final int mActivatedColor;
417        private final int mDeactivatedColor;
418        private final String mInstruction;
419        private final String mFinishedInstruction;
420        private final TextView mActionLabel;
421        private Runnable mAction;
422
423        public SetupStep(final int stepNo, final String applicationName, final TextView bulletView,
424                final View stepView, final int title, final int instruction,
425                final int finishedInstruction, final int actionIcon, final int actionLabel) {
426            mStepNo = stepNo;
427            mStepView = stepView;
428            mBulletView = bulletView;
429            final Resources res = stepView.getResources();
430            mActivatedColor = res.getColor(R.color.setup_text_action);
431            mDeactivatedColor = res.getColor(R.color.setup_text_dark);
432
433            final TextView titleView = (TextView)mStepView.findViewById(R.id.setup_step_title);
434            titleView.setText(res.getString(title, applicationName));
435            mInstruction = (instruction == 0) ? null
436                    : res.getString(instruction, applicationName);
437            mFinishedInstruction = (finishedInstruction == 0) ? null
438                    : res.getString(finishedInstruction, applicationName);
439
440            mActionLabel = (TextView)mStepView.findViewById(R.id.setup_step_action_label);
441            mActionLabel.setText(res.getString(actionLabel));
442            if (actionIcon == 0) {
443                final int paddingEnd = ViewCompatUtils.getPaddingEnd(mActionLabel);
444                ViewCompatUtils.setPaddingRelative(mActionLabel, paddingEnd, 0, paddingEnd, 0);
445            } else {
446                TextViewCompatUtils.setCompoundDrawablesRelativeWithIntrinsicBounds(
447                        mActionLabel, res.getDrawable(actionIcon), null, null, null);
448            }
449        }
450
451        public void setEnabled(final boolean enabled, final boolean isStepActionAlreadyDone) {
452            mStepView.setVisibility(enabled ? View.VISIBLE : View.GONE);
453            mBulletView.setTextColor(enabled ? mActivatedColor : mDeactivatedColor);
454            final TextView instructionView = (TextView)mStepView.findViewById(
455                    R.id.setup_step_instruction);
456            instructionView.setText(isStepActionAlreadyDone ? mFinishedInstruction : mInstruction);
457            mActionLabel.setVisibility(isStepActionAlreadyDone ? View.GONE : View.VISIBLE);
458        }
459
460        public void setAction(final Runnable action) {
461            mActionLabel.setOnClickListener(this);
462            mAction = action;
463        }
464
465        @Override
466        public void onClick(final View v) {
467            if (v == mActionLabel && mAction != null) {
468                mAction.run();
469                return;
470            }
471        }
472    }
473
474    static final class SetupStepGroup {
475        private final SetupStepIndicatorView mIndicatorView;
476        private final ArrayList<SetupStep> mGroup = CollectionUtils.newArrayList();
477
478        public SetupStepGroup(final SetupStepIndicatorView indicatorView) {
479            mIndicatorView = indicatorView;
480        }
481
482        public void addStep(final SetupStep step) {
483            mGroup.add(step);
484        }
485
486        public void enableStep(final int enableStepNo, final boolean isStepActionAlreadyDone) {
487            for (final SetupStep step : mGroup) {
488                step.setEnabled(step.mStepNo == enableStepNo, isStepActionAlreadyDone);
489            }
490            mIndicatorView.setIndicatorPosition(enableStepNo - STEP_1, mGroup.size());
491        }
492    }
493}
494