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.widget.ImageView;
     32 import android.widget.TextView;
     33 import android.widget.VideoView;
     34 
     35 import com.android.inputmethod.compat.TextViewCompatUtils;
     36 import com.android.inputmethod.compat.ViewCompatUtils;
     37 import com.android.inputmethod.latin.CollectionUtils;
     38 import com.android.inputmethod.latin.R;
     39 import com.android.inputmethod.latin.RichInputMethodManager;
     40 import com.android.inputmethod.latin.SettingsActivity;
     41 import com.android.inputmethod.latin.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 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