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