1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * 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, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17package com.android.inputmethod.latin;
18
19import com.android.inputmethod.keyboard.Keyboard;
20import com.android.inputmethod.latin.Utils.RingCharBuffer;
21
22import android.util.Log;
23
24public class TextEntryState {
25    private static final String TAG = TextEntryState.class.getSimpleName();
26    private static final boolean DEBUG = false;
27
28    private static final int UNKNOWN = 0;
29    private static final int START = 1;
30    private static final int IN_WORD = 2;
31    private static final int ACCEPTED_DEFAULT = 3;
32    private static final int PICKED_SUGGESTION = 4;
33    private static final int PUNCTUATION_AFTER_WORD = 5;
34    private static final int PUNCTUATION_AFTER_ACCEPTED = 6;
35    private static final int SPACE_AFTER_ACCEPTED = 7;
36    private static final int SPACE_AFTER_PICKED = 8;
37    private static final int UNDO_COMMIT = 9;
38    private static final int RECORRECTING = 10;
39    private static final int PICKED_RECORRECTION = 11;
40
41    private static int sState = UNKNOWN;
42    private static int sPreviousState = UNKNOWN;
43
44    private static void setState(final int newState) {
45        sPreviousState = sState;
46        sState = newState;
47    }
48
49    public static void acceptedDefault(CharSequence typedWord, CharSequence actualWord,
50            int separatorCode) {
51        if (typedWord == null) return;
52        setState(ACCEPTED_DEFAULT);
53        LatinImeLogger.logOnAutoCorrection(
54                typedWord.toString(), actualWord.toString(), separatorCode);
55        if (DEBUG)
56            displayState("acceptedDefault", "typedWord", typedWord, "actualWord", actualWord);
57    }
58
59    // State.ACCEPTED_DEFAULT will be changed to other sub-states
60    // (see "case ACCEPTED_DEFAULT" in typedCharacter() below),
61    // and should be restored back to State.ACCEPTED_DEFAULT after processing for each sub-state.
62    public static void backToAcceptedDefault(CharSequence typedWord) {
63        if (typedWord == null) return;
64        switch (sState) {
65        case SPACE_AFTER_ACCEPTED:
66        case PUNCTUATION_AFTER_ACCEPTED:
67        case IN_WORD:
68            setState(ACCEPTED_DEFAULT);
69            break;
70        default:
71            break;
72        }
73        if (DEBUG) displayState("backToAcceptedDefault", "typedWord", typedWord);
74    }
75
76    public static void acceptedTyped(CharSequence typedWord) {
77        setState(PICKED_SUGGESTION);
78        if (DEBUG) displayState("acceptedTyped", "typedWord", typedWord);
79    }
80
81    public static void acceptedSuggestion(CharSequence typedWord, CharSequence actualWord) {
82        if (sState == RECORRECTING || sState == PICKED_RECORRECTION) {
83            setState(PICKED_RECORRECTION);
84        } else {
85            setState(PICKED_SUGGESTION);
86        }
87        if (DEBUG)
88            displayState("acceptedSuggestion", "typedWord", typedWord, "actualWord", actualWord);
89    }
90
91    public static void selectedForRecorrection() {
92        setState(RECORRECTING);
93        if (DEBUG) displayState("selectedForRecorrection");
94    }
95
96    public static void onAbortRecorrection() {
97        if (sState == RECORRECTING || sState == PICKED_RECORRECTION) {
98            setState(START);
99        }
100        if (DEBUG) displayState("onAbortRecorrection");
101    }
102
103    public static void typedCharacter(char c, boolean isSeparator, int x, int y) {
104        final boolean isSpace = (c == Keyboard.CODE_SPACE);
105        switch (sState) {
106        case IN_WORD:
107            if (isSpace || isSeparator) {
108                setState(START);
109            } else {
110                // State hasn't changed.
111            }
112            break;
113        case ACCEPTED_DEFAULT:
114        case SPACE_AFTER_PICKED:
115        case PUNCTUATION_AFTER_ACCEPTED:
116            if (isSpace) {
117                setState(SPACE_AFTER_ACCEPTED);
118            } else if (isSeparator) {
119                // Swap
120                setState(PUNCTUATION_AFTER_ACCEPTED);
121            } else {
122                setState(IN_WORD);
123            }
124            break;
125        case PICKED_SUGGESTION:
126        case PICKED_RECORRECTION:
127            if (isSpace) {
128                setState(SPACE_AFTER_PICKED);
129            } else if (isSeparator) {
130                // Swap
131                setState(PUNCTUATION_AFTER_ACCEPTED);
132            } else {
133                setState(IN_WORD);
134            }
135            break;
136        case START:
137        case UNKNOWN:
138        case SPACE_AFTER_ACCEPTED:
139        case PUNCTUATION_AFTER_WORD:
140            if (!isSpace && !isSeparator) {
141                setState(IN_WORD);
142            } else {
143                setState(START);
144            }
145            break;
146        case UNDO_COMMIT:
147            if (isSpace || isSeparator) {
148                setState(START);
149            } else {
150                setState(IN_WORD);
151            }
152            break;
153        case RECORRECTING:
154            setState(START);
155            break;
156        }
157        RingCharBuffer.getInstance().push(c, x, y);
158        if (isSeparator) {
159            LatinImeLogger.logOnInputSeparator();
160        } else {
161            LatinImeLogger.logOnInputChar();
162        }
163        if (DEBUG) displayState("typedCharacter", "char", c, "isSeparator", isSeparator);
164    }
165
166    public static void backspace() {
167        if (sState == ACCEPTED_DEFAULT) {
168            setState(UNDO_COMMIT);
169            LatinImeLogger.logOnAutoCorrectionCancelled();
170        } else if (sState == UNDO_COMMIT) {
171            setState(IN_WORD);
172        }
173        if (DEBUG) displayState("backspace");
174    }
175
176    public static void reset() {
177        setState(START);
178        if (DEBUG) displayState("reset");
179    }
180
181    public static boolean isAcceptedDefault() {
182        return sState == ACCEPTED_DEFAULT;
183    }
184
185    public static boolean isSpaceAfterPicked() {
186        return sState == SPACE_AFTER_PICKED;
187    }
188
189    public static boolean isUndoCommit() {
190        return sState == UNDO_COMMIT;
191    }
192
193    public static boolean isPunctuationAfterAccepted() {
194        return sState == PUNCTUATION_AFTER_ACCEPTED;
195    }
196
197    public static boolean isRecorrecting() {
198        return sState == RECORRECTING || sState == PICKED_RECORRECTION;
199    }
200
201    public static String getState() {
202        return stateName(sState);
203    }
204
205    private static String stateName(int state) {
206        switch (state) {
207        case START: return "START";
208        case IN_WORD: return "IN_WORD";
209        case ACCEPTED_DEFAULT: return "ACCEPTED_DEFAULT";
210        case PICKED_SUGGESTION: return "PICKED_SUGGESTION";
211        case PUNCTUATION_AFTER_WORD: return "PUNCTUATION_AFTER_WORD";
212        case PUNCTUATION_AFTER_ACCEPTED: return "PUNCTUATION_AFTER_ACCEPTED";
213        case SPACE_AFTER_ACCEPTED: return "SPACE_AFTER_ACCEPTED";
214        case SPACE_AFTER_PICKED: return "SPACE_AFTER_PICKED";
215        case UNDO_COMMIT: return "UNDO_COMMIT";
216        case RECORRECTING: return "RECORRECTING";
217        case PICKED_RECORRECTION: return "PICKED_RECORRECTION";
218        default: return "UNKNOWN";
219        }
220    }
221
222    private static void displayState(String title, Object ... args) {
223        final StringBuilder sb = new StringBuilder(title);
224        sb.append(':');
225        for (int i = 0; i < args.length; i += 2) {
226            sb.append(' ');
227            sb.append(args[i]);
228            sb.append('=');
229            sb.append(args[i+1].toString());
230        }
231        sb.append(" state=");
232        sb.append(stateName(sState));
233        sb.append(" previous=");
234        sb.append(stateName(sPreviousState));
235        Log.d(TAG, sb.toString());
236    }
237}
238