summaryrefslogtreecommitdiff
path: root/src/com/android/inputmethod/pinyin/SoftKeyboardView.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/inputmethod/pinyin/SoftKeyboardView.java')
-rw-r--r--src/com/android/inputmethod/pinyin/SoftKeyboardView.java478
1 files changed, 478 insertions, 0 deletions
diff --git a/src/com/android/inputmethod/pinyin/SoftKeyboardView.java b/src/com/android/inputmethod/pinyin/SoftKeyboardView.java
new file mode 100644
index 0000000..5543f33
--- /dev/null
+++ b/src/com/android/inputmethod/pinyin/SoftKeyboardView.java
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.pinyin;
+
+import com.android.inputmethod.pinyin.SoftKeyboard.KeyRow;
+
+import java.util.List;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Paint.FontMetricsInt;
+import android.graphics.drawable.Drawable;
+import android.os.Vibrator;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Class used to show a soft keyboard.
+ *
+ * A soft keyboard view should not handle touch event itself, because we do bias
+ * correction, need a global strategy to map an event into a proper view to
+ * achieve better user experience.
+ */
+public class SoftKeyboardView extends View {
+ /**
+ * The definition of the soft keyboard for the current this soft keyboard
+ * view.
+ */
+ private SoftKeyboard mSoftKeyboard;
+
+ /**
+ * The popup balloon hint for key press/release.
+ */
+ private BalloonHint mBalloonPopup;
+
+ /**
+ * The on-key balloon hint for key press/release. If it is null, on-key
+ * highlight will be drawn on th soft keyboard view directly.
+ */
+ private BalloonHint mBalloonOnKey;
+
+ /** Used to play key sounds. */
+ private SoundManager mSoundManager;
+
+ /** The last key pressed. */
+ private SoftKey mSoftKeyDown;
+
+ /** Used to indicate whether the user is holding on a key. */
+ private boolean mKeyPressed = false;
+
+ /**
+ * The location offset of the view to the keyboard container.
+ */
+ private int mOffsetToSkbContainer[] = new int[2];
+
+ /**
+ * The location of the desired hint view to the keyboard container.
+ */
+ private int mHintLocationToSkbContainer[] = new int[2];
+
+ /**
+ * Text size for normal key.
+ */
+ private int mNormalKeyTextSize;
+
+ /**
+ * Text size for function key.
+ */
+ private int mFunctionKeyTextSize;
+
+ /**
+ * Long press timer used to response long-press.
+ */
+ private SkbContainer.LongPressTimer mLongPressTimer;
+
+ /**
+ * Repeated events for long press
+ */
+ private boolean mRepeatForLongPress = false;
+
+ /**
+ * If this parameter is true, the balloon will never be dismissed even if
+ * user moves a lot from the pressed point.
+ */
+ private boolean mMovingNeverHidePopupBalloon = false;
+
+ /** Vibration for key press. */
+ private Vibrator mVibrator;
+
+ /** Vibration pattern for key press. */
+ protected long[] mVibratePattern = new long[] {1, 20};
+
+ /**
+ * The dirty rectangle used to mark the area to re-draw during key press and
+ * release. Currently, whenever we can invalidate(Rect), view will call
+ * onDraw() and we MUST draw the whole view. This dirty information is for
+ * future use.
+ */
+ private Rect mDirtyRect = new Rect();
+
+ private Paint mPaint;
+ private FontMetricsInt mFmi;
+ private boolean mDimSkb;
+
+ public SoftKeyboardView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ mSoundManager = SoundManager.getInstance(mContext);
+
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
+ mFmi = mPaint.getFontMetricsInt();
+ }
+
+ public boolean setSoftKeyboard(SoftKeyboard softSkb) {
+ if (null == softSkb) {
+ return false;
+ }
+ mSoftKeyboard = softSkb;
+ Drawable bg = softSkb.getSkbBackground();
+ if (null != bg) setBackgroundDrawable(bg);
+ return true;
+ }
+
+ public SoftKeyboard getSoftKeyboard() {
+ return mSoftKeyboard;
+ }
+
+ public void resizeKeyboard(int skbWidth, int skbHeight) {
+ mSoftKeyboard.setSkbCoreSize(skbWidth, skbHeight);
+ }
+
+ public void setBalloonHint(BalloonHint balloonOnKey,
+ BalloonHint balloonPopup, boolean movingNeverHidePopup) {
+ mBalloonOnKey = balloonOnKey;
+ mBalloonPopup = balloonPopup;
+ mMovingNeverHidePopupBalloon = movingNeverHidePopup;
+ }
+
+ public void setOffsetToSkbContainer(int offsetToSkbContainer[]) {
+ mOffsetToSkbContainer[0] = offsetToSkbContainer[0];
+ mOffsetToSkbContainer[1] = offsetToSkbContainer[1];
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int measuredWidth = 0;
+ int measuredHeight = 0;
+ if (null != mSoftKeyboard) {
+ measuredWidth = mSoftKeyboard.getSkbCoreWidth();
+ measuredHeight = mSoftKeyboard.getSkbCoreHeight();
+ measuredWidth += mPaddingLeft + mPaddingRight;
+ measuredHeight += mPaddingTop + mPaddingBottom;
+ }
+ setMeasuredDimension(measuredWidth, measuredHeight);
+ }
+
+ private void showBalloon(BalloonHint balloon, int balloonLocationToSkb[],
+ boolean movePress) {
+ long delay = BalloonHint.TIME_DELAY_SHOW;
+ if (movePress) delay = 0;
+ if (balloon.needForceDismiss()) {
+ balloon.delayedDismiss(0);
+ }
+ if (!balloon.isShowing()) {
+ balloon.delayedShow(delay, balloonLocationToSkb);
+ } else {
+ balloon.delayedUpdate(delay, balloonLocationToSkb, balloon
+ .getWidth(), balloon.getHeight());
+ }
+ long b = System.currentTimeMillis();
+ }
+
+ public void resetKeyPress(long balloonDelay) {
+ if (!mKeyPressed) return;
+ mKeyPressed = false;
+ if (null != mBalloonOnKey) {
+ mBalloonOnKey.delayedDismiss(balloonDelay);
+ } else {
+ if (null != mSoftKeyDown) {
+ if (mDirtyRect.isEmpty()) {
+ mDirtyRect.set(mSoftKeyDown.mLeft, mSoftKeyDown.mTop,
+ mSoftKeyDown.mRight, mSoftKeyDown.mBottom);
+ }
+ invalidate(mDirtyRect);
+ } else {
+ invalidate();
+ }
+ }
+ mBalloonPopup.delayedDismiss(balloonDelay);
+ }
+
+ // If movePress is true, means that this function is called because user
+ // moves his finger to this button. If movePress is false, means that this
+ // function is called when user just presses this key.
+ public SoftKey onKeyPress(int x, int y,
+ SkbContainer.LongPressTimer longPressTimer, boolean movePress) {
+ mKeyPressed = false;
+ boolean moveWithinPreviousKey = false;
+ if (movePress) {
+ SoftKey newKey = mSoftKeyboard.mapToKey(x, y);
+ if (newKey == mSoftKeyDown) moveWithinPreviousKey = true;
+ mSoftKeyDown = newKey;
+ } else {
+ mSoftKeyDown = mSoftKeyboard.mapToKey(x, y);
+ }
+ if (moveWithinPreviousKey || null == mSoftKeyDown) return mSoftKeyDown;
+ mKeyPressed = true;
+
+ if (!movePress) {
+ tryPlayKeyDown();
+ tryVibrate();
+ }
+
+ mLongPressTimer = longPressTimer;
+
+ if (!movePress) {
+ if (mSoftKeyDown.getPopupResId() > 0 || mSoftKeyDown.repeatable()) {
+ mLongPressTimer.startTimer();
+ }
+ } else {
+ mLongPressTimer.removeTimer();
+ }
+
+ int desired_width;
+ int desired_height;
+ float textSize;
+ Environment env = Environment.getInstance();
+
+ if (null != mBalloonOnKey) {
+ Drawable keyHlBg = mSoftKeyDown.getKeyHlBg();
+ mBalloonOnKey.setBalloonBackground(keyHlBg);
+
+ // Prepare the on-key balloon
+ int keyXMargin = mSoftKeyboard.getKeyXMargin();
+ int keyYMargin = mSoftKeyboard.getKeyYMargin();
+ desired_width = mSoftKeyDown.width() - 2 * keyXMargin;
+ desired_height = mSoftKeyDown.height() - 2 * keyYMargin;
+ textSize = env
+ .getKeyTextSize(SoftKeyType.KEYTYPE_ID_NORMAL_KEY != mSoftKeyDown.mKeyType.mKeyTypeId);
+ Drawable icon = mSoftKeyDown.getKeyIcon();
+ if (null != icon) {
+ mBalloonOnKey.setBalloonConfig(icon, desired_width,
+ desired_height);
+ } else {
+ mBalloonOnKey.setBalloonConfig(mSoftKeyDown.getKeyLabel(),
+ textSize, true, mSoftKeyDown.getColorHl(),
+ desired_width, desired_height);
+ }
+
+ mHintLocationToSkbContainer[0] = mPaddingLeft + mSoftKeyDown.mLeft
+ - (mBalloonOnKey.getWidth() - mSoftKeyDown.width()) / 2;
+ mHintLocationToSkbContainer[0] += mOffsetToSkbContainer[0];
+ mHintLocationToSkbContainer[1] = mPaddingTop
+ + (mSoftKeyDown.mBottom - keyYMargin)
+ - mBalloonOnKey.getHeight();
+ mHintLocationToSkbContainer[1] += mOffsetToSkbContainer[1];
+ showBalloon(mBalloonOnKey, mHintLocationToSkbContainer, movePress);
+ } else {
+ mDirtyRect.union(mSoftKeyDown.mLeft, mSoftKeyDown.mTop,
+ mSoftKeyDown.mRight, mSoftKeyDown.mBottom);
+ invalidate(mDirtyRect);
+ }
+
+ // Prepare the popup balloon
+ if (mSoftKeyDown.needBalloon()) {
+ Drawable balloonBg = mSoftKeyboard.getBalloonBackground();
+ mBalloonPopup.setBalloonBackground(balloonBg);
+
+ desired_width = mSoftKeyDown.width() + env.getKeyBalloonWidthPlus();
+ desired_height = mSoftKeyDown.height()
+ + env.getKeyBalloonHeightPlus();
+ textSize = env
+ .getBalloonTextSize(SoftKeyType.KEYTYPE_ID_NORMAL_KEY != mSoftKeyDown.mKeyType.mKeyTypeId);
+ Drawable iconPopup = mSoftKeyDown.getKeyIconPopup();
+ if (null != iconPopup) {
+ mBalloonPopup.setBalloonConfig(iconPopup, desired_width,
+ desired_height);
+ } else {
+ mBalloonPopup.setBalloonConfig(mSoftKeyDown.getKeyLabel(),
+ textSize, mSoftKeyDown.needBalloon(), mSoftKeyDown
+ .getColorBalloon(), desired_width,
+ desired_height);
+ }
+
+ // The position to show.
+ mHintLocationToSkbContainer[0] = mPaddingLeft + mSoftKeyDown.mLeft
+ + -(mBalloonPopup.getWidth() - mSoftKeyDown.width()) / 2;
+ mHintLocationToSkbContainer[0] += mOffsetToSkbContainer[0];
+ mHintLocationToSkbContainer[1] = mPaddingTop + mSoftKeyDown.mTop
+ - mBalloonPopup.getHeight();
+ mHintLocationToSkbContainer[1] += mOffsetToSkbContainer[1];
+ showBalloon(mBalloonPopup, mHintLocationToSkbContainer, movePress);
+ } else {
+ mBalloonPopup.delayedDismiss(0);
+ }
+
+ if (mRepeatForLongPress) longPressTimer.startTimer();
+ return mSoftKeyDown;
+ }
+
+ public SoftKey onKeyRelease(int x, int y) {
+ mKeyPressed = false;
+ if (null == mSoftKeyDown) return null;
+
+ mLongPressTimer.removeTimer();
+
+ if (null != mBalloonOnKey) {
+ mBalloonOnKey.delayedDismiss(BalloonHint.TIME_DELAY_DISMISS);
+ } else {
+ mDirtyRect.union(mSoftKeyDown.mLeft, mSoftKeyDown.mTop,
+ mSoftKeyDown.mRight, mSoftKeyDown.mBottom);
+ invalidate(mDirtyRect);
+ }
+
+ if (mSoftKeyDown.needBalloon()) {
+ mBalloonPopup.delayedDismiss(BalloonHint.TIME_DELAY_DISMISS);
+ }
+
+ if (mSoftKeyDown.moveWithinKey(x - mPaddingLeft, y - mPaddingTop)) {
+ return mSoftKeyDown;
+ }
+ return null;
+ }
+
+ public SoftKey onKeyMove(int x, int y) {
+ if (null == mSoftKeyDown) return null;
+
+ if (mSoftKeyDown.moveWithinKey(x - mPaddingLeft, y - mPaddingTop)) {
+ return mSoftKeyDown;
+ }
+
+ // The current key needs to be updated.
+ mDirtyRect.union(mSoftKeyDown.mLeft, mSoftKeyDown.mTop,
+ mSoftKeyDown.mRight, mSoftKeyDown.mBottom);
+
+ if (mRepeatForLongPress) {
+ if (mMovingNeverHidePopupBalloon) {
+ return onKeyPress(x, y, mLongPressTimer, true);
+ }
+
+ if (null != mBalloonOnKey) {
+ mBalloonOnKey.delayedDismiss(0);
+ } else {
+ invalidate(mDirtyRect);
+ }
+
+ if (mSoftKeyDown.needBalloon()) {
+ mBalloonPopup.delayedDismiss(0);
+ }
+
+ if (null != mLongPressTimer) {
+ mLongPressTimer.removeTimer();
+ }
+ return onKeyPress(x, y, mLongPressTimer, true);
+ } else {
+ // When user moves between keys, repeated response is disabled.
+ return onKeyPress(x, y, mLongPressTimer, true);
+ }
+ }
+
+ private void tryVibrate() {
+ if (!Settings.getVibrate()) {
+ return;
+ }
+ if (mVibrator == null) {
+ mVibrator = new Vibrator();
+ }
+ mVibrator.vibrate(mVibratePattern, -1);
+ }
+
+ private void tryPlayKeyDown() {
+ if (Settings.getKeySound()) {
+ mSoundManager.playKeyDown();
+ }
+ }
+
+ public void dimSoftKeyboard(boolean dimSkb) {
+ mDimSkb = dimSkb;
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (null == mSoftKeyboard) return;
+
+ canvas.translate(mPaddingLeft, mPaddingTop);
+
+ Environment env = Environment.getInstance();
+ mNormalKeyTextSize = env.getKeyTextSize(false);
+ mFunctionKeyTextSize = env.getKeyTextSize(true);
+ // Draw the last soft keyboard
+ int rowNum = mSoftKeyboard.getRowNum();
+ int keyXMargin = mSoftKeyboard.getKeyXMargin();
+ int keyYMargin = mSoftKeyboard.getKeyYMargin();
+ for (int row = 0; row < rowNum; row++) {
+ KeyRow keyRow = mSoftKeyboard.getKeyRowForDisplay(row);
+ if (null == keyRow) continue;
+ List<SoftKey> softKeys = keyRow.mSoftKeys;
+ int keyNum = softKeys.size();
+ for (int i = 0; i < keyNum; i++) {
+ SoftKey softKey = softKeys.get(i);
+ if (SoftKeyType.KEYTYPE_ID_NORMAL_KEY == softKey.mKeyType.mKeyTypeId) {
+ mPaint.setTextSize(mNormalKeyTextSize);
+ } else {
+ mPaint.setTextSize(mFunctionKeyTextSize);
+ }
+ drawSoftKey(canvas, softKey, keyXMargin, keyYMargin);
+ }
+ }
+
+ if (mDimSkb) {
+ mPaint.setColor(0xa0000000);
+ canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
+ }
+
+ mDirtyRect.setEmpty();
+ }
+
+ private void drawSoftKey(Canvas canvas, SoftKey softKey, int keyXMargin,
+ int keyYMargin) {
+ Drawable bg;
+ int textColor;
+ if (mKeyPressed && softKey == mSoftKeyDown) {
+ bg = softKey.getKeyHlBg();
+ textColor = softKey.getColorHl();
+ } else {
+ bg = softKey.getKeyBg();
+ textColor = softKey.getColor();
+ }
+
+ if (null != bg) {
+ bg.setBounds(softKey.mLeft + keyXMargin, softKey.mTop + keyYMargin,
+ softKey.mRight - keyXMargin, softKey.mBottom - keyYMargin);
+ bg.draw(canvas);
+ }
+
+ String keyLabel = softKey.getKeyLabel();
+ Drawable keyIcon = softKey.getKeyIcon();
+ if (null != keyIcon) {
+ Drawable icon = keyIcon;
+ int marginLeft = (softKey.width() - icon.getIntrinsicWidth()) / 2;
+ int marginRight = softKey.width() - icon.getIntrinsicWidth()
+ - marginLeft;
+ int marginTop = (softKey.height() - icon.getIntrinsicHeight()) / 2;
+ int marginBottom = softKey.height() - icon.getIntrinsicHeight()
+ - marginTop;
+ icon.setBounds(softKey.mLeft + marginLeft,
+ softKey.mTop + marginTop, softKey.mRight - marginRight,
+ softKey.mBottom - marginBottom);
+ icon.draw(canvas);
+ } else if (null != keyLabel) {
+ mPaint.setColor(textColor);
+ float x = softKey.mLeft
+ + (softKey.width() - mPaint.measureText(keyLabel)) / 2.0f;
+ int fontHeight = mFmi.bottom - mFmi.top;
+ float marginY = (softKey.height() - fontHeight) / 2.0f;
+ float y = softKey.mTop + marginY - mFmi.top + mFmi.bottom / 1.5f;
+ canvas.drawText(keyLabel, x, y + 1, mPaint);
+ }
+ }
+}