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