diff options
Diffstat (limited to 'java/src')
10 files changed, 618 insertions, 530 deletions
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java index fb75d6dc0..618322357 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java @@ -66,6 +66,8 @@ public final class DictionarySettingsFragment extends PreferenceFragment private boolean mChangedSettings; private DictionaryListInterfaceState mDictionaryListInterfaceState = new DictionaryListInterfaceState(); + private TreeMap<String, WordListPreference> mCurrentPreferenceMap = + new TreeMap<String, WordListPreference>(); // never null private final BroadcastReceiver mConnectivityChangedReceiver = new BroadcastReceiver() { @Override @@ -278,7 +280,7 @@ public final class DictionarySettingsFragment extends PreferenceFragment return result; } else { final String systemLocaleString = Locale.getDefault().toString(); - final TreeMap<String, WordListPreference> prefList = + final TreeMap<String, WordListPreference> prefMap = new TreeMap<String, WordListPreference>(); final int idIndex = cursor.getColumnIndex(MetadataDbHelper.WORDLISTID_COLUMN); final int versionIndex = cursor.getColumnIndex(MetadataDbHelper.VERSION_COLUMN); @@ -299,16 +301,31 @@ public final class DictionarySettingsFragment extends PreferenceFragment // The key is sorted in lexicographic order, according to the match level, then // the description. final String key = matchLevelString + "." + description + "." + wordlistId; - final WordListPreference existingPref = prefList.get(key); + final WordListPreference existingPref = prefMap.get(key); if (null == existingPref || hasPriority(status, existingPref.mStatus)) { - final WordListPreference pref = new WordListPreference(activity, - mDictionaryListInterfaceState, mClientId, wordlistId, version, locale, - description, status, filesize); - prefList.put(key, pref); + final WordListPreference oldPreference = mCurrentPreferenceMap.get(key); + final WordListPreference pref; + if (null != oldPreference + && oldPreference.mVersion == version + && oldPreference.mLocale.equals(locale)) { + // If the old preference has all the new attributes, reuse it. We test + // for version and locale because although attributes other than status + // need to be the same, others have been tested through the key of the + // map. Also, status may differ so we don't want to use #equals() here. + pref = oldPreference; + pref.mStatus = status; + } else { + // Otherwise, discard it and create a new one instead. + pref = new WordListPreference(activity, mDictionaryListInterfaceState, + mClientId, wordlistId, version, locale, description, status, + filesize); + } + prefMap.put(key, pref); } } while (cursor.moveToNext()); cursor.close(); - return prefList.values(); + mCurrentPreferenceMap = prefMap; + return prefMap.values(); } } diff --git a/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java b/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java index 29015d61b..451a0fb82 100644 --- a/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java +++ b/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java @@ -58,6 +58,8 @@ public final class WordListPreference extends Preference { // The metadata word list id and version of this word list. public final String mWordlistId; public final int mVersion; + public final Locale mLocale; + public final String mDescription; // The status public int mStatus; // The size of the dictionary file @@ -80,6 +82,8 @@ public final class WordListPreference extends Preference { mVersion = version; mWordlistId = wordlistId; mFilesize = filesize; + mLocale = locale; + mDescription = description; setLayoutResource(R.layout.dictionary_line); diff --git a/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java b/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java index 7fd1bedcb..761d9dcd6 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java @@ -18,6 +18,7 @@ package com.android.inputmethod.keyboard.internal; import android.content.res.TypedArray; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; @@ -35,12 +36,19 @@ import com.android.inputmethod.latin.ResizableIntArray; * @attr ref R.styleable#MainKeyboardView_gesturePreviewTrailWidth */ final class GesturePreviewTrail { + public static final boolean DBG_SHOW_POINTS = false; + public static final int POINT_TYPE_SAMPLED = 0; + public static final int POINT_TYPE_INTERPOLATED = 1; + public static final int POINT_TYPE_COMPROMISED = 2; + private static final int DEFAULT_CAPACITY = GestureStrokeWithPreviewPoints.PREVIEW_CAPACITY; // These three {@link ResizableIntArray}s should be synchronized by {@link #mEventTimes}. private final ResizableIntArray mXCoordinates = new ResizableIntArray(DEFAULT_CAPACITY); private final ResizableIntArray mYCoordinates = new ResizableIntArray(DEFAULT_CAPACITY); private final ResizableIntArray mEventTimes = new ResizableIntArray(DEFAULT_CAPACITY); + private final ResizableIntArray mPointTypes = new ResizableIntArray( + DBG_SHOW_POINTS ? DEFAULT_CAPACITY : 0); private int mCurrentStrokeId = -1; // The wall time of the zero value in {@link #mEventTimes} private long mCurrentTimeBase; @@ -75,9 +83,9 @@ final class GesturePreviewTrail { R.styleable.MainKeyboardView_gesturePreviewTrailShadowRatio, 0); mTrailShadowEnabled = (trailShadowRatioInt > 0); mTrailShadowRatio = (float)trailShadowRatioInt / (float)PERCENTAGE_INT; - mFadeoutStartDelay = mainKeyboardViewAttr.getInt( + mFadeoutStartDelay = DBG_SHOW_POINTS ? 2000 : mainKeyboardViewAttr.getInt( R.styleable.MainKeyboardView_gesturePreviewTrailFadeoutStartDelay, 0); - mFadeoutDuration = mainKeyboardViewAttr.getInt( + mFadeoutDuration = DBG_SHOW_POINTS ? 200 : mainKeyboardViewAttr.getInt( R.styleable.MainKeyboardView_gesturePreviewTrailFadeoutDuration, 0); mTrailLingerDuration = mFadeoutStartDelay + mFadeoutDuration; mUpdateInterval = mainKeyboardViewAttr.getInt( @@ -125,7 +133,7 @@ final class GesturePreviewTrail { final int lastInterpolatedIndex = (strokeId == mCurrentStrokeId) ? mLastInterpolatedDrawIndex : trailSize; mLastInterpolatedDrawIndex = stroke.interpolateStrokeAndReturnStartIndexOfLastSegment( - lastInterpolatedIndex, mEventTimes, mXCoordinates, mYCoordinates); + lastInterpolatedIndex, mEventTimes, mXCoordinates, mYCoordinates, mPointTypes); if (strokeId != mCurrentStrokeId) { final int elapsedTime = (int)(downTime - mCurrentTimeBase); for (int i = mTrailStartIndex; i < trailSize; i++) { @@ -204,6 +212,7 @@ final class GesturePreviewTrail { final int[] eventTimes = mEventTimes.getPrimitiveArray(); final int[] xCoords = mXCoordinates.getPrimitiveArray(); final int[] yCoords = mYCoordinates.getPrimitiveArray(); + final int[] pointTypes = mPointTypes.getPrimitiveArray(); final int sinceDown = (int)(SystemClock.uptimeMillis() - mCurrentTimeBase); int startIndex; for (startIndex = mTrailStartIndex; startIndex < trailSize; startIndex++) { @@ -246,6 +255,17 @@ final class GesturePreviewTrail { final int alpha = getAlpha(elapsedTime, params); paint.setAlpha(alpha); canvas.drawPath(path, paint); + if (DBG_SHOW_POINTS) { + if (pointTypes[i] == POINT_TYPE_INTERPOLATED) { + paint.setColor(Color.RED); + } else if (pointTypes[i] == POINT_TYPE_SAMPLED) { + paint.setColor(0xFFA000FF); + } else { + paint.setColor(Color.GREEN); + } + canvas.drawCircle(p1x - 1, p1y - 1, 2, paint); + paint.setColor(params.mTrailColor); + } } } p1x = p2x; @@ -265,6 +285,9 @@ final class GesturePreviewTrail { mEventTimes.setLength(newSize); mXCoordinates.setLength(newSize); mYCoordinates.setLength(newSize); + if (DBG_SHOW_POINTS) { + mPointTypes.setLength(newSize); + } // The start index of the last segment of the stroke // {@link mLastInterpolatedDrawIndex} should also be updated because all array // elements have just been shifted for compaction or been zeroed. diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java index 93ff26466..70363e602 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java @@ -145,7 +145,7 @@ public class GestureStroke { public void setKeyboardGeometry(final int keyWidth, final int keyboardHeight) { mKeyWidth = keyWidth; mMinYCoordinate = -(int)(keyboardHeight * EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO); - mMaxYCoordinate = keyboardHeight - 1; + mMaxYCoordinate = keyboardHeight; // TODO: Find an appropriate base metric for these length. Maybe diagonal length of the key? mDetectFastMoveSpeedThreshold = (int)(keyWidth * mParams.mDetectFastMoveSpeedThreshold); mGestureDynamicDistanceThresholdFrom = diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java index 312fd2160..ccb8802c6 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java @@ -152,7 +152,7 @@ public final class GestureStrokeWithPreviewPoints extends GestureStroke { */ public int interpolateStrokeAndReturnStartIndexOfLastSegment(final int lastInterpolatedIndex, final ResizableIntArray eventTimes, final ResizableIntArray xCoords, - final ResizableIntArray yCoords) { + final ResizableIntArray yCoords, final ResizableIntArray types) { final int size = mPreviewEventTimes.getLength(); final int[] pt = mPreviewEventTimes.getPrimitiveArray(); final int[] px = mPreviewXCoordinates.getPrimitiveArray(); @@ -189,11 +189,17 @@ public final class GestureStrokeWithPreviewPoints extends GestureStroke { eventTimes.add(d1, (int)(dt * t) + t1); xCoords.add(d1, (int)mInterpolator.mInterpolatedX); yCoords.add(d1, (int)mInterpolator.mInterpolatedY); + if (GesturePreviewTrail.DBG_SHOW_POINTS) { + types.add(d1, GesturePreviewTrail.POINT_TYPE_INTERPOLATED); + } d1++; } eventTimes.add(d1, pt[p2]); xCoords.add(d1, px[p2]); yCoords.add(d1, py[p2]); + if (GesturePreviewTrail.DBG_SHOW_POINTS) { + types.add(d1, GesturePreviewTrail.POINT_TYPE_SAMPLED); + } } return lastInterpolatedDrawIndex; } diff --git a/java/src/com/android/inputmethod/latin/ResourceUtils.java b/java/src/com/android/inputmethod/latin/ResourceUtils.java index b74b979b5..f0bfe75fe 100644 --- a/java/src/com/android/inputmethod/latin/ResourceUtils.java +++ b/java/src/com/android/inputmethod/latin/ResourceUtils.java @@ -35,8 +35,7 @@ public final class ResourceUtils { // This utility class is not publicly instantiable. } - private static final String DEFAULT_PREFIX = "DEFAULT,"; - private static final String HARDWARE_PREFIX = Build.HARDWARE + ","; + private static final String DEFAULT_KEY = "DEFAULT"; private static final HashMap<String, String> sDeviceOverrideValueMap = CollectionUtils.newHashMap(); @@ -48,28 +47,29 @@ public final class ResourceUtils { } final String[] overrideArray = res.getStringArray(overrideResId); - final String overrideValue = StringUtils.findPrefixedString(HARDWARE_PREFIX, overrideArray); + final String hardwareKey = "HARDWARE=" + Build.HARDWARE; + final String overrideValue = StringUtils.findValueOfKey(hardwareKey, overrideArray); // The overrideValue might be an empty string. if (overrideValue != null) { if (DEBUG) { Log.d(TAG, "Find override value:" + " resource="+ res.getResourceEntryName(overrideResId) - + " Build.HARDWARE=" + Build.HARDWARE + " override=" + overrideValue); + + " " + hardwareKey + " override=" + overrideValue); } sDeviceOverrideValueMap.put(key, overrideValue); return overrideValue; } - final String defaultValue = StringUtils.findPrefixedString(DEFAULT_PREFIX, overrideArray); + final String defaultValue = StringUtils.findValueOfKey(DEFAULT_KEY, overrideArray); // The defaultValue might be an empty string. if (defaultValue == null) { Log.w(TAG, "Couldn't find override value nor default value:" + " resource="+ res.getResourceEntryName(overrideResId) - + " Build.HARDWARE=" + Build.HARDWARE); + + " " + hardwareKey); } else if (DEBUG) { Log.d(TAG, "Found default value:" + " resource="+ res.getResourceEntryName(overrideResId) - + " Build.HARDWARE=" + Build.HARDWARE + " default=" + defaultValue); + + " " + hardwareKey + " " + DEFAULT_KEY + "=" + defaultValue); } sDeviceOverrideValueMap.put(key, defaultValue); return defaultValue; diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java index d5ee58a63..5ff101f7a 100644 --- a/java/src/com/android/inputmethod/latin/StringUtils.java +++ b/java/src/com/android/inputmethod/latin/StringUtils.java @@ -65,17 +65,24 @@ public final class StringUtils { } /** - * Find a string that start with specified prefix from an array. + * Find a value that has a specified key from an array of key-comma-value. * - * @param prefix a prefix string to find. - * @param array an string array to be searched. - * @return the rest part of the string that starts with the prefix. + * @param key a key string to find. + * @param array an array of key-comma-value string to be searched. + * @return the value part of the first string that has a specified key. * Returns null if it couldn't be found. */ - public static String findPrefixedString(final String prefix, final String[] array) { + public static String findValueOfKey(final String key, final String[] array) { + if (array == null) { + return null; + } for (final String element : array) { - if (element.startsWith(prefix)) { - return element.substring(prefix.length()); + final int posComma = element.indexOf(','); + if (posComma < 0) { + throw new RuntimeException("Element has no comma: " + element); + } + if (element.substring(0, posComma).equals(key)) { + return element.substring(posComma + 1); } } return null; diff --git a/java/src/com/android/inputmethod/latin/setup/SetupActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupActivity.java index acb0766f2..8a2de887d 100644 --- a/java/src/com/android/inputmethod/latin/setup/SetupActivity.java +++ b/java/src/com/android/inputmethod/latin/setup/SetupActivity.java @@ -17,265 +17,27 @@ package com.android.inputmethod.latin.setup; import android.app.Activity; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.res.Resources; -import android.media.MediaPlayer; -import android.net.Uri; import android.os.Bundle; -import android.os.Message; import android.provider.Settings; -import android.util.Log; -import android.view.View; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.VideoView; -import com.android.inputmethod.compat.TextViewCompatUtils; -import com.android.inputmethod.compat.ViewCompatUtils; -import com.android.inputmethod.latin.CollectionUtils; -import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; -import com.android.inputmethod.latin.SettingsActivity; -import com.android.inputmethod.latin.StaticInnerHandlerWrapper; - -import java.util.ArrayList; - -// TODO: Use Fragment to implement welcome screen and setup steps. -public final class SetupActivity extends Activity implements View.OnClickListener { - private static final String TAG = SetupActivity.class.getSimpleName(); - - private View mWelcomeScreen; - private View mSetupScreen; - private Uri mWelcomeVideoUri; - private VideoView mWelcomeVideoView; - private View mActionStart; - private View mActionNext; - private TextView mStep1Bullet; - private TextView mActionFinish; - private SetupStepGroup mSetupStepGroup; - private static final String STATE_STEP = "step"; - private int mStepNumber; - private static final int STEP_WELCOME = 0; - private static final int STEP_1 = 1; - private static final int STEP_2 = 2; - private static final int STEP_3 = 3; - private boolean mWasLanguageAndInputSettingsInvoked; - - private final SettingsPoolingHandler mHandler = new SettingsPoolingHandler(this); - - static final class SettingsPoolingHandler extends StaticInnerHandlerWrapper<SetupActivity> { - private static final int MSG_POLLING_IME_SETTINGS = 0; - private static final long IME_SETTINGS_POLLING_INTERVAL = 200; - - public SettingsPoolingHandler(final SetupActivity outerInstance) { - super(outerInstance); - } - - @Override - public void handleMessage(final Message msg) { - final SetupActivity setupActivity = getOuterInstance(); - if (setupActivity == null) { - return; - } - switch (msg.what) { - case MSG_POLLING_IME_SETTINGS: - if (SetupActivity.isThisImeEnabled(setupActivity)) { - setupActivity.invokeSetupWizardOfThisIme(); - return; - } - startPollingImeSettings(); - break; - } - } - - public void startPollingImeSettings() { - sendMessageDelayed(obtainMessage(MSG_POLLING_IME_SETTINGS), - IME_SETTINGS_POLLING_INTERVAL); - } - - public void cancelPollingImeSettings() { - removeMessages(MSG_POLLING_IME_SETTINGS); - } - } +public final class SetupActivity extends Activity { @Override protected void onCreate(final Bundle savedInstanceState) { - setTheme(android.R.style.Theme_DeviceDefault_Light_NoActionBar); super.onCreate(savedInstanceState); - - setContentView(R.layout.setup_wizard); - - RichInputMethodManager.init(this); - - if (savedInstanceState == null) { - mStepNumber = determineSetupStepNumber(); - if (mStepNumber == STEP_1 && !mWasLanguageAndInputSettingsInvoked) { - mStepNumber = STEP_WELCOME; - } - if (mStepNumber == STEP_3) { - // This IME already has been enabled and set as current IME. - // TODO: Implement tutorial. - invokeSettingsOfThisIme(); - finish(); - return; - } - } else { - mStepNumber = savedInstanceState.getInt(STATE_STEP); - } - - final String applicationName = getResources().getString(getApplicationInfo().labelRes); - mWelcomeScreen = findViewById(R.id.setup_welcome_screen); - final TextView welcomeTitle = (TextView)findViewById(R.id.setup_welcome_title); - welcomeTitle.setText(getString(R.string.setup_welcome_title, applicationName)); - - mSetupScreen = findViewById(R.id.setup_steps_screen); - final TextView stepsTitle = (TextView)findViewById(R.id.setup_title); - stepsTitle.setText(getString(R.string.setup_steps_title, applicationName)); - - final SetupStepIndicatorView indicatorView = - (SetupStepIndicatorView)findViewById(R.id.setup_step_indicator); - mSetupStepGroup = new SetupStepGroup(indicatorView); - - mStep1Bullet = (TextView)findViewById(R.id.setup_step1_bullet); - mStep1Bullet.setOnClickListener(this); - final SetupStep step1 = new SetupStep(STEP_1, applicationName, - mStep1Bullet, findViewById(R.id.setup_step1), - R.string.setup_step1_title, R.string.setup_step1_instruction, - R.string.setup_step1_finished_instruction, R.drawable.ic_setup_step1, - R.string.setup_step1_action); - step1.setAction(new Runnable() { - @Override - public void run() { - invokeLanguageAndInputSettings(); - mHandler.startPollingImeSettings(); - } - }); - mSetupStepGroup.addStep(step1); - - final SetupStep step2 = new SetupStep(STEP_2, applicationName, - (TextView)findViewById(R.id.setup_step2_bullet), findViewById(R.id.setup_step2), - R.string.setup_step2_title, R.string.setup_step2_instruction, - 0 /* finishedInstruction */, R.drawable.ic_setup_step2, - R.string.setup_step2_action); - step2.setAction(new Runnable() { - @Override - public void run() { - // Invoke input method picker. - RichInputMethodManager.getInstance().getInputMethodManager() - .showInputMethodPicker(); - } - }); - mSetupStepGroup.addStep(step2); - - final SetupStep step3 = new SetupStep(STEP_3, applicationName, - (TextView)findViewById(R.id.setup_step3_bullet), findViewById(R.id.setup_step3), - R.string.setup_step3_title, R.string.setup_step3_instruction, - 0 /* finishedInstruction */, R.drawable.ic_setup_step3, - R.string.setup_step3_action); - step3.setAction(new Runnable() { - @Override - public void run() { - invokeSubtypeEnablerOfThisIme(); - } - }); - mSetupStepGroup.addStep(step3); - - mWelcomeVideoUri = new Uri.Builder() - .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) - .authority(getPackageName()) - .path(Integer.toString(R.raw.setup_welcome_video)) - .build(); - mWelcomeVideoView = (VideoView)findViewById(R.id.setup_welcome_video); - mWelcomeVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { - @Override - public void onPrepared(final MediaPlayer mp) { - // Now VideoView has been laid-out and ready to play, remove background of it to - // reveal the video. - mWelcomeVideoView.setBackgroundResource(0); - mp.setLooping(true); - } - }); - final ImageView welcomeImageView = (ImageView)findViewById(R.id.setup_welcome_image); - mWelcomeVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() { - @Override - public boolean onError(final MediaPlayer mp, final int what, final int extra) { - Log.e(TAG, "Playing welcome video causes error: what=" + what + " extra=" + extra); - mWelcomeVideoView.setVisibility(View.GONE); - welcomeImageView.setImageResource(R.raw.setup_welcome_image); - welcomeImageView.setVisibility(View.VISIBLE); - return true; - } - }); - - mActionStart = findViewById(R.id.setup_start_label); - mActionStart.setOnClickListener(this); - mActionNext = findViewById(R.id.setup_next); - mActionNext.setOnClickListener(this); - mActionFinish = (TextView)findViewById(R.id.setup_finish); - TextViewCompatUtils.setCompoundDrawablesRelativeWithIntrinsicBounds(mActionFinish, - getResources().getDrawable(R.drawable.ic_setup_finish), null, null, null); - mActionFinish.setOnClickListener(this); - } - - @Override - public void onClick(final View v) { - if (v == mActionFinish) { - finish(); - return; - } - final int currentStep = determineSetupStepNumber(); - final int nextStep; - if (v == mActionStart) { - nextStep = STEP_1; - } else if (v == mActionNext) { - nextStep = mStepNumber + 1; - } else if (v == mStep1Bullet && currentStep == STEP_2) { - nextStep = STEP_1; - } else { - nextStep = mStepNumber; - } - if (mStepNumber != nextStep) { - mStepNumber = nextStep; - updateSetupStepView(); - } - } - - private void invokeSetupWizardOfThisIme() { - final Intent intent = new Intent(); - intent.setClass(this, SetupActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED - | Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - } - - private void invokeSettingsOfThisIme() { - final Intent intent = new Intent(); - intent.setClass(this, SettingsActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED - | Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - } - - private void invokeLanguageAndInputSettings() { - final Intent intent = new Intent(); - intent.setAction(Settings.ACTION_INPUT_METHOD_SETTINGS); - intent.addCategory(Intent.CATEGORY_DEFAULT); - startActivity(intent); - mWasLanguageAndInputSettingsInvoked = true; - } - - private void invokeSubtypeEnablerOfThisIme() { - final InputMethodInfo imi = - RichInputMethodManager.getInstance().getInputMethodInfoOfThisIme(); final Intent intent = new Intent(); - intent.setAction(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS); - intent.addCategory(Intent.CATEGORY_DEFAULT); - intent.putExtra(Settings.EXTRA_INPUT_METHOD_ID, imi.getId()); + intent.setClass(this, SetupWizardActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP + | Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); + if (!isFinishing()) { + finish(); + } } /** @@ -312,170 +74,4 @@ public final class SetupActivity extends Activity implements View.OnClickListene context.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); return myImi.getId().equals(currentImeId); } - - private int determineSetupStepNumber() { - mHandler.cancelPollingImeSettings(); - if (!isThisImeEnabled(this)) { - return STEP_1; - } - if (!isThisImeCurrent(this)) { - return STEP_2; - } - return STEP_3; - } - - @Override - protected void onSaveInstanceState(final Bundle outState) { - super.onSaveInstanceState(outState); - outState.putInt(STATE_STEP, mStepNumber); - } - - @Override - protected void onRestoreInstanceState(final Bundle savedInstanceState) { - super.onRestoreInstanceState(savedInstanceState); - mStepNumber = savedInstanceState.getInt(STATE_STEP); - } - - @Override - protected void onRestart() { - super.onRestart(); - if (mStepNumber != STEP_WELCOME) { - mStepNumber = determineSetupStepNumber(); - } - } - - @Override - protected void onResume() { - super.onResume(); - updateSetupStepView(); - } - - @Override - public void onBackPressed() { - if (mStepNumber == STEP_1) { - mStepNumber = STEP_WELCOME; - updateSetupStepView(); - return; - } - super.onBackPressed(); - } - - private static void hideAndStopVideo(final VideoView videoView) { - videoView.stopPlayback(); - videoView.setVisibility(View.INVISIBLE); - } - - @Override - protected void onPause() { - hideAndStopVideo(mWelcomeVideoView); - super.onPause(); - } - - @Override - public void onWindowFocusChanged(final boolean hasFocus) { - super.onWindowFocusChanged(hasFocus); - if (hasFocus && mStepNumber != STEP_WELCOME) { - mStepNumber = determineSetupStepNumber(); - updateSetupStepView(); - } - } - - private void updateSetupStepView() { - final boolean welcomeScreen = (mStepNumber == STEP_WELCOME); - mWelcomeScreen.setVisibility(welcomeScreen ? View.VISIBLE : View.GONE); - mSetupScreen.setVisibility(welcomeScreen ? View.GONE: View.VISIBLE); - if (welcomeScreen) { - mWelcomeVideoView.setVisibility(View.VISIBLE); - mWelcomeVideoView.setVideoURI(mWelcomeVideoUri); - mWelcomeVideoView.start(); - return; - } - hideAndStopVideo(mWelcomeVideoView); - final boolean isStepActionAlreadyDone = mStepNumber < determineSetupStepNumber(); - mSetupStepGroup.enableStep(mStepNumber, isStepActionAlreadyDone); - mActionNext.setVisibility(isStepActionAlreadyDone ? View.VISIBLE : View.GONE); - mActionFinish.setVisibility((mStepNumber == STEP_3) ? View.VISIBLE : View.GONE); - } - - static final class SetupStep implements View.OnClickListener { - public final int mStepNo; - private final View mStepView; - private final TextView mBulletView; - private final int mActivatedColor; - private final int mDeactivatedColor; - private final String mInstruction; - private final String mFinishedInstruction; - private final TextView mActionLabel; - private Runnable mAction; - - public SetupStep(final int stepNo, final String applicationName, final TextView bulletView, - final View stepView, final int title, final int instruction, - final int finishedInstruction,final int actionIcon, final int actionLabel) { - mStepNo = stepNo; - mStepView = stepView; - mBulletView = bulletView; - final Resources res = stepView.getResources(); - mActivatedColor = res.getColor(R.color.setup_text_action); - mDeactivatedColor = res.getColor(R.color.setup_text_dark); - - final TextView titleView = (TextView)mStepView.findViewById(R.id.setup_step_title); - titleView.setText(res.getString(title, applicationName)); - mInstruction = (instruction == 0) ? null - : res.getString(instruction, applicationName); - mFinishedInstruction = (finishedInstruction == 0) ? null - : res.getString(finishedInstruction, applicationName); - - mActionLabel = (TextView)mStepView.findViewById(R.id.setup_step_action_label); - mActionLabel.setText(res.getString(actionLabel)); - if (actionIcon == 0) { - final int paddingEnd = ViewCompatUtils.getPaddingEnd(mActionLabel); - ViewCompatUtils.setPaddingRelative(mActionLabel, paddingEnd, 0, paddingEnd, 0); - } else { - TextViewCompatUtils.setCompoundDrawablesRelativeWithIntrinsicBounds( - mActionLabel, res.getDrawable(actionIcon), null, null, null); - } - } - - public void setEnabled(final boolean enabled, final boolean isStepActionAlreadyDone) { - mStepView.setVisibility(enabled ? View.VISIBLE : View.GONE); - mBulletView.setTextColor(enabled ? mActivatedColor : mDeactivatedColor); - final TextView instructionView = (TextView)mStepView.findViewById( - R.id.setup_step_instruction); - instructionView.setText(isStepActionAlreadyDone ? mFinishedInstruction : mInstruction); - mActionLabel.setVisibility(isStepActionAlreadyDone ? View.GONE : View.VISIBLE); - } - - public void setAction(final Runnable action) { - mActionLabel.setOnClickListener(this); - mAction = action; - } - - @Override - public void onClick(final View v) { - if (v == mActionLabel && mAction != null) { - mAction.run(); - return; - } - } - } - - static final class SetupStepGroup { - private final SetupStepIndicatorView mIndicatorView; - private final ArrayList<SetupStep> mGroup = CollectionUtils.newArrayList(); - - public SetupStepGroup(final SetupStepIndicatorView indicatorView) { - mIndicatorView = indicatorView; - } - - public void addStep(final SetupStep step) { - mGroup.add(step); - } - - public void enableStep(final int enableStepNo, final boolean isStepActionAlreadyDone) { - for (final SetupStep step : mGroup) { - step.setEnabled(step.mStepNo == enableStepNo, isStepActionAlreadyDone); - } - mIndicatorView.setIndicatorPosition(enableStepNo - STEP_1, mGroup.size()); - } - } } diff --git a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java new file mode 100644 index 000000000..3406ecf34 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java @@ -0,0 +1,466 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.setup; + +import android.app.Activity; +import android.content.ContentResolver; +import android.content.Intent; +import android.content.res.Resources; +import android.media.MediaPlayer; +import android.net.Uri; +import android.os.Bundle; +import android.os.Message; +import android.provider.Settings; +import android.util.Log; +import android.view.View; +import android.view.inputmethod.InputMethodInfo; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.VideoView; + +import com.android.inputmethod.compat.TextViewCompatUtils; +import com.android.inputmethod.compat.ViewCompatUtils; +import com.android.inputmethod.latin.CollectionUtils; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.RichInputMethodManager; +import com.android.inputmethod.latin.SettingsActivity; +import com.android.inputmethod.latin.StaticInnerHandlerWrapper; + +import java.util.ArrayList; + +// TODO: Use Fragment to implement welcome screen and setup steps. +public final class SetupWizardActivity extends Activity implements View.OnClickListener { + static final String TAG = SetupWizardActivity.class.getSimpleName(); + + private View mSetupWizard; + private View mWelcomeScreen; + private View mSetupScreen; + private Uri mWelcomeVideoUri; + private VideoView mWelcomeVideoView; + private View mActionStart; + private View mActionNext; + private TextView mStep1Bullet; + private TextView mActionFinish; + private SetupStepGroup mSetupStepGroup; + private static final String STATE_STEP = "step"; + private int mStepNumber; + private static final int STEP_WELCOME = 0; + private static final int STEP_1 = 1; + private static final int STEP_2 = 2; + private static final int STEP_3 = 3; + private static final int STEP_LAUNCHING_IME_SETTINGS = 4; + private static final int STEP_BACK_FROM_IME_SETTINGS = 5; + + final SettingsPoolingHandler mHandler = new SettingsPoolingHandler(this); + + static final class SettingsPoolingHandler + extends StaticInnerHandlerWrapper<SetupWizardActivity> { + private static final int MSG_POLLING_IME_SETTINGS = 0; + private static final long IME_SETTINGS_POLLING_INTERVAL = 200; + + public SettingsPoolingHandler(final SetupWizardActivity outerInstance) { + super(outerInstance); + } + + @Override + public void handleMessage(final Message msg) { + final SetupWizardActivity setupWizardActivity = getOuterInstance(); + if (setupWizardActivity == null) { + return; + } + switch (msg.what) { + case MSG_POLLING_IME_SETTINGS: + if (SetupActivity.isThisImeEnabled(setupWizardActivity)) { + setupWizardActivity.invokeSetupWizardOfThisIme(); + return; + } + startPollingImeSettings(); + break; + } + } + + public void startPollingImeSettings() { + sendMessageDelayed(obtainMessage(MSG_POLLING_IME_SETTINGS), + IME_SETTINGS_POLLING_INTERVAL); + } + + public void cancelPollingImeSettings() { + removeMessages(MSG_POLLING_IME_SETTINGS); + } + } + + @Override + protected void onCreate(final Bundle savedInstanceState) { + setTheme(android.R.style.Theme_Translucent_NoTitleBar); + super.onCreate(savedInstanceState); + + setContentView(R.layout.setup_wizard); + mSetupWizard = findViewById(R.id.setup_wizard); + + RichInputMethodManager.init(this); + + if (savedInstanceState == null) { + mStepNumber = determineSetupStepNumberFromLauncher(); + } else { + mStepNumber = savedInstanceState.getInt(STATE_STEP); + } + + final String applicationName = getResources().getString(getApplicationInfo().labelRes); + mWelcomeScreen = findViewById(R.id.setup_welcome_screen); + final TextView welcomeTitle = (TextView)findViewById(R.id.setup_welcome_title); + welcomeTitle.setText(getString(R.string.setup_welcome_title, applicationName)); + + mSetupScreen = findViewById(R.id.setup_steps_screen); + final TextView stepsTitle = (TextView)findViewById(R.id.setup_title); + stepsTitle.setText(getString(R.string.setup_steps_title, applicationName)); + + final SetupStepIndicatorView indicatorView = + (SetupStepIndicatorView)findViewById(R.id.setup_step_indicator); + mSetupStepGroup = new SetupStepGroup(indicatorView); + + mStep1Bullet = (TextView)findViewById(R.id.setup_step1_bullet); + mStep1Bullet.setOnClickListener(this); + final SetupStep step1 = new SetupStep(STEP_1, applicationName, + mStep1Bullet, findViewById(R.id.setup_step1), + R.string.setup_step1_title, R.string.setup_step1_instruction, + R.string.setup_step1_finished_instruction, R.drawable.ic_setup_step1, + R.string.setup_step1_action); + step1.setAction(new Runnable() { + @Override + public void run() { + invokeLanguageAndInputSettings(); + mHandler.startPollingImeSettings(); + } + }); + mSetupStepGroup.addStep(step1); + + final SetupStep step2 = new SetupStep(STEP_2, applicationName, + (TextView)findViewById(R.id.setup_step2_bullet), findViewById(R.id.setup_step2), + R.string.setup_step2_title, R.string.setup_step2_instruction, + 0 /* finishedInstruction */, R.drawable.ic_setup_step2, + R.string.setup_step2_action); + step2.setAction(new Runnable() { + @Override + public void run() { + // Invoke input method picker. + RichInputMethodManager.getInstance().getInputMethodManager() + .showInputMethodPicker(); + } + }); + mSetupStepGroup.addStep(step2); + + final SetupStep step3 = new SetupStep(STEP_3, applicationName, + (TextView)findViewById(R.id.setup_step3_bullet), findViewById(R.id.setup_step3), + R.string.setup_step3_title, R.string.setup_step3_instruction, + 0 /* finishedInstruction */, R.drawable.ic_setup_step3, + R.string.setup_step3_action); + step3.setAction(new Runnable() { + @Override + public void run() { + invokeSubtypeEnablerOfThisIme(); + } + }); + mSetupStepGroup.addStep(step3); + + mWelcomeVideoUri = new Uri.Builder() + .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) + .authority(getPackageName()) + .path(Integer.toString(R.raw.setup_welcome_video)) + .build(); + final VideoView welcomeVideoView = (VideoView)findViewById(R.id.setup_welcome_video); + welcomeVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { + @Override + public void onPrepared(final MediaPlayer mp) { + // Now VideoView has been laid-out and ready to play, remove background of it to + // reveal the video. + welcomeVideoView.setBackgroundResource(0); + mp.setLooping(true); + } + }); + final ImageView welcomeImageView = (ImageView)findViewById(R.id.setup_welcome_image); + welcomeVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() { + @Override + public boolean onError(final MediaPlayer mp, final int what, final int extra) { + Log.e(TAG, "Playing welcome video causes error: what=" + what + " extra=" + extra); + welcomeVideoView.setVisibility(View.GONE); + welcomeImageView.setImageResource(R.raw.setup_welcome_image); + welcomeImageView.setVisibility(View.VISIBLE); + return true; + } + }); + mWelcomeVideoView = welcomeVideoView; + + mActionStart = findViewById(R.id.setup_start_label); + mActionStart.setOnClickListener(this); + mActionNext = findViewById(R.id.setup_next); + mActionNext.setOnClickListener(this); + mActionFinish = (TextView)findViewById(R.id.setup_finish); + TextViewCompatUtils.setCompoundDrawablesRelativeWithIntrinsicBounds(mActionFinish, + getResources().getDrawable(R.drawable.ic_setup_finish), null, null, null); + mActionFinish.setOnClickListener(this); + } + + @Override + public void onClick(final View v) { + if (v == mActionFinish) { + finish(); + return; + } + final int currentStep = determineSetupStepNumber(); + final int nextStep; + if (v == mActionStart) { + nextStep = STEP_1; + } else if (v == mActionNext) { + nextStep = mStepNumber + 1; + } else if (v == mStep1Bullet && currentStep == STEP_2) { + nextStep = STEP_1; + } else { + nextStep = mStepNumber; + } + if (mStepNumber != nextStep) { + mStepNumber = nextStep; + updateSetupStepView(); + } + } + + void invokeSetupWizardOfThisIme() { + final Intent intent = new Intent(); + intent.setClass(this, SetupWizardActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED + | Intent.FLAG_ACTIVITY_SINGLE_TOP + | Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + } + + private void invokeSettingsOfThisIme() { + final Intent intent = new Intent(); + intent.setClass(this, SettingsActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED + | Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + } + + void invokeLanguageAndInputSettings() { + final Intent intent = new Intent(); + intent.setAction(Settings.ACTION_INPUT_METHOD_SETTINGS); + intent.addCategory(Intent.CATEGORY_DEFAULT); + startActivity(intent); + } + + void invokeSubtypeEnablerOfThisIme() { + final InputMethodInfo imi = + RichInputMethodManager.getInstance().getInputMethodInfoOfThisIme(); + final Intent intent = new Intent(); + intent.setAction(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS); + intent.addCategory(Intent.CATEGORY_DEFAULT); + intent.putExtra(Settings.EXTRA_INPUT_METHOD_ID, imi.getId()); + startActivity(intent); + } + + private int determineSetupStepNumberFromLauncher() { + final int stepNumber = determineSetupStepNumber(); + if (stepNumber == STEP_1) { + return STEP_WELCOME; + } + if (stepNumber == STEP_3) { + return STEP_LAUNCHING_IME_SETTINGS; + } + return stepNumber; + } + + private int determineSetupStepNumber() { + mHandler.cancelPollingImeSettings(); + if (!SetupActivity.isThisImeEnabled(this)) { + return STEP_1; + } + if (!SetupActivity.isThisImeCurrent(this)) { + return STEP_2; + } + return STEP_3; + } + + @Override + protected void onSaveInstanceState(final Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt(STATE_STEP, mStepNumber); + } + + @Override + protected void onRestoreInstanceState(final Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + mStepNumber = savedInstanceState.getInt(STATE_STEP); + } + + private static boolean isInSetupSteps(final int stepNumber) { + return stepNumber >= STEP_1 && stepNumber <= STEP_3; + } + + @Override + protected void onRestart() { + super.onRestart(); + if (isInSetupSteps(mStepNumber)) { + mStepNumber = determineSetupStepNumber(); + } + } + + @Override + protected void onResume() { + super.onResume(); + if (mStepNumber == STEP_LAUNCHING_IME_SETTINGS) { + // Prevent white screen flashing while launching settings activity. + mSetupWizard.setVisibility(View.INVISIBLE); + invokeSettingsOfThisIme(); + mStepNumber = STEP_BACK_FROM_IME_SETTINGS; + return; + } + if (mStepNumber == STEP_BACK_FROM_IME_SETTINGS) { + finish(); + return; + } + updateSetupStepView(); + } + + @Override + public void onBackPressed() { + if (mStepNumber == STEP_1) { + mStepNumber = STEP_WELCOME; + updateSetupStepView(); + return; + } + super.onBackPressed(); + } + + private static void hideAndStopVideo(final VideoView videoView) { + videoView.stopPlayback(); + videoView.setVisibility(View.INVISIBLE); + } + + @Override + protected void onPause() { + hideAndStopVideo(mWelcomeVideoView); + super.onPause(); + } + + @Override + public void onWindowFocusChanged(final boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + if (hasFocus && isInSetupSteps(mStepNumber)) { + mStepNumber = determineSetupStepNumber(); + updateSetupStepView(); + } + } + + private void updateSetupStepView() { + mSetupWizard.setVisibility(View.VISIBLE); + final boolean welcomeScreen = (mStepNumber == STEP_WELCOME); + mWelcomeScreen.setVisibility(welcomeScreen ? View.VISIBLE : View.GONE); + mSetupScreen.setVisibility(welcomeScreen ? View.GONE : View.VISIBLE); + if (welcomeScreen) { + mWelcomeVideoView.setVisibility(View.VISIBLE); + mWelcomeVideoView.setVideoURI(mWelcomeVideoUri); + mWelcomeVideoView.start(); + return; + } + hideAndStopVideo(mWelcomeVideoView); + final boolean isStepActionAlreadyDone = mStepNumber < determineSetupStepNumber(); + mSetupStepGroup.enableStep(mStepNumber, isStepActionAlreadyDone); + mActionNext.setVisibility(isStepActionAlreadyDone ? View.VISIBLE : View.GONE); + mActionFinish.setVisibility((mStepNumber == STEP_3) ? View.VISIBLE : View.GONE); + } + + static final class SetupStep implements View.OnClickListener { + public final int mStepNo; + private final View mStepView; + private final TextView mBulletView; + private final int mActivatedColor; + private final int mDeactivatedColor; + private final String mInstruction; + private final String mFinishedInstruction; + private final TextView mActionLabel; + private Runnable mAction; + + public SetupStep(final int stepNo, final String applicationName, final TextView bulletView, + final View stepView, final int title, final int instruction, + final int finishedInstruction, final int actionIcon, final int actionLabel) { + mStepNo = stepNo; + mStepView = stepView; + mBulletView = bulletView; + final Resources res = stepView.getResources(); + mActivatedColor = res.getColor(R.color.setup_text_action); + mDeactivatedColor = res.getColor(R.color.setup_text_dark); + + final TextView titleView = (TextView)mStepView.findViewById(R.id.setup_step_title); + titleView.setText(res.getString(title, applicationName)); + mInstruction = (instruction == 0) ? null + : res.getString(instruction, applicationName); + mFinishedInstruction = (finishedInstruction == 0) ? null + : res.getString(finishedInstruction, applicationName); + + mActionLabel = (TextView)mStepView.findViewById(R.id.setup_step_action_label); + mActionLabel.setText(res.getString(actionLabel)); + if (actionIcon == 0) { + final int paddingEnd = ViewCompatUtils.getPaddingEnd(mActionLabel); + ViewCompatUtils.setPaddingRelative(mActionLabel, paddingEnd, 0, paddingEnd, 0); + } else { + TextViewCompatUtils.setCompoundDrawablesRelativeWithIntrinsicBounds( + mActionLabel, res.getDrawable(actionIcon), null, null, null); + } + } + + public void setEnabled(final boolean enabled, final boolean isStepActionAlreadyDone) { + mStepView.setVisibility(enabled ? View.VISIBLE : View.GONE); + mBulletView.setTextColor(enabled ? mActivatedColor : mDeactivatedColor); + final TextView instructionView = (TextView)mStepView.findViewById( + R.id.setup_step_instruction); + instructionView.setText(isStepActionAlreadyDone ? mFinishedInstruction : mInstruction); + mActionLabel.setVisibility(isStepActionAlreadyDone ? View.GONE : View.VISIBLE); + } + + public void setAction(final Runnable action) { + mActionLabel.setOnClickListener(this); + mAction = action; + } + + @Override + public void onClick(final View v) { + if (v == mActionLabel && mAction != null) { + mAction.run(); + return; + } + } + } + + static final class SetupStepGroup { + private final SetupStepIndicatorView mIndicatorView; + private final ArrayList<SetupStep> mGroup = CollectionUtils.newArrayList(); + + public SetupStepGroup(final SetupStepIndicatorView indicatorView) { + mIndicatorView = indicatorView; + } + + public void addStep(final SetupStep step) { + mGroup.add(step); + } + + public void enableStep(final int enableStepNo, final boolean isStepActionAlreadyDone) { + for (final SetupStep step : mGroup) { + step.setEnabled(step.mStepNo == enableStepNo, isStepActionAlreadyDone); + } + mIndicatorView.setIndicatorPosition(enableStepNo - STEP_1, mGroup.size()); + } + } +} diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index cd2f9944d..c8242c8b1 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -149,18 +149,18 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private static final ResearchLogger sInstance = new ResearchLogger(); private static String sAccountType = null; private static String sAllowedAccountDomain = null; - /* package */ ResearchLog mMainResearchLog; + private ResearchLog mMainResearchLog; // always non-null after init() is called // mFeedbackLog records all events for the session, private or not (excepting // passwords). It is written to permanent storage only if the user explicitly commands // the system to do so. // LogUnits are queued in the LogBuffers and published to the ResearchLogs when words are // complete. - /* package */ MainLogBuffer mMainLogBuffer; + /* package for test */ MainLogBuffer mMainLogBuffer; // always non-null after init() is called // TODO: Remove the feedback log. The feedback log continuously captured user data in case the // user wanted to submit it. We now use the mUserRecordingLogBuffer to allow the user to // explicitly reproduce a problem. - /* package */ ResearchLog mFeedbackLog; - /* package */ LogBuffer mFeedbackLogBuffer; + private ResearchLog mFeedbackLog; + private LogBuffer mFeedbackLogBuffer; /* package */ ResearchLog mUserRecordingLog; /* package */ LogBuffer mUserRecordingLogBuffer; private File mUserRecordingFile = null; @@ -240,6 +240,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mResearchLogDirectory = new ResearchLogDirectory(mLatinIME); cleanLogDirectoryIfNeeded(mResearchLogDirectory, System.currentTimeMillis()); + // Initialize log buffers + resetLogBuffers(); + // Initialize external services mUploadIntent = new Intent(mLatinIME, UploaderService.class); mUploadNowIntent = new Intent(mLatinIME, UploaderService.class); @@ -251,6 +254,39 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mReplayer.setKeyboardSwitcher(keyboardSwitcher); } + private void resetLogBuffers() { + mMainResearchLog = new ResearchLog(mResearchLogDirectory.getLogFilePath( + System.currentTimeMillis(), System.nanoTime()), mLatinIME); + final int numWordsToIgnore = new Random().nextInt(NUMBER_OF_WORDS_BETWEEN_SAMPLES + 1); + mMainLogBuffer = new MainLogBuffer(NUMBER_OF_WORDS_BETWEEN_SAMPLES, numWordsToIgnore, + mSuggest) { + @Override + protected void publish(final ArrayList<LogUnit> logUnits, + boolean canIncludePrivateData) { + canIncludePrivateData |= IS_LOGGING_EVERYTHING; + for (final LogUnit logUnit : logUnits) { + if (DEBUG) { + final String wordsString = logUnit.getWordsAsString(); + Log.d(TAG, "onPublish: '" + wordsString + + "', hc: " + logUnit.containsCorrection() + + ", cipd: " + canIncludePrivateData); + } + for (final String word : logUnit.getWordsAsStringArray()) { + final Dictionary dictionary = getDictionary(); + mStatistics.recordWordEntered( + dictionary != null && dictionary.isValidWord(word), + logUnit.containsCorrection()); + } + } + publishLogUnits(logUnits, mMainResearchLog, canIncludePrivateData); + } + }; + + mFeedbackLog = new ResearchLog(mResearchLogDirectory.getLogFilePath( + System.currentTimeMillis(), System.nanoTime()), mLatinIME); + mFeedbackLogBuffer = new FixedLogBuffer(FEEDBACK_WORD_BUFFER_SIZE); + } + private void cleanLogDirectoryIfNeeded(final ResearchLogDirectory researchLogDirectory, final long now) { final long lastCleanupTime = ResearchSettings.readResearchLastDirCleanupTime(mPrefs); @@ -379,49 +415,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang requestIndicatorRedraw(); mStatistics.reset(); checkForEmptyEditor(); - if (mFeedbackLogBuffer == null) { - resetFeedbackLogging(); - } - if (!isAllowedToLog()) { - // Log.w(TAG, "not in usability mode; not logging"); - return; - } - if (mMainLogBuffer == null) { - mMainResearchLog = new ResearchLog(mResearchLogDirectory.getLogFilePath( - System.currentTimeMillis(), System.nanoTime()), mLatinIME); - final int numWordsToIgnore = new Random().nextInt(NUMBER_OF_WORDS_BETWEEN_SAMPLES + 1); - mMainLogBuffer = new MainLogBuffer(NUMBER_OF_WORDS_BETWEEN_SAMPLES, numWordsToIgnore, - mSuggest) { - @Override - protected void publish(final ArrayList<LogUnit> logUnits, - boolean canIncludePrivateData) { - canIncludePrivateData |= IS_LOGGING_EVERYTHING; - for (final LogUnit logUnit : logUnits) { - if (DEBUG) { - final String wordsString = logUnit.getWordsAsString(); - Log.d(TAG, "onPublish: '" + wordsString - + "', hc: " + logUnit.containsCorrection() - + ", cipd: " + canIncludePrivateData); - } - for (final String word : logUnit.getWordsAsStringArray()) { - final Dictionary dictionary = getDictionary(); - mStatistics.recordWordEntered( - dictionary != null && dictionary.isValidWord(word), - logUnit.containsCorrection()); - } - } - if (mMainResearchLog != null) { - publishLogUnits(logUnits, mMainResearchLog, canIncludePrivateData); - } - } - }; - } - } - - private void resetFeedbackLogging() { - mFeedbackLog = new ResearchLog(mResearchLogDirectory.getLogFilePath( - System.currentTimeMillis(), System.nanoTime()), mLatinIME); - mFeedbackLogBuffer = new FixedLogBuffer(FEEDBACK_WORD_BUFFER_SIZE); } /* package */ void stop() { @@ -431,35 +424,27 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang // Commit mCurrentLogUnit before closing. commitCurrentLogUnit(); - if (mMainLogBuffer != null) { - mMainLogBuffer.shiftAndPublishAll(); - logStatistics(); - commitCurrentLogUnit(); - mMainLogBuffer.setIsStopping(); - mMainLogBuffer.shiftAndPublishAll(); - mMainResearchLog.blockingClose(RESEARCHLOG_CLOSE_TIMEOUT_IN_MS); - mMainLogBuffer = null; - } - if (mFeedbackLogBuffer != null) { - mFeedbackLog.blockingClose(RESEARCHLOG_CLOSE_TIMEOUT_IN_MS); - mFeedbackLogBuffer = null; - } + mMainLogBuffer.shiftAndPublishAll(); + logStatistics(); + commitCurrentLogUnit(); + mMainLogBuffer.setIsStopping(); + mMainLogBuffer.shiftAndPublishAll(); + mMainResearchLog.blockingClose(RESEARCHLOG_CLOSE_TIMEOUT_IN_MS); + mFeedbackLog.blockingClose(RESEARCHLOG_CLOSE_TIMEOUT_IN_MS); + + resetLogBuffers(); } public void abort() { if (DEBUG) { Log.d(TAG, "abort called"); } - if (mMainLogBuffer != null) { - mMainLogBuffer.clear(); - mMainResearchLog.blockingAbort(RESEARCHLOG_ABORT_TIMEOUT_IN_MS); - mMainLogBuffer = null; - } - if (mFeedbackLogBuffer != null) { - mFeedbackLogBuffer.clear(); - mFeedbackLog.blockingAbort(RESEARCHLOG_ABORT_TIMEOUT_IN_MS); - mFeedbackLogBuffer = null; - } + mMainLogBuffer.clear(); + mMainResearchLog.blockingAbort(RESEARCHLOG_ABORT_TIMEOUT_IN_MS); + mFeedbackLogBuffer.clear(); + mFeedbackLog.blockingAbort(RESEARCHLOG_ABORT_TIMEOUT_IN_MS); + + resetLogBuffers(); } private void restart() { @@ -740,8 +725,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang public void initSuggest(final Suggest suggest) { mSuggest = suggest; - // MainLogBuffer has out-of-date Suggest object. Need to close it down and create a new - // one. + // MainLogBuffer now has an out-of-date Suggest object. Close down MainLogBuffer and create + // a new one. if (mMainLogBuffer != null) { stop(); start(); @@ -852,9 +837,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang ": " + mCurrentLogUnit.getWordsAsString() : "")); } if (!mCurrentLogUnit.isEmpty()) { - if (mMainLogBuffer != null) { - mMainLogBuffer.shiftIn(mCurrentLogUnit); - } + mMainLogBuffer.shiftIn(mCurrentLogUnit); if (mFeedbackLogBuffer != null) { mFeedbackLogBuffer.shiftIn(mCurrentLogUnit); } @@ -882,9 +865,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang // // Note that we don't use mLastLogUnit here, because it only goes one word back and is only // needed for reverts, which only happen one back. - if (mMainLogBuffer == null) { - return; - } final LogUnit oldLogUnit = mMainLogBuffer.peekLastLogUnit(); // Check that expected word matches. @@ -938,6 +918,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final ResearchLog researchLog, final boolean canIncludePrivateData) { final LogUnit openingLogUnit = new LogUnit(); if (logUnits.isEmpty()) return; + if (!isAllowedToLog()) return; // LogUnits not containing private data, such as contextual data for the log, do not require // logSegment boundary statements. if (canIncludePrivateData) { @@ -1371,11 +1352,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang public static void latinIME_promotePhantomSpace() { final ResearchLogger researchLogger = getInstance(); final LogUnit logUnit; - if (researchLogger.mMainLogBuffer == null) { - logUnit = researchLogger.mCurrentLogUnit; - } else { - logUnit = researchLogger.mMainLogBuffer.peekLastLogUnit(); - } + logUnit = researchLogger.mMainLogBuffer.peekLastLogUnit(); researchLogger.enqueueEvent(logUnit, LOGSTATEMENT_LATINIME_PROMOTEPHANTOMSPACE); } @@ -1392,11 +1369,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final String charactersAfterSwap) { final ResearchLogger researchLogger = getInstance(); final LogUnit logUnit; - if (researchLogger.mMainLogBuffer == null) { - logUnit = null; - } else { - logUnit = researchLogger.mMainLogBuffer.peekLastLogUnit(); - } + logUnit = researchLogger.mMainLogBuffer.peekLastLogUnit(); if (logUnit != null) { researchLogger.enqueueEvent(logUnit, LOGSTATEMENT_LATINIME_SWAPSWAPPERANDSPACE, originalCharacters, charactersAfterSwap); @@ -1469,11 +1442,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final ResearchLogger researchLogger = getInstance(); // TODO: Verify that mCurrentLogUnit has been restored and contains the reverted word. final LogUnit logUnit; - if (researchLogger.mMainLogBuffer == null) { - logUnit = null; - } else { - logUnit = researchLogger.mMainLogBuffer.peekLastLogUnit(); - } + logUnit = researchLogger.mMainLogBuffer.peekLastLogUnit(); if (originallyTypedWord.length() > 0 && hasLetters(originallyTypedWord)) { if (logUnit != null) { logUnit.setWords(originallyTypedWord); |