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.LeakGuardHandlerWrapper;
     41 import com.android.inputmethod.latin.utils.UncachedInputMethodManagerUtils;
     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 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