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