• 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.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 
SettingsPoolingHandler(final SetupWizardActivity ownerInstance, final InputMethodManager imm)83         public SettingsPoolingHandler(final SetupWizardActivity ownerInstance,
84                 final InputMethodManager imm) {
85             super(ownerInstance);
86             mImmInHandler = imm;
87         }
88 
89         @Override
handleMessage(final Message msg)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 
startPollingImeSettings()107         public void startPollingImeSettings() {
108             sendMessageDelayed(obtainMessage(MSG_POLLING_IME_SETTINGS),
109                     IME_SETTINGS_POLLING_INTERVAL);
110         }
111 
cancelPollingImeSettings()112         public void cancelPollingImeSettings() {
113             removeMessages(MSG_POLLING_IME_SETTINGS);
114         }
115     }
116 
117     @Override
onCreate(final Bundle savedInstanceState)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
onClick(final View v)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 
invokeSetupWizardOfThisIme()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 
invokeSettingsOfThisIme()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 
invokeLanguageAndInputSettings()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 
invokeInputMethodPicker()275     void invokeInputMethodPicker() {
276         // Invoke input method picker.
277         mImm.showInputMethodPicker();
278         mNeedsToAdjustStepNumberToSystemState = true;
279     }
280 
invokeSubtypeEnablerOfThisIme()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 
determineSetupStepNumberFromLauncher()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 
determineSetupStepNumber()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
onSaveInstanceState(final Bundle outState)317     protected void onSaveInstanceState(final Bundle outState) {
318         super.onSaveInstanceState(outState);
319         outState.putInt(STATE_STEP, mStepNumber);
320     }
321 
322     @Override
onRestoreInstanceState(final Bundle savedInstanceState)323     protected void onRestoreInstanceState(final Bundle savedInstanceState) {
324         super.onRestoreInstanceState(savedInstanceState);
325         mStepNumber = savedInstanceState.getInt(STATE_STEP);
326     }
327 
isInSetupSteps(final int stepNumber)328     private static boolean isInSetupSteps(final int stepNumber) {
329         return stepNumber >= STEP_1 && stepNumber <= STEP_3;
330     }
331 
332     @Override
onRestart()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
onResume()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
onBackPressed()361     public void onBackPressed() {
362         if (mStepNumber == STEP_1) {
363             mStepNumber = STEP_WELCOME;
364             updateSetupStepView();
365             return;
366         }
367         super.onBackPressed();
368     }
369 
hideWelcomeVideoAndShowWelcomeImage()370     void hideWelcomeVideoAndShowWelcomeImage() {
371         mWelcomeVideoView.setVisibility(View.GONE);
372         mWelcomeImageView.setImageResource(R.raw.setup_welcome_image);
373         mWelcomeImageView.setVisibility(View.VISIBLE);
374     }
375 
showAndStartWelcomeVideo()376     private void showAndStartWelcomeVideo() {
377         mWelcomeVideoView.setVisibility(View.VISIBLE);
378         mWelcomeVideoView.setVideoURI(mWelcomeVideoUri);
379         mWelcomeVideoView.start();
380     }
381 
hideAndStopWelcomeVideo()382     private void hideAndStopWelcomeVideo() {
383         mWelcomeVideoView.stopPlayback();
384         mWelcomeVideoView.setVisibility(View.GONE);
385     }
386 
387     @Override
onPause()388     protected void onPause() {
389         hideAndStopWelcomeVideo();
390         super.onPause();
391     }
392 
393     @Override
onWindowFocusChanged(final boolean hasFocus)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 
updateSetupStepView()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