SetupActivity.java revision 242c066dde910097d286082fd255d04e5aa684cb
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 17package com.android.inputmethod.latin.setup; 18 19import android.app.Activity; 20import android.content.Context; 21import android.content.Intent; 22import android.content.res.Resources; 23import android.graphics.PorterDuff; 24import android.graphics.drawable.Drawable; 25import android.os.Bundle; 26import android.os.Message; 27import android.provider.Settings; 28import android.view.View; 29import android.view.inputmethod.InputMethodInfo; 30import android.view.inputmethod.InputMethodManager; 31import android.widget.TextView; 32 33import com.android.inputmethod.compat.TextViewCompatUtils; 34import com.android.inputmethod.compat.ViewCompatUtils; 35import com.android.inputmethod.latin.CollectionUtils; 36import com.android.inputmethod.latin.R; 37import com.android.inputmethod.latin.RichInputMethodManager; 38import com.android.inputmethod.latin.SettingsActivity; 39import com.android.inputmethod.latin.StaticInnerHandlerWrapper; 40 41import java.util.HashMap; 42 43public final class SetupActivity extends Activity { 44 private SetupStepIndicatorView mStepIndicatorView; 45 private final SetupStepGroup mSetupSteps = new SetupStepGroup(); 46 private static final String STATE_STEP = "step"; 47 private int mStepNumber; 48 private static final int STEP_1 = 1; 49 private static final int STEP_2 = 2; 50 private static final int STEP_3 = 3; 51 52 private final SettingsPoolingHandler mHandler = new SettingsPoolingHandler(this); 53 54 static final class SettingsPoolingHandler extends StaticInnerHandlerWrapper<SetupActivity> { 55 private static final int MSG_POLLING_IME_SETTINGS = 0; 56 private static final long IME_SETTINGS_POLLING_INTERVAL = 200; 57 58 public SettingsPoolingHandler(final SetupActivity outerInstance) { 59 super(outerInstance); 60 } 61 62 @Override 63 public void handleMessage(final Message msg) { 64 final SetupActivity setupActivity = getOuterInstance(); 65 if (setupActivity == null) { 66 return; 67 } 68 switch (msg.what) { 69 case MSG_POLLING_IME_SETTINGS: 70 if (SetupActivity.isThisImeEnabled(setupActivity)) { 71 setupActivity.invokeSetupWizardOfThisIme(); 72 return; 73 } 74 startPollingImeSettings(); 75 break; 76 } 77 } 78 79 public void startPollingImeSettings() { 80 sendMessageDelayed(obtainMessage(MSG_POLLING_IME_SETTINGS), 81 IME_SETTINGS_POLLING_INTERVAL); 82 } 83 84 public void cancelPollingImeSettings() { 85 removeMessages(MSG_POLLING_IME_SETTINGS); 86 } 87 } 88 89 @Override 90 protected void onCreate(final Bundle savedInstanceState) { 91 setTheme(android.R.style.Theme_DeviceDefault_Light_NoActionBar); 92 super.onCreate(savedInstanceState); 93 94 setContentView(R.layout.setup_wizard); 95 96 RichInputMethodManager.init(this); 97 98 if (savedInstanceState == null) { 99 mStepNumber = determineSetupStepNumber(); 100 } else { 101 mStepNumber = savedInstanceState.getInt(STATE_STEP); 102 } 103 104 if (mStepNumber == STEP_3) { 105 // This IME already has been enabled and set as current IME. 106 // TODO: Implement tutorial. 107 invokeSettingsOfThisIme(); 108 finish(); 109 return; 110 } 111 112 // TODO: Use sans-serif-thin font family depending on the system locale white list and 113 // the SDK version. 114 final TextView titleView = (TextView)findViewById(R.id.setup_title); 115 final int appName = getApplicationInfo().labelRes; 116 titleView.setText(getString(R.string.setup_title, getString(appName))); 117 118 mStepIndicatorView = (SetupStepIndicatorView)findViewById(R.id.setup_step_indicator); 119 120 final SetupStep step1 = new SetupStep(findViewById(R.id.setup_step1), 121 appName, R.string.setup_step1_title, R.string.setup_step1_instruction, 122 R.drawable.ic_settings_language, R.string.language_settings); 123 step1.setAction(new Runnable() { 124 @Override 125 public void run() { 126 invokeLanguageAndInputSettings(); 127 mHandler.startPollingImeSettings(); 128 } 129 }); 130 mSetupSteps.addStep(STEP_1, step1); 131 132 final SetupStep step2 = new SetupStep(findViewById(R.id.setup_step2), 133 appName, R.string.setup_step2_title, R.string.setup_step2_instruction, 134 0 /* actionIcon */, R.string.select_input_method); 135 step2.setAction(new Runnable() { 136 @Override 137 public void run() { 138 // Invoke input method picker. 139 RichInputMethodManager.getInstance().getInputMethodManager() 140 .showInputMethodPicker(); 141 } 142 }); 143 mSetupSteps.addStep(STEP_2, step2); 144 145 final SetupStep step3 = new SetupStep(findViewById(R.id.setup_step3), 146 appName, R.string.setup_step3_title, 0 /* instruction */, 147 R.drawable.sym_keyboard_language_switch, R.string.setup_step3_instruction); 148 step3.setAction(new Runnable() { 149 @Override 150 public void run() { 151 invokeSubtypeEnablerOfThisIme(); 152 } 153 }); 154 mSetupSteps.addStep(STEP_3, step3); 155 } 156 157 private void invokeSetupWizardOfThisIme() { 158 final Intent intent = new Intent(); 159 intent.setClass(this, SetupActivity.class); 160 intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 161 | Intent.FLAG_ACTIVITY_CLEAR_TOP); 162 startActivity(intent); 163 } 164 165 private void invokeSettingsOfThisIme() { 166 final Intent intent = new Intent(); 167 intent.setClass(this, SettingsActivity.class); 168 intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 169 | Intent.FLAG_ACTIVITY_CLEAR_TOP); 170 startActivity(intent); 171 } 172 173 private void invokeLanguageAndInputSettings() { 174 final Intent intent = new Intent(); 175 intent.setAction(Settings.ACTION_INPUT_METHOD_SETTINGS); 176 intent.addCategory(Intent.CATEGORY_DEFAULT); 177 startActivity(intent); 178 } 179 180 private void invokeSubtypeEnablerOfThisIme() { 181 final InputMethodInfo imi = 182 RichInputMethodManager.getInstance().getInputMethodInfoOfThisIme(); 183 final Intent intent = new Intent(); 184 intent.setAction(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS); 185 intent.addCategory(Intent.CATEGORY_DEFAULT); 186 intent.putExtra(Settings.EXTRA_INPUT_METHOD_ID, imi.getId()); 187 startActivity(intent); 188 } 189 190 /** 191 * Check if the IME specified by the context is enabled. 192 * Note that {@link RichInputMethodManager} must have been initialized before calling this 193 * method. 194 * 195 * @param context package context of the IME to be checked. 196 * @return true if this IME is enabled. 197 */ 198 public static boolean isThisImeEnabled(final Context context) { 199 final String packageName = context.getPackageName(); 200 final InputMethodManager imm = RichInputMethodManager.getInstance().getInputMethodManager(); 201 for (final InputMethodInfo imi : imm.getEnabledInputMethodList()) { 202 if (packageName.equals(imi.getPackageName())) { 203 return true; 204 } 205 } 206 return false; 207 } 208 209 /** 210 * Check if the IME specified by the context is the current IME. 211 * Note that {@link RichInputMethodManager} must have been initialized before calling this 212 * method. 213 * 214 * @param context package context of the IME to be checked. 215 * @return true if this IME is the current IME. 216 */ 217 public static boolean isThisImeCurrent(final Context context) { 218 final InputMethodInfo myImi = 219 RichInputMethodManager.getInstance().getInputMethodInfoOfThisIme(); 220 final String currentImeId = Settings.Secure.getString( 221 context.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); 222 return myImi.getId().equals(currentImeId); 223 } 224 225 private int determineSetupStepNumber() { 226 mHandler.cancelPollingImeSettings(); 227 if (!isThisImeEnabled(this)) { 228 return STEP_1; 229 } 230 if (!isThisImeCurrent(this)) { 231 return STEP_2; 232 } 233 return STEP_3; 234 } 235 236 @Override 237 protected void onSaveInstanceState(final Bundle outState) { 238 super.onSaveInstanceState(outState); 239 outState.putInt(STATE_STEP, mStepNumber); 240 } 241 242 @Override 243 protected void onRestoreInstanceState(final Bundle savedInstanceState) { 244 super.onRestoreInstanceState(savedInstanceState); 245 mStepNumber = savedInstanceState.getInt(STATE_STEP); 246 } 247 248 @Override 249 protected void onStart() { 250 super.onStart(); 251 mStepNumber = determineSetupStepNumber(); 252 } 253 254 @Override 255 protected void onRestart() { 256 super.onRestart(); 257 mStepNumber = determineSetupStepNumber(); 258 } 259 260 @Override 261 protected void onResume() { 262 super.onResume(); 263 updateSetupStepView(); 264 } 265 266 @Override 267 public void onWindowFocusChanged(final boolean hasFocus) { 268 super.onWindowFocusChanged(hasFocus); 269 if (!hasFocus) { 270 return; 271 } 272 mStepNumber = determineSetupStepNumber(); 273 updateSetupStepView(); 274 } 275 276 private void updateSetupStepView() { 277 final int layoutDirection = ViewCompatUtils.getLayoutDirection(mStepIndicatorView); 278 mStepIndicatorView.setIndicatorPosition( 279 getIndicatorPosition(mStepNumber, mSetupSteps.getTotalStep(), layoutDirection)); 280 mSetupSteps.enableStep(mStepNumber); 281 } 282 283 private static float getIndicatorPosition(final int step, final int totalStep, 284 final int layoutDirection) { 285 final float pos = ((step - STEP_1) * 2 + 1) / (float)(totalStep * 2); 286 return (layoutDirection == ViewCompatUtils.LAYOUT_DIRECTION_RTL) ? 1.0f - pos : pos; 287 } 288 289 static final class SetupStep implements View.OnClickListener { 290 private final View mRootView; 291 private final TextView mActionLabel; 292 private Runnable mAction; 293 294 public SetupStep(final View rootView, final int appName, final int title, 295 final int instruction, final int actionIcon, final int actionLabel) { 296 mRootView = rootView; 297 final Resources res = rootView.getResources(); 298 final String applicationName = res.getString(appName); 299 300 final TextView titleView = (TextView)rootView.findViewById(R.id.setup_step_title); 301 titleView.setText(res.getString(title, applicationName)); 302 303 final TextView instructionView = (TextView)rootView.findViewById( 304 R.id.setup_step_instruction); 305 if (instruction == 0) { 306 instructionView.setVisibility(View.GONE); 307 } else { 308 instructionView.setText(res.getString(instruction, applicationName)); 309 } 310 311 mActionLabel = (TextView)rootView.findViewById(R.id.setup_step_action_label); 312 mActionLabel.setText(res.getString(actionLabel)); 313 if (actionIcon == 0) { 314 final int paddingEnd = ViewCompatUtils.getPaddingEnd(mActionLabel); 315 ViewCompatUtils.setPaddingRelative(mActionLabel, paddingEnd, 0, paddingEnd, 0); 316 } else { 317 final int overrideColor = res.getColor(R.color.setup_text_action); 318 final Drawable icon = res.getDrawable(actionIcon); 319 icon.setColorFilter(overrideColor, PorterDuff.Mode.MULTIPLY); 320 icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight()); 321 TextViewCompatUtils.setCompoundDrawablesRelative( 322 mActionLabel, icon, null, null, null); 323 } 324 } 325 326 public void setEnabled(final boolean enabled) { 327 mRootView.setVisibility(enabled ? View.VISIBLE : View.GONE); 328 } 329 330 public void setAction(final Runnable action) { 331 mActionLabel.setOnClickListener(this); 332 mAction = action; 333 } 334 335 @Override 336 public void onClick(final View v) { 337 if (mAction != null) { 338 mAction.run(); 339 } 340 } 341 } 342 343 static final class SetupStepGroup { 344 private final HashMap<Integer, SetupStep> mGroup = CollectionUtils.newHashMap(); 345 346 public void addStep(final int stepNo, final SetupStep step) { 347 mGroup.put(stepNo, step); 348 } 349 350 public void enableStep(final int enableStepNo) { 351 for (final Integer stepNo : mGroup.keySet()) { 352 final SetupStep step = mGroup.get(stepNo); 353 step.setEnabled(stepNo == enableStepNo); 354 } 355 } 356 357 public int getTotalStep() { 358 return mGroup.size(); 359 } 360 } 361} 362