Fast key preview
Previously the key preview was implemented by PopupWindow. The key preview popup window would be dismissed and shown each time the key was released and pressed. It turned out that it was taking several milliseconds. This change implements the key preview by a simple TextView which will be layout-ed at absolute coordinates within the screen FrameLayout. And dismissing and showing the key preview is implemented by the TextView's visibility. This implementation needs careful coordination of candidates.xml layout and LatinIME.onComputeInsets to control suggestion strip visibility. Bug: 4179964 Change-Id: Id6347cb35b91eb14ab20dd2f312e58b54b6625a7main
parent
47d2ef69d3
commit
7a3d3ae17f
|
@ -1,29 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
/*
|
|
||||||
**
|
|
||||||
** Copyright 2010, 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.
|
|
||||||
*/
|
|
||||||
-->
|
|
||||||
|
|
||||||
<set
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:interpolator="@android:anim/decelerate_interpolator"
|
|
||||||
>
|
|
||||||
<alpha
|
|
||||||
android:fromAlpha="0.5"
|
|
||||||
android:toAlpha="1.0"
|
|
||||||
android:duration="@integer/config_preview_fadein_anim_time" />
|
|
||||||
</set>
|
|
|
@ -1,29 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
/*
|
|
||||||
**
|
|
||||||
** Copyright 2010, 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.
|
|
||||||
*/
|
|
||||||
-->
|
|
||||||
|
|
||||||
<set
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:interpolator="@android:anim/accelerate_interpolator"
|
|
||||||
>
|
|
||||||
<alpha
|
|
||||||
android:fromAlpha="1.0"
|
|
||||||
android:toAlpha="0.0"
|
|
||||||
android:duration="@integer/config_preview_fadeout_anim_time" />
|
|
||||||
</set>
|
|
|
@ -25,5 +25,5 @@
|
||||||
<alpha
|
<alpha
|
||||||
android:fromAlpha="0.5"
|
android:fromAlpha="0.5"
|
||||||
android:toAlpha="1.0"
|
android:toAlpha="1.0"
|
||||||
android:duration="@integer/config_preview_fadein_anim_time" />
|
android:duration="@integer/config_mini_keyboard_fadein_anim_time" />
|
||||||
</set>
|
</set>
|
||||||
|
|
|
@ -25,5 +25,5 @@
|
||||||
<alpha
|
<alpha
|
||||||
android:fromAlpha="1.0"
|
android:fromAlpha="1.0"
|
||||||
android:toAlpha="0.0"
|
android:toAlpha="0.0"
|
||||||
android:duration="@integer/config_preview_fadeout_anim_time" />
|
android:duration="@integer/config_mini_keyboard_fadeout_anim_time" />
|
||||||
</set>
|
</set>
|
||||||
|
|
|
@ -21,25 +21,33 @@
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
|
android:gravity="bottom"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="@dimen/candidate_strip_minimum_height"
|
||||||
|
>
|
||||||
|
<!-- On tablets, the candidate strip is centered with horizontal paddings on both sides because
|
||||||
|
width of the landscape mode is too long for the candidate strip. This LinearLayout is
|
||||||
|
required to hold the paddings. -->
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/candidate_strip_height"
|
||||||
android:background="@drawable/keyboard_suggest_strip_holo"
|
android:background="@drawable/keyboard_suggest_strip_holo"
|
||||||
android:paddingRight="@dimen/candidate_strip_padding"
|
android:paddingRight="@dimen/candidate_strip_padding"
|
||||||
android:paddingLeft="@dimen/candidate_strip_padding"
|
android:paddingLeft="@dimen/candidate_strip_padding"
|
||||||
>
|
>
|
||||||
<HorizontalScrollView
|
<HorizontalScrollView
|
||||||
android:id="@+id/candidates_scroll_view"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:fadingEdge="horizontal"
|
android:fadingEdge="horizontal"
|
||||||
android:fadingEdgeLength="@dimen/candidate_strip_fading_edge_length"
|
android:fadingEdgeLength="@dimen/candidate_strip_fading_edge_length"
|
||||||
android:scrollbars="none"
|
android:scrollbars="none"
|
||||||
>
|
>
|
||||||
<com.android.inputmethod.latin.CandidateView
|
<com.android.inputmethod.latin.CandidateView
|
||||||
android:id="@+id/candidates"
|
android:id="@+id/candidates"
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/candidate_strip_height"
|
android:layout_height="match_parent" />
|
||||||
android:background="@drawable/keyboard_suggest_strip_holo" />
|
|
||||||
</HorizontalScrollView>
|
</HorizontalScrollView>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
|
@ -21,25 +21,24 @@
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
|
android:gravity="bottom"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@drawable/keyboard_suggest_strip"
|
android:minHeight="@dimen/candidate_strip_minimum_height"
|
||||||
android:paddingRight="@dimen/candidate_strip_padding"
|
android:paddingRight="@dimen/candidate_strip_padding"
|
||||||
android:paddingLeft="@dimen/candidate_strip_padding"
|
android:paddingLeft="@dimen/candidate_strip_padding"
|
||||||
>
|
>
|
||||||
<HorizontalScrollView
|
<HorizontalScrollView
|
||||||
android:id="@+id/candidates_scroll_view"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="wrap_content"
|
android:layout_height="@dimen/candidate_strip_height"
|
||||||
android:layout_height="wrap_content"
|
android:background="@drawable/keyboard_suggest_strip"
|
||||||
android:fadingEdge="horizontal"
|
android:fadingEdge="horizontal"
|
||||||
android:fadingEdgeLength="@dimen/candidate_strip_fading_edge_length"
|
android:fadingEdgeLength="@dimen/candidate_strip_fading_edge_length"
|
||||||
android:scrollbars="none"
|
android:scrollbars="none"
|
||||||
>
|
>
|
||||||
<com.android.inputmethod.latin.CandidateView
|
<com.android.inputmethod.latin.CandidateView
|
||||||
android:id="@+id/candidates"
|
android:id="@+id/candidates"
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/candidate_strip_height"
|
android:layout_height="match_parent" />
|
||||||
android:background="@drawable/keyboard_suggest_strip" />
|
|
||||||
</HorizontalScrollView>
|
</HorizontalScrollView>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
@ -46,6 +46,9 @@
|
||||||
<dimen name="key_preview_offset_holo">8.0mm</dimen>
|
<dimen name="key_preview_offset_holo">8.0mm</dimen>
|
||||||
|
|
||||||
<dimen name="candidate_strip_height">46dip</dimen>
|
<dimen name="candidate_strip_height">46dip</dimen>
|
||||||
|
<!-- candidate_strip_minimum_height =
|
||||||
|
key_preview_height_holo - key_preview_offset_holo + alpha -->
|
||||||
|
<dimen name="candidate_strip_minimum_height">18mm</dimen>
|
||||||
<dimen name="candidate_strip_padding">15.0mm</dimen>
|
<dimen name="candidate_strip_padding">15.0mm</dimen>
|
||||||
<dimen name="candidate_min_width">0.3in</dimen>
|
<dimen name="candidate_min_width">0.3in</dimen>
|
||||||
<dimen name="candidate_padding">12dip</dimen>
|
<dimen name="candidate_padding">12dip</dimen>
|
||||||
|
|
|
@ -49,8 +49,6 @@
|
||||||
<integer name="config_final_fadeout_percentage_of_language_on_spacebar">15</integer>
|
<integer name="config_final_fadeout_percentage_of_language_on_spacebar">15</integer>
|
||||||
<integer name="config_delay_before_preview">0</integer>
|
<integer name="config_delay_before_preview">0</integer>
|
||||||
<integer name="config_delay_after_preview">10</integer>
|
<integer name="config_delay_after_preview">10</integer>
|
||||||
<integer name="config_preview_fadein_anim_time">0</integer>
|
|
||||||
<integer name="config_preview_fadeout_anim_time">70</integer>
|
|
||||||
<integer name="config_mini_keyboard_fadein_anim_time">0</integer>
|
<integer name="config_mini_keyboard_fadein_anim_time">0</integer>
|
||||||
<integer name="config_mini_keyboard_fadeout_anim_time">100</integer>
|
<integer name="config_mini_keyboard_fadeout_anim_time">100</integer>
|
||||||
<integer name="config_delay_before_key_repeat_start">400</integer>
|
<integer name="config_delay_before_key_repeat_start">400</integer>
|
||||||
|
|
|
@ -52,6 +52,9 @@
|
||||||
<dimen name="key_preview_offset_holo">0.193in</dimen>
|
<dimen name="key_preview_offset_holo">0.193in</dimen>
|
||||||
|
|
||||||
<dimen name="candidate_strip_height">42dip</dimen>
|
<dimen name="candidate_strip_height">42dip</dimen>
|
||||||
|
<!-- candidate_strip_minimum_height =
|
||||||
|
key_preview_height_holo - key_preview_offset_holo + alpha -->
|
||||||
|
<dimen name="candidate_strip_minimum_height">100sp</dimen>
|
||||||
<dimen name="candidate_strip_fading_edge_length">63dip</dimen>
|
<dimen name="candidate_strip_fading_edge_length">63dip</dimen>
|
||||||
<dimen name="candidate_strip_padding">0dip</dimen>
|
<dimen name="candidate_strip_padding">0dip</dimen>
|
||||||
<dimen name="candidate_min_width">0.3in</dimen>
|
<dimen name="candidate_min_width">0.3in</dimen>
|
||||||
|
|
|
@ -35,10 +35,6 @@
|
||||||
<item name="backgroundDimAmount">0.5</item>
|
<item name="backgroundDimAmount">0.5</item>
|
||||||
<item name="colorScheme">white</item>
|
<item name="colorScheme">white</item>
|
||||||
</style>
|
</style>
|
||||||
<style name="KeyPreviewAnimation">
|
|
||||||
<item name="android:windowEnterAnimation">@anim/key_preview_fadein</item>
|
|
||||||
<item name="android:windowExitAnimation">@anim/key_preview_fadeout</item>
|
|
||||||
</style>
|
|
||||||
<style name="MiniKeyboardAnimation">
|
<style name="MiniKeyboardAnimation">
|
||||||
<item name="android:windowEnterAnimation">@anim/mini_keyboard_fadein</item>
|
<item name="android:windowEnterAnimation">@anim/mini_keyboard_fadein</item>
|
||||||
<item name="android:windowExitAnimation">@anim/mini_keyboard_fadeout</item>
|
<item name="android:windowExitAnimation">@anim/mini_keyboard_fadeout</item>
|
||||||
|
|
|
@ -19,20 +19,9 @@ package com.android.inputmethod.compat;
|
||||||
import com.android.inputmethod.latin.SubtypeSwitcher;
|
import com.android.inputmethod.latin.SubtypeSwitcher;
|
||||||
|
|
||||||
import android.inputmethodservice.InputMethodService;
|
import android.inputmethodservice.InputMethodService;
|
||||||
import android.view.View;
|
|
||||||
import android.view.inputmethod.InputMethodSubtype;
|
import android.view.inputmethod.InputMethodSubtype;
|
||||||
import android.widget.HorizontalScrollView;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
public class InputMethodServiceCompatWrapper extends InputMethodService {
|
public class InputMethodServiceCompatWrapper extends InputMethodService {
|
||||||
private static final Method METHOD_HorizontalScrollView_setOverScrollMode =
|
|
||||||
CompatUtils.getMethod(HorizontalScrollView.class, "setOverScrollMode", int.class);
|
|
||||||
private static final Field FIELD_View_OVER_SCROLL_NEVER =
|
|
||||||
CompatUtils.getField(View.class, "OVER_SCROLL_NEVER");
|
|
||||||
private static final Integer View_OVER_SCROLL_NEVER =
|
|
||||||
(Integer)CompatUtils.getFieldValue(null, null, FIELD_View_OVER_SCROLL_NEVER);
|
|
||||||
// CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED needs to be false if the API level is 10
|
// CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED needs to be false if the API level is 10
|
||||||
// or previous. Note that InputMethodSubtype was added in the API level 11.
|
// or previous. Note that InputMethodSubtype was added in the API level 11.
|
||||||
// For the API level 11 or later, LatinIME should override onCurrentInputMethodSubtypeChanged().
|
// For the API level 11 or later, LatinIME should override onCurrentInputMethodSubtypeChanged().
|
||||||
|
@ -66,13 +55,6 @@ public class InputMethodServiceCompatWrapper extends InputMethodService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void setOverScrollModeNever(HorizontalScrollView scrollView) {
|
|
||||||
if (View_OVER_SCROLL_NEVER != null) {
|
|
||||||
CompatUtils.invoke(scrollView, null, METHOD_HorizontalScrollView_setOverScrollMode,
|
|
||||||
View_OVER_SCROLL_NEVER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
// Functions using API v11 or later //
|
// Functions using API v11 or later //
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
|
|
|
@ -45,8 +45,8 @@ import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup.LayoutParams;
|
import android.view.ViewGroup;
|
||||||
import android.view.WindowManager;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.PopupWindow;
|
import android.widget.PopupWindow;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
@ -110,14 +110,12 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
||||||
// Key preview popup
|
// Key preview popup
|
||||||
private boolean mInForeground;
|
private boolean mInForeground;
|
||||||
private TextView mPreviewText;
|
private TextView mPreviewText;
|
||||||
private PopupWindow mPreviewPopup;
|
|
||||||
private int mPreviewTextSizeLarge;
|
private int mPreviewTextSizeLarge;
|
||||||
private int[] mOffsetInWindow;
|
private final int[] mOffsetInWindow = new int[2];
|
||||||
private int mOldPreviewKeyIndex = KeyDetector.NOT_A_KEY;
|
private int mOldPreviewKeyIndex = KeyDetector.NOT_A_KEY;
|
||||||
private boolean mShowPreview = true;
|
private boolean mShowPreview = true;
|
||||||
private int mPopupPreviewOffsetX;
|
private int mPopupPreviewOffsetX;
|
||||||
private int mPopupPreviewOffsetY;
|
private int mPopupPreviewOffsetY;
|
||||||
private int mWindowY;
|
|
||||||
private int mPopupPreviewDisplayedY;
|
private int mPopupPreviewDisplayedY;
|
||||||
private final int mDelayBeforePreview;
|
private final int mDelayBeforePreview;
|
||||||
private final int mDelayAfterPreview;
|
private final int mDelayAfterPreview;
|
||||||
|
@ -125,7 +123,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
||||||
// Popup mini keyboard
|
// Popup mini keyboard
|
||||||
private PopupWindow mMiniKeyboardPopup;
|
private PopupWindow mMiniKeyboardPopup;
|
||||||
private KeyboardView mMiniKeyboardView;
|
private KeyboardView mMiniKeyboardView;
|
||||||
private View mMiniKeyboardParent;
|
|
||||||
private final WeakHashMap<Key, View> mMiniKeyboardCache = new WeakHashMap<Key, View>();
|
private final WeakHashMap<Key, View> mMiniKeyboardCache = new WeakHashMap<Key, View>();
|
||||||
private int mMiniKeyboardOriginX;
|
private int mMiniKeyboardOriginX;
|
||||||
private int mMiniKeyboardOriginY;
|
private int mMiniKeyboardOriginY;
|
||||||
|
@ -204,7 +201,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
||||||
showKey(msg.arg1, (PointerTracker)msg.obj);
|
showKey(msg.arg1, (PointerTracker)msg.obj);
|
||||||
break;
|
break;
|
||||||
case MSG_DISMISS_PREVIEW:
|
case MSG_DISMISS_PREVIEW:
|
||||||
mPreviewPopup.dismiss();
|
mPreviewText.setVisibility(View.INVISIBLE);
|
||||||
break;
|
break;
|
||||||
case MSG_REPEAT_KEY: {
|
case MSG_REPEAT_KEY: {
|
||||||
final PointerTracker tracker = (PointerTracker)msg.obj;
|
final PointerTracker tracker = (PointerTracker)msg.obj;
|
||||||
|
@ -227,12 +224,11 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
||||||
|
|
||||||
public void popupPreview(long delay, int keyIndex, PointerTracker tracker) {
|
public void popupPreview(long delay, int keyIndex, PointerTracker tracker) {
|
||||||
removeMessages(MSG_POPUP_PREVIEW);
|
removeMessages(MSG_POPUP_PREVIEW);
|
||||||
if (mPreviewPopup.isShowing() && mPreviewText.getVisibility() == VISIBLE) {
|
if (mPreviewText.getVisibility() == VISIBLE) {
|
||||||
// Show right away, if it's already visible and finger is moving around
|
// Show right away, if it's already visible and finger is moving around
|
||||||
showKey(keyIndex, tracker);
|
showKey(keyIndex, tracker);
|
||||||
} else {
|
} else {
|
||||||
sendMessageDelayed(obtainMessage(MSG_POPUP_PREVIEW, keyIndex, 0, tracker),
|
sendMessageDelayed(obtainMessage(MSG_POPUP_PREVIEW, keyIndex, 0, tracker), delay);
|
||||||
delay);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,10 +237,8 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dismissPreview(long delay) {
|
public void dismissPreview(long delay) {
|
||||||
if (mPreviewPopup.isShowing()) {
|
|
||||||
sendMessageDelayed(obtainMessage(MSG_DISMISS_PREVIEW), delay);
|
sendMessageDelayed(obtainMessage(MSG_DISMISS_PREVIEW), delay);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void cancelDismissPreview() {
|
public void cancelDismissPreview() {
|
||||||
removeMessages(MSG_DISMISS_PREVIEW);
|
removeMessages(MSG_DISMISS_PREVIEW);
|
||||||
|
@ -366,24 +360,17 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
||||||
|
|
||||||
final Resources res = getResources();
|
final Resources res = getResources();
|
||||||
|
|
||||||
mPreviewPopup = new PopupWindow(context);
|
|
||||||
if (previewLayout != 0) {
|
if (previewLayout != 0) {
|
||||||
mPreviewText = (TextView) LayoutInflater.from(context).inflate(previewLayout, null);
|
mPreviewText = (TextView) LayoutInflater.from(context).inflate(previewLayout, null);
|
||||||
mPreviewTextSizeLarge = (int) res.getDimension(R.dimen.key_preview_text_size_large);
|
mPreviewTextSizeLarge = (int) res.getDimension(R.dimen.key_preview_text_size_large);
|
||||||
mPreviewPopup.setContentView(mPreviewText);
|
|
||||||
mPreviewPopup.setBackgroundDrawable(null);
|
|
||||||
} else {
|
} else {
|
||||||
mShowPreview = false;
|
mShowPreview = false;
|
||||||
}
|
}
|
||||||
mPreviewPopup.setTouchable(false);
|
|
||||||
mPreviewPopup.setAnimationStyle(R.style.KeyPreviewAnimation);
|
|
||||||
mPreviewPopup.setClippingEnabled(false);
|
|
||||||
mDelayBeforePreview = res.getInteger(R.integer.config_delay_before_preview);
|
mDelayBeforePreview = res.getInteger(R.integer.config_delay_before_preview);
|
||||||
mDelayAfterPreview = res.getInteger(R.integer.config_delay_after_preview);
|
mDelayAfterPreview = res.getInteger(R.integer.config_delay_after_preview);
|
||||||
mKeyLabelHorizontalPadding = (int)res.getDimension(
|
mKeyLabelHorizontalPadding = (int)res.getDimension(
|
||||||
R.dimen.key_label_horizontal_alignment_padding);
|
R.dimen.key_label_horizontal_alignment_padding);
|
||||||
|
|
||||||
mMiniKeyboardParent = this;
|
|
||||||
mMiniKeyboardPopup = new PopupWindow(context);
|
mMiniKeyboardPopup = new PopupWindow(context);
|
||||||
mMiniKeyboardPopup.setBackgroundDrawable(null);
|
mMiniKeyboardPopup.setBackgroundDrawable(null);
|
||||||
mMiniKeyboardPopup.setAnimationStyle(R.style.MiniKeyboardAnimation);
|
mMiniKeyboardPopup.setAnimationStyle(R.style.MiniKeyboardAnimation);
|
||||||
|
@ -583,7 +570,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
||||||
public void setPopupOffset(int x, int y) {
|
public void setPopupOffset(int x, int y) {
|
||||||
mPopupPreviewOffsetX = x;
|
mPopupPreviewOffsetX = x;
|
||||||
mPopupPreviewOffsetY = y;
|
mPopupPreviewOffsetY = y;
|
||||||
mPreviewPopup.dismiss();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -915,8 +901,16 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Must fix popup preview on xlarge layout
|
// TODO: Introduce minimum duration for displaying key previews
|
||||||
|
// TODO: Display up to two key previews when the user presses two keys at the same time
|
||||||
private void showKey(final int keyIndex, PointerTracker tracker) {
|
private void showKey(final int keyIndex, PointerTracker tracker) {
|
||||||
|
// If the preview popup has no parent view yet, add it to the screen FrameLayout.
|
||||||
|
if (mPreviewText.getParent() == null) {
|
||||||
|
final FrameLayout screenContent = (FrameLayout) getRootView()
|
||||||
|
.findViewById(android.R.id.content);
|
||||||
|
screenContent.addView(mPreviewText, new FrameLayout.LayoutParams(0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
Key key = tracker.getKey(keyIndex);
|
Key key = tracker.getKey(keyIndex);
|
||||||
// If keyIndex is invalid or IME is already closed, we must not show key preview.
|
// If keyIndex is invalid or IME is already closed, we must not show key preview.
|
||||||
// Trying to show preview PopupWindow while root window is closed causes
|
// Trying to show preview PopupWindow while root window is closed causes
|
||||||
|
@ -948,7 +942,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
||||||
int popupWidth = Math.max(mPreviewText.getMeasuredWidth(), keyDrawWidth
|
int popupWidth = Math.max(mPreviewText.getMeasuredWidth(), keyDrawWidth
|
||||||
+ mPreviewText.getPaddingLeft() + mPreviewText.getPaddingRight());
|
+ mPreviewText.getPaddingLeft() + mPreviewText.getPaddingRight());
|
||||||
final int popupHeight = mPreviewHeight;
|
final int popupHeight = mPreviewHeight;
|
||||||
LayoutParams lp = mPreviewText.getLayoutParams();
|
final ViewGroup.LayoutParams lp = mPreviewText.getLayoutParams();
|
||||||
if (lp != null) {
|
if (lp != null) {
|
||||||
lp.width = popupWidth;
|
lp.width = popupWidth;
|
||||||
lp.height = popupHeight;
|
lp.height = popupHeight;
|
||||||
|
@ -958,47 +952,23 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
||||||
int popupPreviewY = key.mY - popupHeight + mPreviewOffset;
|
int popupPreviewY = key.mY - popupHeight + mPreviewOffset;
|
||||||
|
|
||||||
mHandler.cancelDismissPreview();
|
mHandler.cancelDismissPreview();
|
||||||
if (mOffsetInWindow == null) {
|
|
||||||
mOffsetInWindow = new int[2];
|
|
||||||
getLocationInWindow(mOffsetInWindow);
|
getLocationInWindow(mOffsetInWindow);
|
||||||
mOffsetInWindow[0] += mPopupPreviewOffsetX; // Offset may be zero
|
mOffsetInWindow[0] += mPopupPreviewOffsetX; // Offset may be zero
|
||||||
mOffsetInWindow[1] += mPopupPreviewOffsetY; // Offset may be zero
|
mOffsetInWindow[1] += mPopupPreviewOffsetY; // Offset may be zero
|
||||||
int[] windowLocation = new int[2];
|
|
||||||
getLocationOnScreen(windowLocation);
|
|
||||||
mWindowY = windowLocation[1];
|
|
||||||
}
|
|
||||||
// Set the preview background state
|
// Set the preview background state
|
||||||
mPreviewText.getBackground().setState(
|
mPreviewText.getBackground().setState(
|
||||||
key.mPopupCharacters != null ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET);
|
key.mPopupCharacters != null ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET);
|
||||||
popupPreviewX += mOffsetInWindow[0];
|
popupPreviewX += mOffsetInWindow[0];
|
||||||
popupPreviewY += mOffsetInWindow[1];
|
popupPreviewY += mOffsetInWindow[1];
|
||||||
|
|
||||||
// If the popup cannot be shown above the key, put it on the side
|
// Place the key preview.
|
||||||
if (popupPreviewY + mWindowY < 0) {
|
// TODO: Adjust position of key previews which touch screen edges
|
||||||
// If the key you're pressing is on the left side of the keyboard, show the popup on
|
if (lp instanceof ViewGroup.MarginLayoutParams) {
|
||||||
// the right, offset by enough to see at least one key to the left/right.
|
ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams)lp;
|
||||||
if (keyDrawX + keyDrawWidth <= getWidth() / 2) {
|
mlp.setMargins(popupPreviewX, popupPreviewY, 0, 0);
|
||||||
popupPreviewX += (int) (keyDrawWidth * 2.5);
|
|
||||||
} else {
|
|
||||||
popupPreviewX -= (int) (keyDrawWidth * 2.5);
|
|
||||||
}
|
}
|
||||||
popupPreviewY += popupHeight;
|
// Record popup preview position to display mini-keyboard later at the same position
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (mPreviewPopup.isShowing()) {
|
|
||||||
mPreviewPopup.update(popupPreviewX, popupPreviewY, popupWidth, popupHeight);
|
|
||||||
} else {
|
|
||||||
mPreviewPopup.setWidth(popupWidth);
|
|
||||||
mPreviewPopup.setHeight(popupHeight);
|
|
||||||
mPreviewPopup.showAtLocation(mMiniKeyboardParent, Gravity.NO_GRAVITY,
|
|
||||||
popupPreviewX, popupPreviewY);
|
|
||||||
}
|
|
||||||
} catch (WindowManager.BadTokenException e) {
|
|
||||||
// Swallow the exception which will be happened when IME is already closed.
|
|
||||||
Log.w(TAG, "LatinIME is already closed when tried showing key preview.");
|
|
||||||
}
|
|
||||||
// Record popup preview position to display mini-keyboard later at the same positon
|
|
||||||
mPopupPreviewDisplayedY = popupPreviewY;
|
mPopupPreviewDisplayedY = popupPreviewY;
|
||||||
mPreviewText.setVisibility(VISIBLE);
|
mPreviewText.setVisibility(VISIBLE);
|
||||||
}
|
}
|
||||||
|
@ -1114,7 +1084,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
||||||
final Keyboard keyboard = new MiniKeyboardBuilder(this, mKeyboard.getPopupKeyboardResId(),
|
final Keyboard keyboard = new MiniKeyboardBuilder(this, mKeyboard.getPopupKeyboardResId(),
|
||||||
popupKey).build();
|
popupKey).build();
|
||||||
miniKeyboardView.setKeyboard(keyboard);
|
miniKeyboardView.setKeyboard(keyboard);
|
||||||
miniKeyboardView.mMiniKeyboardParent = this;
|
|
||||||
|
|
||||||
container.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
|
container.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
|
||||||
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
|
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
|
||||||
|
@ -1349,7 +1318,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void closing() {
|
public void closing() {
|
||||||
mPreviewPopup.dismiss();
|
mPreviewText.setVisibility(View.GONE);
|
||||||
mHandler.cancelAllMessages();
|
mHandler.cancelAllMessages();
|
||||||
|
|
||||||
dismissPopupKeyboard();
|
dismissPopupKeyboard();
|
||||||
|
|
|
@ -549,7 +549,6 @@ public class PointerTracker {
|
||||||
final Key key = getKey(keyIndex);
|
final Key key = getKey(keyIndex);
|
||||||
if (key != null && !key.mEnabled)
|
if (key != null && !key.mEnabled)
|
||||||
return;
|
return;
|
||||||
updateKeyGraphics(keyIndex);
|
|
||||||
// The modifier key, such as shift key, should not be shown as preview when multi-touch is
|
// The modifier key, such as shift key, should not be shown as preview when multi-touch is
|
||||||
// supported. On the other hand, if multi-touch is not supported, the modifier key should
|
// supported. On the other hand, if multi-touch is not supported, the modifier key should
|
||||||
// be shown as preview. If accessibility is turned on, the modifier key should be shown as
|
// be shown as preview. If accessibility is turned on, the modifier key should be shown as
|
||||||
|
@ -559,6 +558,7 @@ public class PointerTracker {
|
||||||
} else {
|
} else {
|
||||||
mProxy.showPreview(keyIndex, this);
|
mProxy.showPreview(keyIndex, this);
|
||||||
}
|
}
|
||||||
|
updateKeyGraphics(keyIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startLongPressTimer(int keyIndex) {
|
private void startLongPressTimer(int keyIndex) {
|
||||||
|
|
|
@ -133,7 +133,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT);
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
mPreviewPopup.setContentView(mPreviewText);
|
mPreviewPopup.setContentView(mPreviewText);
|
||||||
mPreviewPopup.setBackgroundDrawable(null);
|
mPreviewPopup.setBackgroundDrawable(null);
|
||||||
mPreviewPopup.setAnimationStyle(R.style.KeyPreviewAnimation);
|
|
||||||
mConfigCandidateHighlightFontColorEnabled =
|
mConfigCandidateHighlightFontColorEnabled =
|
||||||
res.getBoolean(R.bool.config_candidate_highlight_font_color_enabled);
|
res.getBoolean(R.bool.config_candidate_highlight_font_color_enabled);
|
||||||
mColorNormal = res.getColor(R.color.candidate_normal);
|
mColorNormal = res.getColor(R.color.candidate_normal);
|
||||||
|
|
|
@ -56,7 +56,6 @@ import android.util.DisplayMetrics;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.PrintWriterPrinter;
|
import android.util.PrintWriterPrinter;
|
||||||
import android.util.Printer;
|
import android.util.Printer;
|
||||||
import android.view.Gravity;
|
|
||||||
import android.view.HapticFeedbackConstants;
|
import android.view.HapticFeedbackConstants;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
@ -70,8 +69,6 @@ import android.view.inputmethod.EditorInfo;
|
||||||
import android.view.inputmethod.ExtractedText;
|
import android.view.inputmethod.ExtractedText;
|
||||||
import android.view.inputmethod.ExtractedTextRequest;
|
import android.view.inputmethod.ExtractedTextRequest;
|
||||||
import android.view.inputmethod.InputConnection;
|
import android.view.inputmethod.InputConnection;
|
||||||
import android.widget.FrameLayout;
|
|
||||||
import android.widget.HorizontalScrollView;
|
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
|
@ -143,6 +140,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
|
||||||
};
|
};
|
||||||
|
|
||||||
private View mCandidateViewContainer;
|
private View mCandidateViewContainer;
|
||||||
|
private int mCandidateStripHeight;
|
||||||
private CandidateView mCandidateView;
|
private CandidateView mCandidateView;
|
||||||
private Suggest mSuggest;
|
private Suggest mSuggest;
|
||||||
private CompletionInfo[] mApplicationSpecifiedCompletions;
|
private CompletionInfo[] mApplicationSpecifiedCompletions;
|
||||||
|
@ -528,12 +526,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
|
||||||
LayoutInflater inflater = getLayoutInflater();
|
LayoutInflater inflater = getLayoutInflater();
|
||||||
LinearLayout container = (LinearLayout)inflater.inflate(R.layout.candidates, null);
|
LinearLayout container = (LinearLayout)inflater.inflate(R.layout.candidates, null);
|
||||||
mCandidateViewContainer = container;
|
mCandidateViewContainer = container;
|
||||||
if (container.getPaddingRight() != 0) {
|
mCandidateStripHeight = (int)mResources.getDimension(R.dimen.candidate_strip_height);
|
||||||
HorizontalScrollView scrollView =
|
|
||||||
(HorizontalScrollView) container.findViewById(R.id.candidates_scroll_view);
|
|
||||||
setOverScrollModeNever(scrollView);
|
|
||||||
container.setGravity(Gravity.CENTER_HORIZONTAL);
|
|
||||||
}
|
|
||||||
mCandidateView = (CandidateView) container.findViewById(R.id.candidates);
|
mCandidateView = (CandidateView) container.findViewById(R.id.candidates);
|
||||||
mCandidateView.setService(this);
|
mCandidateView.setService(this);
|
||||||
setCandidatesViewShown(true);
|
setCandidatesViewShown(true);
|
||||||
|
@ -581,8 +574,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
|
||||||
switcher.updateShiftState();
|
switcher.updateShiftState();
|
||||||
}
|
}
|
||||||
|
|
||||||
setCandidatesViewShownInternal(isCandidateStripVisible(),
|
setCandidatesViewShownInternal(isCandidateStripVisible(), false /* needsInputViewShown */ );
|
||||||
false /* needsInputViewShown */ );
|
|
||||||
// Delay updating suggestions because keyboard input view may not be shown at this point.
|
// Delay updating suggestions because keyboard input view may not be shown at this point.
|
||||||
mHandler.postUpdateSuggestions();
|
mHandler.postUpdateSuggestions();
|
||||||
|
|
||||||
|
@ -872,10 +864,21 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setCandidatesViewShownInternal(boolean shown, boolean needsInputViewShown) {
|
private void setCandidatesViewShownInternal(boolean shown, boolean needsInputViewShown) {
|
||||||
// TODO: Remove this if we support candidates with hard keyboard
|
// TODO: Modify this if we support candidates with hard keyboard
|
||||||
if (onEvaluateInputViewShown()) {
|
if (onEvaluateInputViewShown()) {
|
||||||
super.setCandidatesViewShown(shown
|
final boolean shouldShowCandidates = shown
|
||||||
&& (needsInputViewShown ? mKeyboardSwitcher.isInputViewShown() : true));
|
&& (needsInputViewShown ? mKeyboardSwitcher.isInputViewShown() : true);
|
||||||
|
if (isExtractViewShown()) {
|
||||||
|
// No need to have extra space to show the key preview.
|
||||||
|
mCandidateViewContainer.setMinimumHeight(0);
|
||||||
|
super.setCandidatesViewShown(shown);
|
||||||
|
} else {
|
||||||
|
// We must control the visibility of the suggestion strip in order to avoid clipped
|
||||||
|
// key previews, even when we don't show the suggestion strip.
|
||||||
|
mCandidateViewContainer.setVisibility(
|
||||||
|
shouldShowCandidates ? View.VISIBLE : View.INVISIBLE);
|
||||||
|
super.setCandidatesViewShown(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -887,35 +890,25 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
|
||||||
@Override
|
@Override
|
||||||
public void onComputeInsets(InputMethodService.Insets outInsets) {
|
public void onComputeInsets(InputMethodService.Insets outInsets) {
|
||||||
super.onComputeInsets(outInsets);
|
super.onComputeInsets(outInsets);
|
||||||
if (!isFullscreenMode()) {
|
final KeyboardView inputView = mKeyboardSwitcher.getInputView();
|
||||||
outInsets.contentTopInsets = outInsets.visibleTopInsets;
|
|
||||||
}
|
|
||||||
KeyboardView inputView = mKeyboardSwitcher.getInputView();
|
|
||||||
// Need to set touchable region only if input view is being shown
|
// Need to set touchable region only if input view is being shown
|
||||||
if (inputView != null && mKeyboardSwitcher.isInputViewShown()) {
|
if (inputView != null && mKeyboardSwitcher.isInputViewShown()) {
|
||||||
final int x = 0;
|
final int containerHeight = mCandidateViewContainer.getHeight();
|
||||||
int y = 0;
|
int touchY = containerHeight;
|
||||||
final int width = inputView.getWidth();
|
if (mCandidateViewContainer.getVisibility() == View.VISIBLE) {
|
||||||
int height = inputView.getHeight() + EXTENDED_TOUCHABLE_REGION_HEIGHT;
|
touchY -= mCandidateStripHeight;
|
||||||
if (mCandidateViewContainer != null) {
|
|
||||||
ViewParent candidateParent = mCandidateViewContainer.getParent();
|
|
||||||
if (candidateParent instanceof FrameLayout) {
|
|
||||||
FrameLayout fl = (FrameLayout) candidateParent;
|
|
||||||
if (fl != null) {
|
|
||||||
// Check frame layout's visibility
|
|
||||||
if (fl.getVisibility() == View.INVISIBLE) {
|
|
||||||
y = fl.getHeight();
|
|
||||||
height += y;
|
|
||||||
} else if (fl.getVisibility() == View.VISIBLE) {
|
|
||||||
height += fl.getHeight();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
outInsets.contentTopInsets = touchY;
|
||||||
|
outInsets.visibleTopInsets = touchY;
|
||||||
|
final int touchWidth = inputView.getWidth();
|
||||||
|
final int touchHeight = inputView.getHeight() + containerHeight
|
||||||
|
// Extend touchable region below the keyboard.
|
||||||
|
+ EXTENDED_TOUCHABLE_REGION_HEIGHT;
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "Touchable region " + x + ", " + y + ", " + width + ", " + height);
|
Log.d(TAG, "Touchable region: y=" + touchY + " width=" + touchWidth
|
||||||
|
+ " height=" + touchHeight);
|
||||||
}
|
}
|
||||||
setTouchableRegionCompat(outInsets, x, y, width, height);
|
setTouchableRegionCompat(outInsets, 0, touchY, touchWidth, touchHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue