• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SettingsPoolingHandler(final SetupWizardActivity outerInstance)79         public SettingsPoolingHandler(final SetupWizardActivity outerInstance) {
80             super(outerInstance);
81         }
82 
83         @Override
handleMessage(final Message msg)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 
startPollingImeSettings()100         public void startPollingImeSettings() {
101             sendMessageDelayed(obtainMessage(MSG_POLLING_IME_SETTINGS),
102                     IME_SETTINGS_POLLING_INTERVAL);
103         }
104 
cancelPollingImeSettings()105         public void cancelPollingImeSettings() {
106             removeMessages(MSG_POLLING_IME_SETTINGS);
107         }
108     }
109 
110     @Override
onCreate(final Bundle savedInstanceState)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
onClick(final View v)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 
invokeSetupWizardOfThisIme()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 
invokeSettingsOfThisIme()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 
invokeLanguageAndInputSettings()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 
invokeInputMethodPicker()266     void invokeInputMethodPicker() {
267         // Invoke input method picker.
268         RichInputMethodManager.getInstance().getInputMethodManager()
269                 .showInputMethodPicker();
270         mNeedsToAdjustStepNumberToSystemState = true;
271     }
272 
invokeSubtypeEnablerOfThisIme()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 
determineSetupStepNumberFromLauncher()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 
determineSetupStepNumber()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
onSaveInstanceState(final Bundle outState)306     protected void onSaveInstanceState(final Bundle outState) {
307         super.onSaveInstanceState(outState);
308         outState.putInt(STATE_STEP, mStepNumber);
309     }
310 
311     @Override
onRestoreInstanceState(final Bundle savedInstanceState)312     protected void onRestoreInstanceState(final Bundle savedInstanceState) {
313         super.onRestoreInstanceState(savedInstanceState);
314         mStepNumber = savedInstanceState.getInt(STATE_STEP);
315     }
316 
isInSetupSteps(final int stepNumber)317     private static boolean isInSetupSteps(final int stepNumber) {
318         return stepNumber >= STEP_1 && stepNumber <= STEP_3;
319     }
320 
321     @Override
onRestart()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
onResume()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
onBackPressed()350     public void onBackPressed() {
351         if (mStepNumber == STEP_1) {
352             mStepNumber = STEP_WELCOME;
353             updateSetupStepView();
354             return;
355         }
356         super.onBackPressed();
357     }
358 
hideWelcomeVideoAndShowWelcomeImage()359     void hideWelcomeVideoAndShowWelcomeImage() {
360         mWelcomeVideoView.setVisibility(View.GONE);
361         mWelcomeImageView.setImageResource(R.raw.setup_welcome_image);
362         mWelcomeImageView.setVisibility(View.VISIBLE);
363     }
364 
showAndStartWelcomeVideo()365     private void showAndStartWelcomeVideo() {
366         mWelcomeVideoView.setVisibility(View.VISIBLE);
367         mWelcomeVideoView.setVideoURI(mWelcomeVideoUri);
368         mWelcomeVideoView.start();
369     }
370 
hideAndStopWelcomeVideo()371     private void hideAndStopWelcomeVideo() {
372         mWelcomeVideoView.stopPlayback();
373         mWelcomeVideoView.setVisibility(View.GONE);
374     }
375 
376     @Override
onPause()377     protected void onPause() {
378         hideAndStopWelcomeVideo();
379         super.onPause();
380     }
381 
382     @Override
onWindowFocusChanged(final boolean hasFocus)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 
updateSetupStepView()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