1/*
2 * Copyright (C) 2008-2012  OMRON SOFTWARE Co., Ltd.
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 jp.co.omronsoft.openwnn;
18
19import jp.co.omronsoft.openwnn.JAJP.*;
20import android.inputmethodservice.InputMethodService;
21import android.view.WindowManager;
22import android.content.Context;
23import android.view.View;
24import android.view.KeyEvent;
25import android.view.MotionEvent;
26import android.content.SharedPreferences;
27import android.preference.PreferenceManager;
28
29import android.util.Log;
30import android.os.*;
31import android.view.inputmethod.*;
32import android.content.res.Configuration;
33import android.graphics.*;
34import android.graphics.drawable.*;
35
36import java.util.ArrayList;
37import java.util.List;
38
39import jp.co.omronsoft.openwnn.KeyAction;
40
41/**
42 * The OpenWnn IME's base class.
43 *
44 * @author Copyright (C) 2009-2011 OMRON SOFTWARE CO., LTD.  All Rights Reserved.
45 */
46public class OpenWnn extends InputMethodService {
47
48    /** Candidate view */
49    protected CandidatesViewManager  mCandidatesViewManager = null;
50    /** Input view (software keyboard) */
51    protected InputViewManager  mInputViewManager = null;
52    /** Conversion engine */
53    protected WnnEngine  mConverter = null;
54    /** Pre-converter (for Romaji-to-Kana input, Hangul input, etc.) */
55    protected LetterConverter  mPreConverter = null;
56    /** The inputing/editing string */
57    protected ComposingText  mComposingText = null;
58    /** The input connection */
59    protected InputConnection mInputConnection = null;
60    /** Auto hide candidate view */
61    protected boolean mAutoHideMode = true;
62    /** Direct input mode */
63    protected boolean mDirectInputMode = true;
64
65    /** Flag for checking if the previous down key event is consumed by OpenWnn  */
66    private boolean mConsumeDownEvent;
67
68    /** for isXLarge */
69    private static boolean mIsXLarge = false;
70
71    /** TextCandidatesViewManager */
72    protected TextCandidatesViewManager mTextCandidatesViewManager = null;
73
74    /** TextCandidates1LineViewManager */
75    protected TextCandidates1LineViewManager mTextCandidates1LineViewManager = null;
76
77    /** The instance of current IME */
78    private static OpenWnn mCurrentIme;
79
80    /** KeyAction list */
81    private List<KeyAction> KeyActionList = new ArrayList<KeyAction>();
82
83    /**
84     * Constructor
85     */
86    public OpenWnn() {
87        super();
88    }
89
90    /***********************************************************************
91     * InputMethodService
92     **********************************************************************/
93    /** @see android.inputmethodservice.InputMethodService#onCreate */
94    @Override public void onCreate() {
95        updateXLargeMode();
96        super.onCreate();
97
98        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
99
100        mCurrentIme = this;
101
102
103        mTextCandidatesViewManager = new TextCandidatesViewManager(-1);
104        if (isXLarge()) {
105            mTextCandidates1LineViewManager =
106                new TextCandidates1LineViewManager(OpenWnnEngineJAJP.LIMIT_OF_CANDIDATES_1LINE);
107            mCandidatesViewManager = mTextCandidates1LineViewManager;
108        } else {
109            mCandidatesViewManager = mTextCandidatesViewManager;
110        }
111
112        if (mConverter != null) { mConverter.init(); }
113        if (mComposingText != null) { mComposingText.clear(); }
114    }
115
116    /** @see android.inputmethodservice.InputMethodService#onCreateCandidatesView */
117    @Override public View onCreateCandidatesView() {
118        if (mCandidatesViewManager != null) {
119            WindowManager wm = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
120            if (isXLarge()) {
121                mCandidatesViewManager = mTextCandidates1LineViewManager;
122                mTextCandidatesViewManager.initView(this,
123                                                        wm.getDefaultDisplay().getWidth(),
124                                                        wm.getDefaultDisplay().getHeight());
125            } else {
126                mCandidatesViewManager = mTextCandidatesViewManager;
127            }
128            View view = mCandidatesViewManager.initView(this,
129                                                        wm.getDefaultDisplay().getWidth(),
130                                                        wm.getDefaultDisplay().getHeight());
131            mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
132            return view;
133        } else {
134            return super.onCreateCandidatesView();
135        }
136    }
137
138    /** @see android.inputmethodservice.InputMethodService#onCreateInputView */
139    @Override public View onCreateInputView() {
140        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
141
142
143        if (mInputViewManager != null) {
144            WindowManager wm = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
145            return mInputViewManager.initView(this,
146                                              wm.getDefaultDisplay().getWidth(),
147                                              wm.getDefaultDisplay().getHeight());
148        } else {
149            return super.onCreateInputView();
150        }
151    }
152
153    /** @see android.inputmethodservice.InputMethodService#onDestroy */
154    @Override public void onDestroy() {
155        super.onDestroy();
156        mCurrentIme = null;
157        close();
158    }
159
160    /** @see android.inputmethodservice.InputMethodService#onKeyDown */
161    @Override public boolean onKeyDown(int keyCode, KeyEvent event) {
162        mConsumeDownEvent = onEvent(new OpenWnnEvent(event));
163
164        KeyAction Keycodeinfo = new KeyAction();
165        Keycodeinfo.mConsumeDownEvent = mConsumeDownEvent;
166        Keycodeinfo.mKeyCode = keyCode;
167
168        int cnt = KeyActionList.size();
169        if (cnt != 0) {
170            for (int i = 0; i < cnt; i++) {
171                if (KeyActionList.get(i).mKeyCode == keyCode) {
172                    KeyActionList.remove(i);
173                    break;
174                }
175            }
176        }
177        KeyActionList.add(Keycodeinfo);
178        if (!mConsumeDownEvent) {
179            return super.onKeyDown(keyCode, event);
180        }
181        return mConsumeDownEvent;
182    }
183
184    /** @see android.inputmethodservice.InputMethodService#onKeyUp */
185    @Override public boolean onKeyUp(int keyCode, KeyEvent event) {
186        boolean ret = mConsumeDownEvent;
187        int cnt = KeyActionList.size();
188        for (int i = 0; i < cnt; i++) {
189            KeyAction Keycodeinfo = KeyActionList.get(i);
190            if (Keycodeinfo.mKeyCode == keyCode) {
191                ret = Keycodeinfo.mConsumeDownEvent;
192                KeyActionList.remove(i);
193                break;
194            }
195        }
196        if (!ret) {
197            ret = super.onKeyUp(keyCode, event);
198        }else{
199            ret = onEvent(new OpenWnnEvent(event));
200        }
201        return ret;
202    }
203
204    /**
205     * Called when the key long press event occurred.
206     *
207     * @see android.inputmethodservice.InputMethodService#onKeyLongPress
208     */
209    @Override public boolean onKeyLongPress(int keyCode, KeyEvent event) {
210        if (mCurrentIme == null) {
211            Log.e("iWnn", "OpenWnn::onKeyLongPress()  Unprocessing onCreate() ");
212            return super.onKeyLongPress(keyCode, event);
213        }
214
215        OpenWnnEvent wnnEvent = new OpenWnnEvent(event);
216        wnnEvent.code = OpenWnnEvent.KEYLONGPRESS;
217        return onEvent(wnnEvent);
218    }
219
220    /** @see android.inputmethodservice.InputMethodService#onStartInput */
221    @Override public void onStartInput(EditorInfo attribute, boolean restarting) {
222        super.onStartInput(attribute, restarting);
223        mInputConnection = getCurrentInputConnection();
224        if (!restarting && mComposingText != null) {
225            mComposingText.clear();
226        }
227    }
228
229    /** @see android.inputmethodservice.InputMethodService#onStartInputView */
230    @Override public void onStartInputView(EditorInfo attribute, boolean restarting) {
231        super.onStartInputView(attribute, restarting);
232        mInputConnection = getCurrentInputConnection();
233
234        setCandidatesViewShown(false);
235        if (mInputConnection != null) {
236            mDirectInputMode = false;
237            if (mConverter != null) { mConverter.init(); }
238        } else {
239            mDirectInputMode = true;
240        }
241        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
242        if (mCandidatesViewManager != null) { mCandidatesViewManager.setPreferences(pref);  }
243        if (mInputViewManager != null) { mInputViewManager.setPreferences(pref, attribute);  }
244        if (mPreConverter != null) { mPreConverter.setPreferences(pref);  }
245        if (mConverter != null) { mConverter.setPreferences(pref);  }
246    }
247
248    /** @see android.inputmethodservice.InputMethodService#requestHideSelf */
249    @Override public void requestHideSelf(int flag) {
250        super.requestHideSelf(flag);
251        if (mInputViewManager == null) {
252            hideWindow();
253        }
254    }
255
256    /** @see android.inputmethodservice.InputMethodService#setCandidatesViewShown */
257    @Override public void setCandidatesViewShown(boolean shown) {
258        super.setCandidatesViewShown(shown);
259        if (shown) {
260            showWindow(true);
261        } else {
262            if (mAutoHideMode && mInputViewManager == null) {
263                hideWindow();
264            }
265        }
266    }
267
268    /** @see android.inputmethodservice.InputMethodService#hideWindow */
269    @Override public void hideWindow() {
270        super.hideWindow();
271        mDirectInputMode = true;
272        hideStatusIcon();
273    }
274    /** @see android.inputmethodservice.InputMethodService#onComputeInsets */
275    @Override public void onComputeInsets(InputMethodService.Insets outInsets) {
276        super.onComputeInsets(outInsets);
277        outInsets.contentTopInsets = outInsets.visibleTopInsets;
278    }
279
280
281    /**********************************************************************
282     * OpenWnn
283     **********************************************************************/
284    /**
285     * Process an event.
286     *
287     * @param  ev  An event
288     * @return  {@code true} if the event is processed in this method; {@code false} if not.
289     */
290    public boolean onEvent(OpenWnnEvent ev) {
291        return false;
292    }
293
294    /**
295     * Search a character for toggle input.
296     *
297     * @param prevChar     The character input previous
298     * @param toggleTable  Toggle table
299     * @param reverse      {@code false} if toggle direction is forward, {@code true} if toggle direction is backward
300     * @return          A character ({@code null} if no character is found)
301     */
302    protected String searchToggleCharacter(String prevChar, String[] toggleTable, boolean reverse) {
303        for (int i = 0; i < toggleTable.length; i++) {
304            if (prevChar.equals(toggleTable[i])) {
305                if (reverse) {
306                    i--;
307                    if (i < 0) {
308                        return toggleTable[toggleTable.length - 1];
309                    } else {
310                        return toggleTable[i];
311                    }
312                } else {
313                    i++;
314                    if (i == toggleTable.length) {
315                        return toggleTable[0];
316                    } else {
317                        return toggleTable[i];
318                    }
319                }
320            }
321        }
322        return null;
323    }
324
325    /**
326     * Processing of resource open when IME ends.
327     */
328    protected void close() {
329        if (mConverter != null) { mConverter.close(); }
330    }
331
332    /**
333     * Whether the x large mode.
334     *
335     * @return      {@code true} if x large; {@code false} if not x large.
336     */
337    public static boolean isXLarge() {
338        return mIsXLarge;
339    }
340
341    /**
342     * Update the x large mode.
343     */
344    public void updateXLargeMode() {
345        mIsXLarge = ((getResources().getConfiguration().screenLayout &
346                      Configuration.SCREENLAYOUT_SIZE_MASK)
347                      == Configuration.SCREENLAYOUT_SIZE_XLARGE);
348    }
349
350    /**
351     * Get the instance of current IME.
352     *
353     * @return the instance of current IME, See {@link jp.co.omronsoft.openwnn.OpenWnn}
354     */
355    public static OpenWnn getCurrentIme() {
356        return mCurrentIme;
357    }
358
359    /**
360     * Check through key code in IME.
361     *
362     * @param keyCode  check key code.
363     * @return {@code true} if through key code; {@code false} otherwise.
364     */
365    protected boolean isThroughKeyCode(int keyCode) {
366        boolean result;
367        switch (keyCode) {
368        case KeyEvent.KEYCODE_CALL:
369        case KeyEvent.KEYCODE_VOLUME_DOWN:
370        case KeyEvent.KEYCODE_VOLUME_UP:
371        case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
372        case KeyEvent.KEYCODE_MEDIA_NEXT:
373        case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
374        case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
375        case KeyEvent.KEYCODE_MEDIA_REWIND:
376        case KeyEvent.KEYCODE_MEDIA_STOP:
377        case KeyEvent.KEYCODE_MUTE:
378        case KeyEvent.KEYCODE_HEADSETHOOK:
379        case KeyEvent.KEYCODE_VOLUME_MUTE:
380        case KeyEvent.KEYCODE_MEDIA_CLOSE:
381        case KeyEvent.KEYCODE_MEDIA_EJECT:
382        case KeyEvent.KEYCODE_MEDIA_PAUSE:
383        case KeyEvent.KEYCODE_MEDIA_PLAY:
384        case KeyEvent.KEYCODE_MEDIA_RECORD:
385        case KeyEvent.KEYCODE_MANNER_MODE:
386            result = true;
387            break;
388
389        default:
390            result = false;
391            break;
392
393        }
394        return result;
395    }
396
397    /**
398     * Check ten-key code.
399     *
400     * @param keyCode  check key code.
401     * @return {@code true} if ten-key code; {@code false} not ten-key code.
402     */
403    protected boolean isTenKeyCode(int keyCode) {
404        boolean result = false;
405        switch (keyCode) {
406        case KeyEvent.KEYCODE_NUMPAD_0:
407        case KeyEvent.KEYCODE_NUMPAD_1:
408        case KeyEvent.KEYCODE_NUMPAD_2:
409        case KeyEvent.KEYCODE_NUMPAD_3:
410        case KeyEvent.KEYCODE_NUMPAD_4:
411        case KeyEvent.KEYCODE_NUMPAD_5:
412        case KeyEvent.KEYCODE_NUMPAD_6:
413        case KeyEvent.KEYCODE_NUMPAD_7:
414        case KeyEvent.KEYCODE_NUMPAD_8:
415        case KeyEvent.KEYCODE_NUMPAD_9:
416        case KeyEvent.KEYCODE_NUMPAD_DOT:
417            result = true;
418            break;
419
420        default:
421            break;
422
423        }
424        return result;
425    }
426}
427