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