Home | History | Annotate | Download | only in setup
      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 
     17 package com.android.inputmethod.latin.setup;
     18 
     19 import android.app.Activity;
     20 import android.content.ContentResolver;
     21 import android.content.Intent;
     22 import android.content.res.Resources;
     23 import android.media.MediaPlayer;
     24 import android.net.Uri;
     25 import android.os.Bundle;
     26 import android.os.Message;
     27 import android.provider.Settings;
     28 import android.util.Log;
     29 import android.view.View;
     30 import android.view.inputmethod.InputMethodInfo;
     31 import android.view.inputmethod.InputMethodManager;
     32 import android.widget.ImageView;
     33 import android.widget.TextView;
     34 import android.widget.VideoView;
     35 
     36 import com.android.inputmethod.compat.TextViewCompatUtils;
     37 import com.android.inputmethod.compat.ViewCompatUtils;
     38 import com.android.inputmethod.latin.R;
     39 import com.android.inputmethod.latin.settings.SettingsActivity;
     40 import com.android.inputmethod.latin.utils.CollectionUtils;
     41 import com.android.inputmethod.latin.utils.StaticInnerHandlerWrapper;
     42 
     43 import java.util.ArrayList;
     44 
     45 // TODO: Use Fragment to implement welcome screen and setup steps.
     46 public 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