| // Copyright 2010-2014, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| package org.mozc.android.inputmethod.japanese; |
| |
| import org.mozc.android.inputmethod.japanese.FeedbackManager.FeedbackEvent; |
| import org.mozc.android.inputmethod.japanese.JapaneseKeyboard.KeyboardSpecification; |
| import org.mozc.android.inputmethod.japanese.KeycodeConverter.KeyEventInterface; |
| import org.mozc.android.inputmethod.japanese.emoji.EmojiProviderType; |
| import org.mozc.android.inputmethod.japanese.emoji.EmojiUtil; |
| import org.mozc.android.inputmethod.japanese.keyboard.KeyEntity; |
| import org.mozc.android.inputmethod.japanese.keyboard.KeyEventHandler; |
| import org.mozc.android.inputmethod.japanese.keyboard.KeyboardActionListener; |
| import org.mozc.android.inputmethod.japanese.keyboard.ProbableKeyEventGuesser; |
| import org.mozc.android.inputmethod.japanese.model.JapaneseSoftwareKeyboardModel; |
| import org.mozc.android.inputmethod.japanese.model.JapaneseSoftwareKeyboardModel.KeyboardMode; |
| import org.mozc.android.inputmethod.japanese.model.SymbolCandidateStorage; |
| import org.mozc.android.inputmethod.japanese.model.SymbolCandidateStorage.SymbolHistoryStorage; |
| import org.mozc.android.inputmethod.japanese.preference.ClientSidePreference.InputStyle; |
| import org.mozc.android.inputmethod.japanese.preference.ClientSidePreference.KeyboardLayout; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.Command; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.CompositionMode; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoCommands.Input.TouchEvent; |
| import org.mozc.android.inputmethod.japanese.resources.R; |
| import org.mozc.android.inputmethod.japanese.ui.MenuDialog; |
| import org.mozc.android.inputmethod.japanese.ui.MenuDialog.MenuDialogListener; |
| import org.mozc.android.inputmethod.japanese.util.ImeSwitcherFactory.ImeSwitcher; |
| import org.mozc.android.inputmethod.japanese.view.SkinType; |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.base.Optional; |
| import com.google.common.base.Preconditions; |
| |
| import android.content.Context; |
| import android.content.res.Configuration; |
| import android.content.res.Resources; |
| import android.graphics.Rect; |
| import android.inputmethodservice.InputMethodService; |
| import android.os.Build; |
| import android.os.IBinder; |
| import android.os.Looper; |
| import android.view.KeyEvent; |
| import android.view.LayoutInflater; |
| import android.view.MotionEvent; |
| import android.view.View; |
| import android.view.View.OnClickListener; |
| import android.view.ViewGroup; |
| import android.view.Window; |
| import android.view.inputmethod.EditorInfo; |
| |
| import java.util.Collections; |
| import java.util.List; |
| |
| import javax.annotation.Nullable; |
| |
| /** |
| * Manages Input, Candidate and Extracted views. |
| * |
| */ |
| public class ViewManager implements ViewManagerInterface { |
| |
| /** |
| * An small wrapper to inject keyboard view resizing when a user selects a candidate. |
| */ |
| class ViewManagerEventListener extends ViewEventDelegator { |
| ViewManagerEventListener(ViewEventListener delegated) { |
| super(delegated); |
| } |
| |
| @Override |
| public void onConversionCandidateSelected(int candidateId, Optional<Integer> rowIndex) { |
| // Restore the keyboard frame if hidden. |
| if (mozcView != null) { |
| mozcView.resetKeyboardFrameVisibility(); |
| } |
| super.onConversionCandidateSelected(candidateId, rowIndex); |
| } |
| } |
| |
| /** |
| * Converts S/W Keyboard's keycode to KeyEvent instance. |
| * Exposed as protected for testing purpose. |
| */ |
| protected void onKey(int primaryCode, List<? extends TouchEvent> touchEventList) { |
| if (primaryCode == keycodeCapslock || |
| primaryCode == keycodeAlt) { |
| // Ignore those key events because they are handled by KeyboardView, |
| // but send touchEventList for logging usage stats. |
| if (eventListener != null) { |
| eventListener.onKeyEvent(null, null, null, touchEventList); |
| } |
| return; |
| } |
| |
| // Keyboard switch event. |
| if (primaryCode == keycodeChartypeToKana || |
| primaryCode == keycodeChartypeTo123 || |
| primaryCode == keycodeChartypeToAbc || |
| primaryCode == keycodeChartypeToKana123 || |
| primaryCode == keycodeChartypeToAbc123) { |
| if (primaryCode == keycodeChartypeToKana) { |
| japaneseSoftwareKeyboardModel.setKeyboardMode(KeyboardMode.KANA); |
| } else if (primaryCode == keycodeChartypeToAbc) { |
| japaneseSoftwareKeyboardModel.setKeyboardMode(KeyboardMode.ALPHABET); |
| } else if (primaryCode == keycodeChartypeTo123 || |
| primaryCode == keycodeChartypeToKana123) { |
| japaneseSoftwareKeyboardModel.setKeyboardMode(KeyboardMode.KANA_NUMBER); |
| } else if (primaryCode == keycodeChartypeToAbc123) { |
| japaneseSoftwareKeyboardModel.setKeyboardMode(KeyboardMode.ALPHABET_NUMBER); |
| } |
| setJapaneseKeyboard( |
| japaneseSoftwareKeyboardModel.getKeyboardSpecification(), touchEventList); |
| return; |
| } |
| |
| if (primaryCode == keycodeMenuDialog || primaryCode == keycodeImePickerDialog) { |
| // We need to reset the keyboard, otherwise it would miss the ACTION_UP event. |
| if (mozcView != null) { |
| mozcView.resetKeyboardViewState(); |
| } |
| if (eventListener != null) { |
| eventListener.onShowMenuDialog(touchEventList); |
| } |
| if (primaryCode == keycodeMenuDialog) { |
| showMenuDialog(); |
| } else if (primaryCode == keycodeImePickerDialog) { |
| showImePickerDialog(); |
| } |
| return; |
| } |
| |
| if (primaryCode == keycodeSymbol) { |
| if (eventListener != null) { |
| eventListener.onSubmitPreedit(); |
| } |
| if (mozcView != null) { |
| mozcView.resetKeyboardViewState(); |
| mozcView.showSymbolInputView(); |
| if (eventListener != null) { |
| eventListener.onShowSymbolInputView(touchEventList); |
| } |
| } |
| return; |
| } |
| |
| if (primaryCode == keycodeUndo) { |
| if (eventListener != null) { |
| eventListener.onUndo(touchEventList); |
| } |
| return; |
| } |
| |
| ProtoCommands.KeyEvent mozcKeyEvent = |
| primaryKeyCodeConverter.createMozcKeyEvent(primaryCode, touchEventList); |
| if (eventListener != null) { |
| eventListener.onKeyEvent(mozcKeyEvent, |
| primaryKeyCodeConverter.getPrimaryCodeKeyEvent(primaryCode), |
| japaneseSoftwareKeyboardModel.getKeyboardSpecification(), |
| touchEventList); |
| } |
| } |
| |
| /** |
| * A simple KeyboardActionListener implementation which just delegates onKey event to |
| * ViewManager's onKey method. |
| */ |
| class KeyboardActionAdapter implements KeyboardActionListener { |
| @Override |
| public void onCancel() { |
| } |
| |
| @Override |
| public void onKey(int primaryCode, List<? extends TouchEvent> touchEventList) { |
| ViewManager.this.onKey(primaryCode, touchEventList); |
| } |
| |
| @Override |
| public void onPress(int primaryCode) { |
| if (eventListener != null && primaryCode != KeyEntity.INVALID_KEY_CODE) { |
| eventListener.onFireFeedbackEvent(FeedbackEvent.KEY_DOWN); |
| } |
| } |
| |
| @Override |
| public void onRelease(int primaryCode) { |
| } |
| } |
| |
| // Registered by the user (typically MozcService) |
| @VisibleForTesting final ViewEventListener eventListener; |
| |
| // The view of the MechaMozc. |
| @VisibleForTesting MozcView mozcView; |
| |
| // Menu dialog and its listener. |
| private final MenuDialogListener menuDialogListener; |
| @VisibleForTesting MenuDialog menuDialog = null; |
| |
| // IME switcher instance to detect that voice input is available or not. |
| private final ImeSwitcher imeSwitcher; |
| |
| // Called back by keyboards. |
| private final KeyEventHandler keyEventHandler; |
| |
| // Model to represent the current software keyboard state. |
| @VisibleForTesting final JapaneseSoftwareKeyboardModel japaneseSoftwareKeyboardModel = |
| new JapaneseSoftwareKeyboardModel(); |
| |
| // The factory of parsed keyboard data. |
| private final JapaneseKeyboardFactory japaneseKeyboardFactory = new JapaneseKeyboardFactory(); |
| |
| private final SymbolCandidateStorage symbolCandidateStorage; |
| |
| // Current fullscreen mode |
| private boolean fullscreenMode = false; |
| |
| // Current narrow mode |
| private boolean narrowMode = false; |
| |
| // Current popup enabled state. |
| private boolean popupEnabled = true; |
| |
| // Current voice input allowed state. |
| private boolean voiceInputAllowed = false; |
| |
| private int flickSensitivity = 0; |
| |
| private CompositionMode hardwareCompositionMode = CompositionMode.HIRAGANA; |
| |
| @VisibleForTesting EmojiProviderType emojiProviderType = EmojiProviderType.NONE; |
| |
| /** Current skin type. */ |
| private SkinType skinType = SkinType.ORANGE_LIGHTGRAY; |
| |
| private LayoutAdjustment layoutAdjustment = LayoutAdjustment.FILL; |
| |
| /** Percentage of keyboard height */ |
| private int keyboardHeightRatio = 100; |
| |
| // Keycodes defined in resource files. |
| // Printable keys are not defined. Refer them using character literal. |
| // These are "constant values" as a matter of practice, |
| // but such name like "KEYCODE_LEFT" makes Lint unhappy |
| // because they are not "static final". |
| private final int keycodeChartypeToKana; |
| private final int keycodeChartypeTo123; |
| private final int keycodeChartypeToAbc; |
| private final int keycodeChartypeToKana123; |
| private final int keycodeChartypeToAbc123; |
| private final int keycodeSymbol; |
| private final int keycodeUndo; |
| private final int keycodeCapslock; |
| private final int keycodeAlt; |
| private final int keycodeMenuDialog; |
| private final int keycodeImePickerDialog; |
| |
| // Handles software keyboard event and sends it to the service. |
| private final KeyboardActionAdapter keyboardActionListener; |
| |
| private final PrimaryKeyCodeConverter primaryKeyCodeConverter; |
| |
| public ViewManager(Context context, final ViewEventListener listener, |
| SymbolHistoryStorage symbolHistoryStorage, ImeSwitcher imeSwitcher, |
| MenuDialogListener menuDialogListener) { |
| this(context, listener, symbolHistoryStorage, imeSwitcher, menuDialogListener, |
| new ProbableKeyEventGuesser(context.getAssets())); |
| } |
| |
| @VisibleForTesting |
| ViewManager(Context context, ViewEventListener listener, |
| SymbolHistoryStorage symbolHistoryStorage, ImeSwitcher imeSwitcher, |
| @Nullable MenuDialogListener menuDialogListener, ProbableKeyEventGuesser guesser) { |
| Preconditions.checkNotNull(context); |
| Preconditions.checkNotNull(listener); |
| Preconditions.checkNotNull(imeSwitcher); |
| |
| primaryKeyCodeConverter = new PrimaryKeyCodeConverter(context, guesser); |
| |
| // Prefetch keycodes from resource |
| Resources res = context.getResources(); |
| keycodeChartypeToKana = res.getInteger(R.integer.key_chartype_to_kana); |
| keycodeChartypeTo123 = res.getInteger(R.integer.key_chartype_to_123); |
| keycodeChartypeToAbc = res.getInteger(R.integer.key_chartype_to_abc); |
| keycodeChartypeToKana123 = res.getInteger(R.integer.key_chartype_to_kana_123); |
| keycodeChartypeToAbc123 = res.getInteger(R.integer.key_chartype_to_abc_123); |
| keycodeSymbol = res.getInteger(R.integer.key_symbol); |
| keycodeUndo = res.getInteger(R.integer.key_undo); |
| keycodeCapslock = res.getInteger(R.integer.key_capslock); |
| keycodeAlt = res.getInteger(R.integer.key_alt); |
| keycodeMenuDialog = res.getInteger(R.integer.key_menu_dialog); |
| keycodeImePickerDialog = res.getInteger(R.integer.key_ime_picker_dialog); |
| |
| // Inject some logics into the listener. |
| eventListener = new ViewManagerEventListener(listener); |
| keyboardActionListener = new KeyboardActionAdapter(); |
| // Prepare callback object. |
| keyEventHandler = new KeyEventHandler( |
| Looper.getMainLooper(), |
| keyboardActionListener, |
| res.getInteger(R.integer.config_repeat_key_delay), |
| res.getInteger(R.integer.config_repeat_key_interval), |
| res.getInteger(R.integer.config_long_press_key_delay)); |
| |
| this.imeSwitcher = imeSwitcher; |
| this.menuDialogListener = menuDialogListener; |
| this.symbolCandidateStorage = new SymbolCandidateStorage(symbolHistoryStorage); |
| } |
| |
| /** |
| * Creates new input view. |
| * |
| * "Input view" is a software keyboard in almost all cases. |
| * |
| * Previously created input view is not accessed any more after calling this method. |
| * |
| * @param context |
| * @return newly created view. |
| */ |
| @Override |
| public MozcView createMozcView(Context context) { |
| // Because an issue about native bitmap memory management on older Android, |
| // there is a potential OutOfMemoryError. To reduce such an error case, |
| // we retry to inflate or to create drawable when OOM is found. |
| // Here is the injecting point of the procedure. |
| LayoutInflater inflater = LayoutInflater.from(context); |
| inflater = inflater.cloneInContext(MozcUtil.getContextWithOutOfMemoryRetrial(context)); |
| mozcView = MozcUtil.inflateWithOutOfMemoryRetrial( |
| MozcView.class, inflater, R.layout.mozc_view, Optional.<ViewGroup>absent(), false); |
| // Suppress update of View's internal state |
| // until all the updates done in this method are finished. Just in case. |
| mozcView.setVisibility(View.GONE); |
| mozcView.setKeyboardHeightRatio(keyboardHeightRatio); |
| mozcView.setEventListener( |
| eventListener, |
| new OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| setNarrowMode(!narrowMode); |
| } |
| }, |
| // User pushes these buttons to move position in order to see hidden text in editing rather |
| // than to change his/her favorite position. So we should not apply it to preferences. |
| new OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| setLayoutAdjustment(v.getContext().getResources(), LayoutAdjustment.LEFT); |
| mozcView.startLayoutAdjustmentAnimation(); |
| } |
| }, |
| new OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| setLayoutAdjustment(v.getContext().getResources(), LayoutAdjustment.RIGHT); |
| mozcView.startLayoutAdjustmentAnimation(); |
| } |
| }); |
| |
| mozcView.setKeyEventHandler(keyEventHandler); |
| |
| setJapaneseKeyboard(japaneseSoftwareKeyboardModel.getKeyboardSpecification(), |
| Collections.<TouchEvent>emptyList()); |
| mozcView.setFullscreenMode(fullscreenMode); |
| mozcView.setLayoutAdjustmentAndNarrowMode(layoutAdjustment, narrowMode); |
| // At the moment, it is necessary to set the storage to the view, *before* setting emoji |
| // provider type. |
| // TODO(hidehiko): Remove the restriction. |
| mozcView.setSymbolCandidateStorage(symbolCandidateStorage); |
| mozcView.setEmojiProviderType(emojiProviderType); |
| mozcView.setHardwareCompositionButtonImage(hardwareCompositionMode); |
| mozcView.setPopupEnabled(popupEnabled); |
| mozcView.setFlickSensitivity(flickSensitivity); |
| mozcView.setSkinType(skinType); |
| |
| // Clear the menu dialog. |
| menuDialog = null; |
| |
| reset(); |
| |
| mozcView.setVisibility(View.VISIBLE); |
| return mozcView; |
| } |
| |
| private void showMenuDialog() { |
| if (mozcView == null) { |
| MozcLog.w("mozcView is not initialized."); |
| return; |
| } |
| |
| boolean voiceInputEnabled = voiceInputAllowed && imeSwitcher.isVoiceImeAvailable(); |
| menuDialog = new MenuDialog( |
| mozcView.getContext(), Optional.fromNullable(menuDialogListener), voiceInputEnabled); |
| IBinder windowToken = mozcView.getWindowToken(); |
| if (windowToken == null) { |
| MozcLog.w("Unknown window token"); |
| } else { |
| menuDialog.setWindowToken(windowToken); |
| } |
| menuDialog.show(); |
| } |
| |
| private void showImePickerDialog() { |
| if (mozcView == null) { |
| MozcLog.w("mozcView is not initialized."); |
| return; |
| } |
| if (!MozcUtil.requestShowInputMethodPicker(mozcView.getContext())) { |
| MozcLog.e("Failed to send message to launch the input method picker dialog."); |
| } |
| } |
| |
| private void maybeDismissMenuDialog() { |
| MenuDialog menuDialog = this.menuDialog; |
| if (menuDialog != null) { |
| menuDialog.dismiss(); |
| } |
| } |
| |
| /** |
| * Renders views which this instance own based on Command.Output. |
| * |
| * Note that showing/hiding views is Service's responsibility. |
| */ |
| @Override |
| public void render(Command outCommand) { |
| if (outCommand == null) { |
| return; |
| } |
| if (mozcView == null) { |
| return; |
| } |
| if (outCommand.getOutput().getAllCandidateWords().getCandidatesCount() == 0 && |
| !outCommand.getInput().getRequestSuggestion()) { |
| // The server doesn't return the suggestion result, because there is following |
| // key sequence, which will trigger the suggest and the new suggestion will overwrite |
| // the current suggest. In order to avoid chattering the candidate window, |
| // we skip the following rendering. |
| return; |
| } |
| |
| mozcView.setCommand(outCommand); |
| if (outCommand.getOutput().getAllCandidateWords().getCandidatesCount() == 0) { |
| // If the candidate is empty (i.e. the CandidateView will go to GONE), |
| // reset the keyboard so that a user can type keyboard. |
| mozcView.resetKeyboardFrameVisibility(); |
| } |
| } |
| |
| /** |
| * @return the current keyboard specification. |
| */ |
| @Override |
| public KeyboardSpecification getJapaneseKeyboardSpecification() { |
| return japaneseSoftwareKeyboardModel.getKeyboardSpecification(); |
| } |
| |
| /** |
| * Set {@code EditorInfo} instance to the current view. |
| */ |
| @Override |
| public void setEditorInfo(EditorInfo attribute) { |
| mozcView.setEmojiEnabled( |
| EmojiUtil.isUnicodeEmojiAvailable(Build.VERSION.SDK_INT), |
| EmojiUtil.isCarrierEmojiAllowed(attribute)); |
| mozcView.setPasswordField(MozcUtil.isPasswordField(attribute)); |
| mozcView.setEditorInfo(attribute); |
| voiceInputAllowed = MozcUtil.isVoiceInputAllowed(attribute); |
| |
| japaneseSoftwareKeyboardModel.setInputType(attribute.inputType); |
| setJapaneseKeyboard( |
| japaneseSoftwareKeyboardModel.getKeyboardSpecification(), |
| Collections.<TouchEvent>emptyList()); |
| } |
| |
| @Override |
| public void setTextForActionButton(CharSequence text) { |
| // TODO(mozc-team): Implement action button handling. |
| } |
| |
| @Override |
| public boolean hideSubInputView() { |
| if (mozcView == null) { |
| return false; |
| } |
| MozcView mozcView = this.mozcView; |
| |
| // Try to hide a sub view from front to back. |
| if (mozcView.hideSymbolInputView()) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Creates and sets a keyboard represented by the resource id to the input frame. |
| * |
| * Note that this method requires inputFrameView is not null, and its first child is |
| * the JapaneseKeyboardView. |
| * @param specification Keyboard specification for the next |
| */ |
| private void setJapaneseKeyboard( |
| KeyboardSpecification specification, List<? extends TouchEvent> touchEventList) { |
| eventListener.onKeyEvent(null, null, specification, touchEventList); |
| if (mozcView != null) { |
| Rect size = mozcView.getKeyboardSize(); |
| JapaneseKeyboard japaneseKeyboard = |
| japaneseKeyboardFactory.get(mozcView.getResources(), specification, |
| size.width(), size.height()); |
| mozcView.setJapaneseKeyboard(japaneseKeyboard); |
| primaryKeyCodeConverter.setJapaneseKeyboard(japaneseKeyboard); |
| } |
| } |
| |
| /** |
| * Set this keyboard layout to the specified one. |
| * @param keyboardLayout New keyboard layout. |
| * @throws NullPointerException If <code>keyboardLayout</code> is <code>null</code>. |
| */ |
| @Override |
| public void setKeyboardLayout(KeyboardLayout keyboardLayout) { |
| Preconditions.checkNotNull(keyboardLayout); |
| |
| if (japaneseSoftwareKeyboardModel.getKeyboardLayout() != keyboardLayout) { |
| // If changed, clear the keyboard cache. |
| japaneseKeyboardFactory.clear(); |
| } |
| |
| japaneseSoftwareKeyboardModel.setKeyboardLayout(keyboardLayout); |
| setJapaneseKeyboard( |
| japaneseSoftwareKeyboardModel.getKeyboardSpecification(), |
| Collections.<TouchEvent>emptyList()); |
| } |
| |
| /** |
| * Set the input style. |
| * @param inputStyle new input style. |
| * @throws NullPointerException If <code>inputStyle</code> is <code>null</code>. |
| * TODO(hidehiko): Refactor out following keyboard switching logic into another class. |
| */ |
| @Override |
| public void setInputStyle(InputStyle inputStyle) { |
| Preconditions.checkNotNull(inputStyle); |
| |
| if (japaneseSoftwareKeyboardModel.getInputStyle() != inputStyle) { |
| // If changed, clear the keyboard cache. |
| japaneseKeyboardFactory.clear(); |
| } |
| |
| japaneseSoftwareKeyboardModel.setInputStyle(inputStyle); |
| setJapaneseKeyboard( |
| japaneseSoftwareKeyboardModel.getKeyboardSpecification(), |
| Collections.<TouchEvent>emptyList()); |
| } |
| |
| @Override |
| public void setQwertyLayoutForAlphabet(boolean qwertyLayoutForAlphabet) { |
| if (japaneseSoftwareKeyboardModel.isQwertyLayoutForAlphabet() != qwertyLayoutForAlphabet) { |
| // If changed, clear the keyboard cache. |
| japaneseKeyboardFactory.clear(); |
| } |
| |
| japaneseSoftwareKeyboardModel.setQwertyLayoutForAlphabet(qwertyLayoutForAlphabet); |
| setJapaneseKeyboard( |
| japaneseSoftwareKeyboardModel.getKeyboardSpecification(), |
| Collections.<TouchEvent>emptyList()); |
| } |
| |
| @Override |
| public void setFullscreenMode(boolean fullscreenMode) { |
| this.fullscreenMode = fullscreenMode; |
| if (mozcView != null) { |
| mozcView.setFullscreenMode(fullscreenMode); |
| } |
| } |
| |
| @Override |
| public boolean isFullscreenMode() { |
| return fullscreenMode; |
| } |
| |
| @Override |
| public void setFlickSensitivity(int flickSensitivity) { |
| this.flickSensitivity = flickSensitivity; |
| if (mozcView != null) { |
| mozcView.setFlickSensitivity(flickSensitivity); |
| } |
| } |
| |
| @Override |
| public void setEmojiProviderType(EmojiProviderType emojiProviderType) { |
| Preconditions.checkNotNull(emojiProviderType); |
| |
| this.emojiProviderType = emojiProviderType; |
| if (mozcView != null) { |
| mozcView.setEmojiProviderType(emojiProviderType); |
| } |
| } |
| |
| /** |
| * @param isNarrowMode Whether mozc view shows in narrow mode or normal. |
| */ |
| @Override |
| public void setNarrowMode(boolean isNarrowMode) { |
| this.narrowMode = isNarrowMode; |
| if (mozcView != null) { |
| mozcView.setLayoutAdjustmentAndNarrowMode(layoutAdjustment, isNarrowMode); |
| } |
| } |
| |
| /** |
| * Returns true if we should transit to narrow mode, |
| * based on returned {@code Command} and {@code KeyEventInterface} from the server. |
| * |
| * <p>If all of the following conditions are satisfied, narrow mode is shown. |
| * <ul> |
| * <li>The key event is from h/w keyboard. |
| * <li>The key event has printable character without modifier. |
| * </ul> |
| */ |
| @Override |
| public void maybeTransitToNarrowMode(Command command, KeyEventInterface keyEventInterface) { |
| Preconditions.checkNotNull(command); |
| // Surely we don't anthing when on narrow mode already. |
| if (isNarrowMode()) { |
| return; |
| } |
| // Do nothing for the input from software keyboard. |
| if (keyEventInterface == null || keyEventInterface.getNativeEvent() == null) { |
| return; |
| } |
| // Do nothing if the key event doesn't have printable character without modifier. |
| if (!command.getInput().hasKey() |
| || !command.getInput().getKey().hasKeyCode() |
| || command.getInput().getKey().hasModifiers()) { |
| return; |
| } |
| // Passed all the check. Transit to narrow mode. |
| hideSubInputView(); |
| setNarrowMode(true); |
| } |
| |
| @Override |
| public boolean isNarrowMode() { |
| return narrowMode; |
| } |
| |
| @Override |
| public void setPopupEnabled(boolean popupEnabled) { |
| this.popupEnabled = popupEnabled; |
| if (mozcView != null) { |
| mozcView.setPopupEnabled(popupEnabled); |
| } |
| } |
| |
| @Override |
| public void setHardwareKeyboardCompositionMode(CompositionMode compositionMode) { |
| hardwareCompositionMode = compositionMode; |
| if (mozcView != null) { |
| mozcView.setHardwareCompositionButtonImage(compositionMode); |
| } |
| } |
| |
| @Override |
| public void setSkinType(SkinType skinType) { |
| this.skinType = skinType; |
| if (mozcView != null) { |
| mozcView.setSkinType(skinType); |
| } |
| } |
| |
| @Override |
| public void setLayoutAdjustment(Resources resources, LayoutAdjustment layoutAdjustment) { |
| this.layoutAdjustment = layoutAdjustment; |
| |
| if (mozcView != null) { |
| mozcView.setLayoutAdjustmentAndNarrowMode(layoutAdjustment, narrowMode); |
| } |
| } |
| |
| @Override |
| public void setKeyboardHeightRatio(int keyboardHeightRatio) { |
| this.keyboardHeightRatio = keyboardHeightRatio; |
| if (mozcView != null) { |
| mozcView.setKeyboardHeightRatio(keyboardHeightRatio); |
| } |
| } |
| |
| /** |
| * Reset the status of the current input view. |
| * |
| * This method must be called when the IME is turned on. |
| * Note that this method can be called before {@link #createMozcView(Context)} |
| * so null-check is mandatory. |
| */ |
| @Override |
| public void reset() { |
| if (mozcView != null) { |
| mozcView.reset(); |
| } |
| |
| // Reset menu dialog. |
| maybeDismissMenuDialog(); |
| } |
| |
| @Override |
| public void computeInsets(Context context, InputMethodService.Insets outInsets, Window window) { |
| // The IME's area is prioritized than app's. |
| // - contentTopInsets |
| // - This is the top part of the UI that is the main content. |
| // - This affects window layout. |
| // So if this value is changed, resizing the application behind happens. |
| // - This value is relative to the top edge of the input method window. |
| // - visibleTopInsets |
| // - This is the top part of the UI that is visibly covering the application behind it. |
| // Changing this value will not cause resizing the application. |
| // - This is *not* to clip IME's drawing area. |
| // - This value is relative to the top edge of the input method window. |
| // Thus it seems that we have to guarantee contentTopInsets <= visibleTopInsets. |
| // If contentTopInsets < visibleTopInsets, the app's UI is drawn on IME's area |
| // but almost all (or completely all?) application does not draw anything |
| // on such "outside" area from the app's window. |
| // Conclusion is that we should guarantee contentTopInsets == visibleTopInsets. |
| // |
| // On Honeycomb or later version, we cannot take touch events outside of IME window. |
| // As its workaround, we cover the almost screen by transparent view, and we need to consider |
| // the gap between the top of user visible IME window, and the transparent view's top here. |
| // Note that touch events for the transparent view will be ignored by IME and automatically |
| // sent to the application if it is not for the IME. |
| |
| View contentView = window.findViewById(Window.ID_ANDROID_CONTENT); |
| int contentViewWidth = contentView.getWidth(); |
| int contentViewHeight = contentView.getHeight(); |
| |
| if (mozcView == null) { |
| outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_CONTENT; |
| outInsets.contentTopInsets = contentViewHeight |
| - context.getResources().getDimensionPixelSize(R.dimen.input_frame_height); |
| outInsets.visibleTopInsets = outInsets.contentTopInsets; |
| return; |
| } |
| |
| mozcView.setInsets(contentViewWidth, contentViewHeight, outInsets); |
| } |
| |
| @Override |
| public void onConfigurationChanged(Configuration newConfig) { |
| primaryKeyCodeConverter.setConfiguration(newConfig); |
| } |
| |
| @Override |
| public boolean isKeyConsumedOnViewAsynchronously(KeyEvent event) { |
| return false; |
| } |
| |
| @Override |
| public void consumeKeyOnViewSynchronously(KeyEvent event) { |
| throw new IllegalArgumentException("ViewManager doesn't consume any key event."); |
| } |
| |
| @Override |
| public boolean isGenericMotionToConsume(MotionEvent event) { |
| return false; |
| } |
| |
| @Override |
| public boolean consumeGenericMotion(MotionEvent event) { |
| return false; |
| } |
| |
| @VisibleForTesting |
| @Override |
| public ViewEventListener getEventListener() { |
| return eventListener; |
| } |
| |
| @VisibleForTesting |
| @Override |
| public JapaneseSoftwareKeyboardModel getJapaneseSoftwareKeyboardModel() { |
| return japaneseSoftwareKeyboardModel; |
| } |
| |
| @VisibleForTesting |
| @Override |
| public boolean isPopupEnabled() { |
| return popupEnabled; |
| } |
| |
| @VisibleForTesting |
| @Override |
| public int getFlickSensitivity() { |
| return flickSensitivity; |
| } |
| |
| @VisibleForTesting |
| @Override |
| public EmojiProviderType getEmojiProviderType() { |
| return emojiProviderType; |
| } |
| |
| @VisibleForTesting |
| @Override |
| public SkinType getSkinType() { |
| return skinType; |
| } |
| |
| @VisibleForTesting |
| @Override |
| public LayoutAdjustment getLayoutAdjustment() { |
| return layoutAdjustment; |
| } |
| |
| @VisibleForTesting |
| @Override |
| public int getKeyboardHeightRatio() { |
| return keyboardHeightRatio; |
| } |
| |
| @Override |
| public void trimMemory() { |
| if (mozcView != null) { |
| mozcView.trimMemory(); |
| } |
| } |
| |
| @Override |
| public KeyboardActionListener getKeyboardActionListener() { |
| return keyboardActionListener; |
| } |
| } |