PageRenderTime 108ms CodeModel.GetById 13ms app.highlight 83ms RepoModel.GetById 2ms app.codeStats 0ms

/ime/latinime/src/com/googlecode/eyesfree/inputmethod/latin/LatinKeyboardBaseView.java

http://eyes-free.googlecode.com/
Java | 1809 lines | 1220 code | 204 blank | 385 comment | 255 complexity | 0ac10b54221dc7e48a00a88aa1d3d2b7 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2 * Copyright (C) 2010 The Android Open Source Project
   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 com.googlecode.eyesfree.inputmethod.latin;
  18
  19import android.content.Context;
  20import android.content.pm.PackageManager;
  21import android.content.res.Resources;
  22import android.content.res.TypedArray;
  23import android.graphics.Bitmap;
  24import android.graphics.Canvas;
  25import android.graphics.Paint;
  26import android.graphics.Paint.Align;
  27import android.graphics.PorterDuff;
  28import android.graphics.Rect;
  29import android.graphics.Region.Op;
  30import android.graphics.Typeface;
  31import android.graphics.drawable.Drawable;
  32import android.inputmethodservice.Keyboard;
  33import android.inputmethodservice.Keyboard.Key;
  34import android.os.Build;
  35import android.os.Handler;
  36import android.os.Message;
  37import android.os.SystemClock;
  38import android.util.AttributeSet;
  39import android.util.Log;
  40import android.util.TypedValue;
  41import android.view.Gravity;
  42import android.view.KeyEvent;
  43import android.view.LayoutInflater;
  44import android.view.MotionEvent;
  45import android.view.View;
  46import android.view.ViewConfiguration;
  47import android.view.ViewGroup.LayoutParams;
  48import android.widget.PopupWindow;
  49import android.widget.TextView;
  50
  51import com.googlecode.eyesfree.inputmethod.MultitouchGestureDetector;
  52import com.googlecode.eyesfree.inputmethod.SegmentDetector;
  53import com.googlecode.eyesfree.inputmethod.SegmentDetector.SegmentListener;
  54import com.googlecode.eyesfree.inputmethod.SimpleMultitouchGestureListener;
  55import com.googlecode.eyesfree.utils.compat.MotionEventCompatUtils;
  56
  57import java.util.ArrayList;
  58import java.util.HashMap;
  59import java.util.LinkedList;
  60import java.util.List;
  61import java.util.WeakHashMap;
  62
  63/**
  64 * A view that renders a virtual {@link LatinKeyboard}. It handles rendering of keys and detecting
  65 * key presses and touch movements. TODO: References to LatinKeyboard in this class should be
  66 * replaced with ones to its base class.
  67 *
  68 * @attr ref R.styleable#LatinKeyboardBaseView_keyBackground
  69 * @attr ref R.styleable#LatinKeyboardBaseView_keyPreviewLayout
  70 * @attr ref R.styleable#LatinKeyboardBaseView_keyPreviewOffset
  71 * @attr ref R.styleable#LatinKeyboardBaseView_labelTextSize
  72 * @attr ref R.styleable#LatinKeyboardBaseView_keyTextSize
  73 * @attr ref R.styleable#LatinKeyboardBaseView_keyTextColor
  74 * @attr ref R.styleable#LatinKeyboardBaseView_verticalCorrection
  75 * @attr ref R.styleable#LatinKeyboardBaseView_popupLayout
  76 */
  77public class LatinKeyboardBaseView extends View implements PointerTracker.UIProxy {
  78    private static final String TAG = "LatinKeyboardBaseView";
  79    private static final boolean DEBUG = false;
  80
  81    public static final int NOT_A_TOUCH_COORDINATE = -1;
  82
  83    public interface OnKeyboardActionListener {
  84
  85        /**
  86         * Called when the user presses a key. This is sent before the {@link #onKey} is called. For
  87         * keys that repeat, this is only called once.
  88         *
  89         * @param key the key being pressed
  90         */
  91        void onPress(Key key);
  92
  93        /**
  94         * Called when the user leaves a key without lifting their finger. This is send before the
  95         * {@link #onKey} is called. For keys that repeat, this is only called once.
  96         *
  97         * @param key the key that was left
  98         */
  99        void onLeaving(Key key);
 100
 101        /**
 102         * Called when the user releases a key. This is sent after the {@link #onKey} is called. For
 103         * keys that repeat, this is only called once.
 104         *
 105         * @param key the key that was released
 106         */
 107        void onRelease(Key key);
 108
 109        /**
 110         * Send a key press to the listener.
 111         *
 112         * @param primaryCode this is the key that was pressed
 113         * @param keyCodes the codes for all the possible alternative keys with the primary code
 114         *            being the first. If the primary key code is a single character such as an
 115         *            alphabet or number or symbol, the alternatives will include other characters
 116         *            that may be on the same key or adjacent keys. These codes are useful to
 117         *            correct for accidental presses of a key adjacent to the intended key.
 118         * @param x x-coordinate pixel of touched event. If onKey is not called by onTouchEvent, the
 119         *            value should be NOT_A_TOUCH_COORDINATE.
 120         * @param y y-coordinate pixel of touched event. If onKey is not called by onTouchEvent, the
 121         *            value should be NOT_A_TOUCH_COORDINATE.
 122         */
 123        void onKey(int primaryCode, int[] keyCodes, int x, int y);
 124
 125        /**
 126         * Sends a sequence of characters to the listener.
 127         *
 128         * @param text the sequence of characters to be displayed.
 129         */
 130        void onText(CharSequence text);
 131
 132        /**
 133         * Called when user released a finger outside any key.
 134         */
 135        void onCancel();
 136
 137        /**
 138         * Called when the user quickly moves the finger from right to left.
 139         *
 140         * @param pointerCount
 141         * @return <code>true</code> if the event was consumed.
 142         */
 143        boolean swipeLeft(int pointerCount);
 144
 145        /**
 146         * Called when the user quickly moves the finger from left to right.
 147         *
 148         * @param pointerCount
 149         * @return <code>true</code> if the event was consumed.
 150         */
 151        boolean swipeRight(int pointerCount);
 152
 153        /**
 154         * Called when the user quickly moves the finger from up to down.
 155         *
 156         * @param pointerCount
 157         * @return <code>true</code> if the event was consumed.
 158         */
 159        boolean swipeDown(int pointerCount);
 160
 161        /**
 162         * Called when the user quickly moves the finger from down to up.
 163         *
 164         * @param pointerCount
 165         * @return <code>true</code> if the event was consumed.
 166         */
 167        boolean swipeUp(int pointerCount);
 168
 169        /**
 170         * Called when the user quickly taps the finger on screen.
 171         *
 172         * @param pointerCount
 173         * @return <code>true</code> if the event was consumed.
 174         */
 175        boolean singleTap(int pointerCount);
 176
 177        /**
 178         * Called when the user double taps the finger on screen.
 179         *
 180         * @param pointerCount
 181         * @return <code>true</code> if the event was consumed.
 182         */
 183        boolean doubleTap(int pointerCount);
 184
 185        /**
 186         * Called when the user long-presses a finger on screen.
 187         *
 188         * @param pointerCount
 189         * @return <code>true</code> if the event was consumed.
 190         */
 191        boolean longPress(int pointerCount);
 192
 193        /**
 194         * Called when the user moves the finger outside the keyboard area.
 195         */
 196        void exploreKeyboardArea();
 197
 198        /**
 199         * Called when the user moves the finger outside the keyboard area.
 200         */
 201        void enteredSegment(int segment);
 202
 203        /**
 204         * Called when the user selects a segment.
 205         */
 206        void selectedSegment(int segment);
 207
 208        /**
 209         * Called when the user moves the finger outside the keyboard area.
 210         */
 211        void leftKeyboardArea();
 212
 213        /**
 214         * Called when the user moves the finger outside the keyboard area, then back inside the
 215         * keyboard area.
 216         */
 217        void enteredKeyboardArea();
 218
 219        /**
 220         * Called when a finger is lifted outside the keyboard area.
 221         */
 222        void upOutsideKeyboardArea();
 223    }
 224
 225    // Timing constants
 226    private final int mKeyRepeatInterval;
 227
 228    // Miscellaneous constants
 229    /* package */static final int NOT_A_KEY = -1;
 230    private static final int[] LONG_PRESSABLE_STATE_SET = {
 231            android.R.attr.state_long_pressable
 232    };
 233    private static final int NUMBER_HINT_VERTICAL_ADJUSTMENT_PIXEL = -1;
 234
 235    // XML attribute
 236    private int mKeyTextSize;
 237    private int mKeyTextColor;
 238    private Typeface mKeyTextStyle = Typeface.DEFAULT;
 239    private int mLabelTextSize;
 240    private int mSymbolColorScheme = 0;
 241    private int mShadowColor;
 242    private float mShadowRadius;
 243    private Drawable mKeyBackground;
 244    private float mBackgroundDimAmount;
 245    private float mKeyHysteresisDistance;
 246    private float mVerticalCorrection;
 247    private int mPreviewOffset;
 248    private int mPreviewHeight;
 249    private int mPopupLayout;
 250
 251    // Main keyboard
 252    private Keyboard mKeyboard;
 253    private Key[] mKeys;
 254    // TODO this attribute should be gotten from Keyboard.
 255    private int mKeyboardVerticalGap;
 256
 257    // Key preview popup
 258    private TextView mPreviewText;
 259    private PopupWindow mPreviewPopup;
 260    private int mPreviewTextSizeLarge;
 261    private int[] mOffsetInWindow;
 262    private int mOldPreviewKeyIndex = NOT_A_KEY;
 263    private boolean mShowPreview = true;
 264    private boolean mShowTouchPoints = true;
 265    private int mPopupPreviewOffsetX;
 266    private int mPopupPreviewOffsetY;
 267    private int mWindowY;
 268    private int mPopupPreviewDisplayedY;
 269    private final int mDelayBeforePreview;
 270    private final int mDelayAfterPreview;
 271
 272    // Popup mini keyboard
 273    private PopupWindow mMiniKeyboardPopup;
 274    private LatinKeyboardBaseView mMiniKeyboard;
 275    private View mMiniKeyboardParent;
 276    private final WeakHashMap<Key, View> mMiniKeyboardCache = new WeakHashMap<Key, View>();
 277    private int mMiniKeyboardOriginX;
 278    private int mMiniKeyboardOriginY;
 279    private long mMiniKeyboardPopupTime;
 280    private int[] mWindowOffset;
 281    private final float mMiniKeyboardSlideAllowance;
 282    private int mMiniKeyboardTrackerId;
 283
 284    /** Listener for {@link OnKeyboardActionListener}. */
 285    private OnKeyboardActionListener mKeyboardActionListener;
 286
 287    private final ArrayList<PointerTracker> mPointerTrackers = new ArrayList<PointerTracker>();
 288
 289    // TODO: Let the PointerTracker class manage this pointer queue
 290    private final PointerQueue mPointerQueue = new PointerQueue();
 291
 292    private final boolean mHasDistinctMultitouch;
 293    private int mOldPointerCount = 1;
 294
 295    // Accessibility
 296    private final int mTouchSlop;
 297    private boolean mIsAccessibilityEnabled;
 298    private boolean mInKeyboardArea = false;
 299
 300    protected KeyDetector mKeyDetector = new ProximityKeyDetector();
 301
 302    // Segment detector
 303    private SegmentDetector mSegmentDetector;
 304
 305    // Swipe gesture detector
 306    private MultitouchGestureDetector mGestureDetector;
 307
 308    // Drawing
 309    /** Whether the keyboard bitmap needs to be redrawn before it's blitted. **/
 310    private boolean mDrawPending;
 311    /** The dirty region in the keyboard bitmap */
 312    private final Rect mDirtyRect = new Rect();
 313    /** The keyboard bitmap for faster updates */
 314    private Bitmap mBuffer;
 315    /**
 316     * Notes if the keyboard just changed, so that we could possibly reallocate the mBuffer.
 317     */
 318    private boolean mKeyboardChanged;
 319    private Key mInvalidatedKey;
 320    /** The canvas for the above mutable keyboard bitmap */
 321    private Canvas mCanvas;
 322    private final Paint mPaint;
 323    private final Rect mPadding;
 324    private final Rect mClipRegion = new Rect(0, 0, 0, 0);
 325    // This map caches key label text height in pixel as value and key label
 326    // text size as map key.
 327    private final HashMap<Integer, Integer> mTextHeightCache = new HashMap<Integer, Integer>();
 328    // Distance from horizontal center of the key, proportional to key label
 329    // text height.
 330    private final float KEY_LABEL_VERTICAL_ADJUSTMENT_FACTOR = 0.55f;
 331    private final String KEY_LABEL_HEIGHT_REFERENCE_CHAR = "H";
 332
 333    private final UIHandler mHandler = new UIHandler();
 334
 335    class UIHandler extends Handler {
 336        private static final int MSG_POPUP_PREVIEW = 1;
 337        private static final int MSG_DISMISS_PREVIEW = 2;
 338        private static final int MSG_REPEAT_KEY = 3;
 339        private static final int MSG_LONGPRESS_KEY = 4;
 340
 341        private boolean mInKeyRepeat;
 342
 343        @Override
 344        public void handleMessage(Message msg) {
 345            switch (msg.what) {
 346                case MSG_POPUP_PREVIEW:
 347                    showKey(msg.arg1, (PointerTracker) msg.obj);
 348                    break;
 349                case MSG_DISMISS_PREVIEW:
 350                    mPreviewPopup.dismiss();
 351                    break;
 352                case MSG_REPEAT_KEY: {
 353                    final PointerTracker tracker = (PointerTracker) msg.obj;
 354                    tracker.repeatKey(msg.arg1);
 355                    startKeyRepeatTimer(mKeyRepeatInterval, msg.arg1, tracker);
 356                    break;
 357                }
 358                case MSG_LONGPRESS_KEY: {
 359                    final PointerTracker tracker = (PointerTracker) msg.obj;
 360                    openPopupIfRequired(msg.arg1, tracker);
 361                    break;
 362                }
 363            }
 364        }
 365
 366        public void popupPreview(long delay, int keyIndex, PointerTracker tracker) {
 367            removeMessages(MSG_POPUP_PREVIEW);
 368            if (mPreviewPopup.isShowing() && mPreviewText.getVisibility() == VISIBLE) {
 369                // Show right away, if it's already visible and finger is moving
 370                // around
 371                showKey(keyIndex, tracker);
 372            } else {
 373                sendMessageDelayed(obtainMessage(MSG_POPUP_PREVIEW, keyIndex, 0, tracker),
 374                        delay);
 375            }
 376        }
 377
 378        public void cancelPopupPreview() {
 379            removeMessages(MSG_POPUP_PREVIEW);
 380        }
 381
 382        public void dismissPreview(long delay) {
 383            if (mPreviewPopup.isShowing()) {
 384                sendMessageDelayed(obtainMessage(MSG_DISMISS_PREVIEW), delay);
 385            }
 386        }
 387
 388        public void cancelDismissPreview() {
 389            removeMessages(MSG_DISMISS_PREVIEW);
 390        }
 391
 392        public void startKeyRepeatTimer(long delay, int keyIndex, PointerTracker tracker) {
 393            mInKeyRepeat = true;
 394            sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, keyIndex, 0, tracker), delay);
 395        }
 396
 397        public void cancelKeyRepeatTimer() {
 398            mInKeyRepeat = false;
 399            removeMessages(MSG_REPEAT_KEY);
 400        }
 401
 402        public boolean isInKeyRepeat() {
 403            return mInKeyRepeat;
 404        }
 405
 406        public void startLongPressTimer(long delay, int keyIndex, PointerTracker tracker) {
 407            // Accessibility disables long press because users are likely to
 408            // need to pause on a key
 409            // for an unspecified duration in order to hear the key's spoken
 410            // description.
 411            if (mIsAccessibilityEnabled) {
 412                return;
 413            }
 414
 415            removeMessages(MSG_LONGPRESS_KEY);
 416            sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, keyIndex, 0, tracker), delay);
 417        }
 418
 419        public void cancelLongPressTimer() {
 420            removeMessages(MSG_LONGPRESS_KEY);
 421        }
 422
 423        public void cancelKeyTimers() {
 424            cancelKeyRepeatTimer();
 425            cancelLongPressTimer();
 426        }
 427
 428        public void cancelAllMessages() {
 429            cancelKeyTimers();
 430            cancelPopupPreview();
 431            cancelDismissPreview();
 432        }
 433    }
 434
 435    static class PointerQueue {
 436        private LinkedList<PointerTracker> mQueue = new LinkedList<PointerTracker>();
 437
 438        public void add(PointerTracker tracker) {
 439            mQueue.add(tracker);
 440        }
 441
 442        public int lastIndexOf(PointerTracker tracker) {
 443            LinkedList<PointerTracker> queue = mQueue;
 444            for (int index = queue.size() - 1; index >= 0; index--) {
 445                PointerTracker t = queue.get(index);
 446                if (t == tracker)
 447                    return index;
 448            }
 449            return -1;
 450        }
 451
 452        public void releaseAllPointersOlderThan(PointerTracker tracker, long eventTime) {
 453            LinkedList<PointerTracker> queue = mQueue;
 454            int oldestPos = 0;
 455            for (PointerTracker t = queue.get(oldestPos); t != tracker; t = queue.get(oldestPos)) {
 456                if (t.isModifier()) {
 457                    oldestPos++;
 458                } else {
 459                    t.onUpEvent(t.getLastX(), t.getLastY(), eventTime);
 460                    t.setAlreadyProcessed();
 461                    queue.remove(oldestPos);
 462                }
 463            }
 464        }
 465
 466        public void releaseAllPointersExcept(PointerTracker tracker, long eventTime) {
 467            for (PointerTracker t : mQueue) {
 468                if (t == tracker)
 469                    continue;
 470                t.onUpEvent(t.getLastX(), t.getLastY(), eventTime);
 471                t.setAlreadyProcessed();
 472            }
 473            mQueue.clear();
 474            if (tracker != null)
 475                mQueue.add(tracker);
 476        }
 477
 478        public void remove(PointerTracker tracker) {
 479            mQueue.remove(tracker);
 480        }
 481    }
 482
 483    public LatinKeyboardBaseView(Context context, AttributeSet attrs) {
 484        this(context, attrs, R.attr.keyboardViewStyle);
 485    }
 486
 487    public LatinKeyboardBaseView(Context context, AttributeSet attrs, int defStyle) {
 488        super(context, attrs, defStyle);
 489
 490        TypedArray a = context.obtainStyledAttributes(
 491                attrs, R.styleable.LatinKeyboardBaseView, defStyle, R.style.LatinKeyboardBaseView);
 492        LayoutInflater inflate =
 493                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 494        int previewLayout = 0;
 495        int keyTextSize = 0;
 496
 497        int n = a.getIndexCount();
 498
 499        for (int i = 0; i < n; i++) {
 500            int attr = a.getIndex(i);
 501
 502            switch (attr) {
 503                case R.styleable.LatinKeyboardBaseView_keyBackground:
 504                    mKeyBackground = a.getDrawable(attr);
 505                    break;
 506                case R.styleable.LatinKeyboardBaseView_keyHysteresisDistance:
 507                    mKeyHysteresisDistance = a.getDimensionPixelOffset(attr, 0);
 508                    break;
 509                case R.styleable.LatinKeyboardBaseView_verticalCorrection:
 510                    mVerticalCorrection = a.getDimensionPixelOffset(attr, 0);
 511                    break;
 512                case R.styleable.LatinKeyboardBaseView_keyPreviewLayout:
 513                    previewLayout = a.getResourceId(attr, 0);
 514                    break;
 515                case R.styleable.LatinKeyboardBaseView_keyPreviewOffset:
 516                    mPreviewOffset = a.getDimensionPixelOffset(attr, 0);
 517                    break;
 518                case R.styleable.LatinKeyboardBaseView_keyPreviewHeight:
 519                    mPreviewHeight = a.getDimensionPixelSize(attr, 80);
 520                    break;
 521                case R.styleable.LatinKeyboardBaseView_keyTextSize:
 522                    mKeyTextSize = a.getDimensionPixelSize(attr, 18);
 523                    break;
 524                case R.styleable.LatinKeyboardBaseView_keyTextColor:
 525                    mKeyTextColor = a.getColor(attr, 0xFF000000);
 526                    break;
 527                case R.styleable.LatinKeyboardBaseView_labelTextSize:
 528                    mLabelTextSize = a.getDimensionPixelSize(attr, 14);
 529                    break;
 530                case R.styleable.LatinKeyboardBaseView_popupLayout:
 531                    mPopupLayout = a.getResourceId(attr, 0);
 532                    break;
 533                case R.styleable.LatinKeyboardBaseView_shadowColor:
 534                    mShadowColor = a.getColor(attr, 0);
 535                    break;
 536                case R.styleable.LatinKeyboardBaseView_shadowRadius:
 537                    mShadowRadius = a.getFloat(attr, 0f);
 538                    break;
 539                // TODO: Use Theme
 540                // (android.R.styleable.Theme_backgroundDimAmount)
 541                case R.styleable.LatinKeyboardBaseView_backgroundDimAmount:
 542                    mBackgroundDimAmount = a.getFloat(attr, 0.5f);
 543                    break;
 544                // case android.R.styleable.
 545                case R.styleable.LatinKeyboardBaseView_keyTextStyle:
 546                    int textStyle = a.getInt(attr, 0);
 547                    switch (textStyle) {
 548                        case 0:
 549                            mKeyTextStyle = Typeface.DEFAULT;
 550                            break;
 551                        case 1:
 552                            mKeyTextStyle = Typeface.DEFAULT_BOLD;
 553                            break;
 554                        default:
 555                            mKeyTextStyle = Typeface.defaultFromStyle(textStyle);
 556                            break;
 557                    }
 558                    break;
 559                case R.styleable.LatinKeyboardBaseView_symbolColorScheme:
 560                    mSymbolColorScheme = a.getInt(attr, 0);
 561                    break;
 562            }
 563        }
 564
 565        final Resources res = getResources();
 566
 567        mPreviewPopup = new PopupWindow(context);
 568        if (previewLayout != 0) {
 569            mPreviewText = (TextView) inflate.inflate(previewLayout, null);
 570            mPreviewTextSizeLarge = (int) res.getDimension(R.dimen.key_preview_text_size_large);
 571            mPreviewPopup.setContentView(mPreviewText);
 572            mPreviewPopup.setBackgroundDrawable(null);
 573        } else {
 574            mShowPreview = false;
 575        }
 576        mPreviewPopup.setTouchable(false);
 577        mPreviewPopup.setAnimationStyle(R.style.KeyPreviewAnimation);
 578        mDelayBeforePreview = res.getInteger(R.integer.config_delay_before_preview);
 579        mDelayAfterPreview = res.getInteger(R.integer.config_delay_after_preview);
 580
 581        mMiniKeyboardParent = this;
 582        mMiniKeyboardPopup = new PopupWindow(context);
 583        mMiniKeyboardPopup.setBackgroundDrawable(null);
 584        mMiniKeyboardPopup.setAnimationStyle(R.style.MiniKeyboardAnimation);
 585
 586        mPaint = new Paint();
 587        mPaint.setAntiAlias(true);
 588        mPaint.setTextSize(keyTextSize);
 589        mPaint.setTextAlign(Align.CENTER);
 590        mPaint.setAlpha(255);
 591
 592        mPadding = new Rect(0, 0, 0, 0);
 593        mKeyBackground.getPadding(mPadding);
 594        mMiniKeyboardSlideAllowance = res.getDimension(R.dimen.mini_keyboard_slide_allowance);
 595
 596        mSegmentDetector = new SegmentDetector();
 597        mSegmentDetector.setListener(segmentListener);
 598        mSegmentDetector.updateDimensions(this);
 599
 600        mGestureDetector = new MultitouchGestureDetector(context);
 601        mGestureDetector.setListener(multitouchListener);
 602        mGestureDetector.setIsLongPressEnabled(false);
 603        mGestureDetector.setIsDoubleTapEnabled(true);
 604        mGestureDetector.setDoubleTapMinFingers(2);
 605
 606        mHasDistinctMultitouch = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO)
 607                && context.getPackageManager().hasSystemFeature(
 608                        PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
 609        mKeyRepeatInterval = res.getInteger(R.integer.config_key_repeat_interval);
 610        mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
 611    }
 612
 613    public void setOnKeyboardActionListener(OnKeyboardActionListener listener) {
 614        mKeyboardActionListener = listener;
 615        for (PointerTracker tracker : mPointerTrackers) {
 616            tracker.setOnKeyboardActionListener(listener);
 617        }
 618    }
 619
 620    /**
 621     * Returns the {@link OnKeyboardActionListener} object.
 622     *
 623     * @return the listener attached to this keyboard
 624     */
 625    protected OnKeyboardActionListener getOnKeyboardActionListener() {
 626        return mKeyboardActionListener;
 627    }
 628
 629    /**
 630     * Attaches a keyboard to this view. The keyboard can be switched at any time and the view will
 631     * re-layout itself to accommodate the keyboard.
 632     *
 633     * @see Keyboard
 634     * @see #getKeyboard()
 635     * @param keyboard the keyboard to display in this view
 636     */
 637    public void setKeyboard(Keyboard keyboard) {
 638        if (mKeyboard != null) {
 639            dismissKeyPreview();
 640        }
 641        // Remove any pending messages, except dismissing preview
 642        mHandler.cancelKeyTimers();
 643        mHandler.cancelPopupPreview();
 644        mKeyboard = keyboard;
 645        LatinImeLogger.onSetKeyboard(keyboard);
 646        mKeys = mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
 647                -getPaddingTop() + mVerticalCorrection);
 648        mKeyboardVerticalGap = (int) getResources().getDimension(R.dimen.key_bottom_gap);
 649        for (PointerTracker tracker : mPointerTrackers) {
 650            tracker.setKeyboard(mKeys, mKeyHysteresisDistance);
 651        }
 652        requestLayout();
 653        // Hint to reallocate the buffer if the size changed
 654        mKeyboardChanged = true;
 655        invalidateAllKeys();
 656        computeProximityThreshold(keyboard);
 657        mMiniKeyboardCache.clear();
 658    }
 659
 660    /**
 661     * Returns the current keyboard being displayed by this view.
 662     *
 663     * @return the currently attached keyboard
 664     * @see #setKeyboard(Keyboard)
 665     */
 666    public Keyboard getKeyboard() {
 667        return mKeyboard;
 668    }
 669
 670    /**
 671     * Return whether the device has distinct multi-touch panel.
 672     *
 673     * @return true if the device has distinct multi-touch panel.
 674     */
 675    @Override
 676    public boolean hasDistinctMultitouch() {
 677        return mHasDistinctMultitouch;
 678    }
 679
 680    /**
 681     * Enables or disables accessibility.
 682     *
 683     * @param accessibilityEnabled whether or not to enable accessibility
 684     */
 685    public void setAccessibilityEnabled(boolean accessibilityEnabled) {
 686        mIsAccessibilityEnabled = accessibilityEnabled;
 687
 688        // Propagate this change to all existing pointer trackers.
 689        for (PointerTracker pointerTracker : mPointerTrackers) {
 690            pointerTracker.setAccessibilityEnabled(accessibilityEnabled);
 691        }
 692    }
 693
 694    /**
 695     * Return whether the device has accessibility turned on.
 696     *
 697     * @return true if the device has accessibility turned on.
 698     */
 699    @Override
 700    public boolean isAccessibilityEnabled() {
 701        return mIsAccessibilityEnabled;
 702    }
 703
 704    /**
 705     * Sets the state of the shift key of the keyboard, if any.
 706     *
 707     * @param shifted whether or not to enable the state of the shift key
 708     * @return true if the shift key state changed, false if there was no change
 709     */
 710    public boolean setShifted(boolean shifted) {
 711        if (mKeyboard != null) {
 712            if (mKeyboard.setShifted(shifted)) {
 713                // The whole keyboard probably needs to be redrawn
 714                invalidateAllKeys();
 715                return true;
 716            }
 717        }
 718        return false;
 719    }
 720
 721    /**
 722     * Returns the state of the shift key of the keyboard, if any.
 723     *
 724     * @return true if the shift is in a pressed state, false otherwise. If there is no shift key on
 725     *         the keyboard or there is no keyboard attached, it returns false.
 726     */
 727    public boolean isShifted() {
 728        if (mKeyboard != null) {
 729            return mKeyboard.isShifted();
 730        }
 731        return false;
 732    }
 733
 734    /**
 735     * Enables or disables long-press.
 736     *
 737     * @param longPressEnabled whether or not to enable long-press
 738     */
 739    public void setLongPressEnabled(boolean longPressEnabled) {
 740        mGestureDetector.setIsLongPressEnabled(longPressEnabled);
 741    }
 742
 743    /**
 744     * Enables or disables the key feedback popup. This is a popup that shows a magnified version of
 745     * the depressed key. By default the preview is enabled.
 746     *
 747     * @param previewEnabled whether or not to enable the key feedback popup
 748     * @see #isPreviewEnabled()
 749     */
 750    public void setPreviewEnabled(boolean previewEnabled) {
 751        mShowPreview = previewEnabled;
 752    }
 753
 754    /**
 755     * Returns the enabled state of the key feedback popup.
 756     *
 757     * @return whether or not the key feedback popup is enabled
 758     * @see #setPreviewEnabled(boolean)
 759     */
 760    public boolean isPreviewEnabled() {
 761        return mShowPreview;
 762    }
 763
 764    public int getSymbolColorScheme() {
 765        return mSymbolColorScheme;
 766    }
 767
 768    public void setPopupParent(View v) {
 769        mMiniKeyboardParent = v;
 770    }
 771
 772    public void setPopupOffset(int x, int y) {
 773        mPopupPreviewOffsetX = x;
 774        mPopupPreviewOffsetY = y;
 775        mPreviewPopup.dismiss();
 776    }
 777
 778    /**
 779     * When enabled, calls to {@link OnKeyboardActionListener#onKey} will include key codes for
 780     * adjacent keys. When disabled, only the primary key code will be reported.
 781     *
 782     * @param enabled whether or not the proximity correction is enabled
 783     */
 784    public void setProximityCorrectionEnabled(boolean enabled) {
 785        mKeyDetector.setProximityCorrectionEnabled(enabled);
 786    }
 787
 788    /**
 789     * Returns true if proximity correction is enabled.
 790     */
 791    public boolean isProximityCorrectionEnabled() {
 792        return mKeyDetector.isProximityCorrectionEnabled();
 793    }
 794
 795    protected CharSequence adjustCase(CharSequence label) {
 796        if (mKeyboard.isShifted() && label != null && label.length() < 3
 797                && Character.isLowerCase(label.charAt(0))) {
 798            label = label.toString().toUpperCase();
 799        }
 800        return label;
 801    }
 802
 803    @Override
 804    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 805        // Round up a little
 806        if (mKeyboard == null) {
 807            setMeasuredDimension(
 808                    getPaddingLeft() + getPaddingRight(), getPaddingTop() + getPaddingBottom());
 809        } else {
 810            int width = mKeyboard.getMinWidth() + getPaddingLeft() + getPaddingRight();
 811            if (MeasureSpec.getSize(widthMeasureSpec) < width + 10) {
 812                width = MeasureSpec.getSize(widthMeasureSpec);
 813            }
 814            setMeasuredDimension(
 815                    getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
 816                    mKeyboard.getHeight() + getPaddingTop() + getPaddingBottom());
 817        }
 818    }
 819
 820    /**
 821     * Compute the average distance between adjacent keys (horizontally and vertically) and square
 822     * it to get the proximity threshold. We use a square here and in computing the touch distance
 823     * from a key's center to avoid taking a square root.
 824     *
 825     * @param keyboard
 826     */
 827    private void computeProximityThreshold(Keyboard keyboard) {
 828        if (keyboard == null)
 829            return;
 830        final Key[] keys = mKeys;
 831        if (keys == null)
 832            return;
 833        int length = keys.length;
 834        int dimensionSum = 0;
 835        for (int i = 0; i < length; i++) {
 836            Key key = keys[i];
 837            dimensionSum += Math.min(key.width, key.height + mKeyboardVerticalGap) + key.gap;
 838        }
 839        if (dimensionSum < 0 || length == 0)
 840            return;
 841        mKeyDetector.setProximityThreshold((int) (dimensionSum * 1.4f / length));
 842    }
 843
 844    @Override
 845    public void onSizeChanged(int w, int h, int oldw, int oldh) {
 846        super.onSizeChanged(w, h, oldw, oldh);
 847        // Release the buffer, if any and it will be reallocated on the next
 848        // draw
 849        mBuffer = null;
 850        mSegmentDetector.updateDimensions(this);
 851    }
 852
 853    @Override
 854    public void onDraw(Canvas canvas) {
 855        super.onDraw(canvas);
 856        if (mDrawPending || mBuffer == null || mKeyboardChanged) {
 857            onBufferDraw();
 858        }
 859        canvas.drawBitmap(mBuffer, 0, 0, null);
 860    }
 861
 862    private void onBufferDraw() {
 863        if (mBuffer == null || mKeyboardChanged) {
 864            if (mBuffer == null || mKeyboardChanged &&
 865                    (mBuffer.getWidth() != getWidth() || mBuffer.getHeight() != getHeight())) {
 866                // Make sure our bitmap is at least 1x1
 867                final int width = Math.max(1, getWidth());
 868                final int height = Math.max(1, getHeight());
 869                mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
 870                mCanvas = new Canvas(mBuffer);
 871            }
 872            invalidateAllKeys();
 873            mKeyboardChanged = false;
 874        }
 875        final Canvas canvas = mCanvas;
 876        canvas.clipRect(mDirtyRect, Op.REPLACE);
 877
 878        if (mKeyboard == null)
 879            return;
 880
 881        final Paint paint = mPaint;
 882        final Drawable keyBackground = mKeyBackground;
 883        final Rect clipRegion = mClipRegion;
 884        final Rect padding = mPadding;
 885        final int kbdPaddingLeft = getPaddingLeft();
 886        final int kbdPaddingTop = getPaddingTop();
 887        final Key[] keys = mKeys;
 888        final Key invalidKey = mInvalidatedKey;
 889
 890        paint.setColor(mKeyTextColor);
 891        boolean drawSingleKey = false;
 892        if (invalidKey != null && canvas.getClipBounds(clipRegion)) {
 893            // TODO we should use Rect.inset and Rect.contains here.
 894            // Is clipRegion completely contained within the invalidated key?
 895            if (invalidKey.x + kbdPaddingLeft - 1 <= clipRegion.left &&
 896                    invalidKey.y + kbdPaddingTop - 1 <= clipRegion.top &&
 897                    invalidKey.x + invalidKey.width + kbdPaddingLeft + 1 >= clipRegion.right &&
 898                    invalidKey.y + invalidKey.height + kbdPaddingTop + 1 >= clipRegion.bottom) {
 899                drawSingleKey = true;
 900            }
 901        }
 902        canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
 903        final int keyCount = keys.length;
 904        for (int i = 0; i < keyCount; i++) {
 905            final Key key = keys[i];
 906            if (drawSingleKey && invalidKey != key) {
 907                continue;
 908            }
 909            int[] drawableState = key.getCurrentDrawableState();
 910            keyBackground.setState(drawableState);
 911
 912            // Switch the character to uppercase if shift is pressed
 913            String label = key.label == null ? null : adjustCase(key.label).toString();
 914
 915            final Rect bounds = keyBackground.getBounds();
 916            if (key.width != bounds.right || key.height != bounds.bottom) {
 917                keyBackground.setBounds(0, 0, key.width, key.height);
 918            }
 919            canvas.translate(key.x + kbdPaddingLeft, key.y + kbdPaddingTop);
 920            keyBackground.draw(canvas);
 921
 922            boolean shouldDrawIcon = true;
 923            if (label != null) {
 924                // For characters, use large font. For labels like "Done", use
 925                // small font.
 926                final int labelSize;
 927                if (label.length() > 1 && key.codes.length < 2) {
 928                    labelSize = mLabelTextSize;
 929                    paint.setTypeface(Typeface.DEFAULT_BOLD);
 930                } else {
 931                    labelSize = mKeyTextSize;
 932                    paint.setTypeface(mKeyTextStyle);
 933                }
 934                paint.setTextSize(labelSize);
 935
 936                Integer labelHeightValue = mTextHeightCache.get(labelSize);
 937                final int labelHeight;
 938                if (labelHeightValue != null) {
 939                    labelHeight = labelHeightValue;
 940                } else {
 941                    Rect textBounds = new Rect();
 942                    paint.getTextBounds(KEY_LABEL_HEIGHT_REFERENCE_CHAR, 0, 1, textBounds);
 943                    labelHeight = textBounds.height();
 944                    mTextHeightCache.put(labelSize, labelHeight);
 945                }
 946
 947                // Draw a drop shadow for the text
 948                paint.setShadowLayer(mShadowRadius, 0, 0, mShadowColor);
 949                final int centerX = (key.width + padding.left - padding.right) / 2;
 950                final int centerY = (key.height + padding.top - padding.bottom) / 2;
 951                final float baseline = centerY
 952                        + labelHeight * KEY_LABEL_VERTICAL_ADJUSTMENT_FACTOR;
 953                canvas.drawText(label, centerX, baseline, paint);
 954                // Turn off drop shadow
 955                paint.setShadowLayer(0, 0, 0, 0);
 956
 957                // Usually don't draw icon if label is not null, but we draw
 958                // icon for the number
 959                // hint and popup hint.
 960                shouldDrawIcon = shouldDrawLabelAndIcon(key);
 961            }
 962            if (key.icon != null && shouldDrawIcon) {
 963                // Special handing for the upper-right number hint icons
 964                final int drawableWidth;
 965                final int drawableHeight;
 966                final int drawableX;
 967                final int drawableY;
 968                if (shouldDrawIconFully(key)) {
 969                    drawableWidth = key.width;
 970                    drawableHeight = key.height;
 971                    drawableX = 0;
 972                    drawableY = NUMBER_HINT_VERTICAL_ADJUSTMENT_PIXEL;
 973                } else {
 974                    drawableWidth = key.icon.getIntrinsicWidth();
 975                    drawableHeight = key.icon.getIntrinsicHeight();
 976                    drawableX = (key.width + padding.left - padding.right - drawableWidth) / 2;
 977                    drawableY = (key.height + padding.top - padding.bottom - drawableHeight) / 2;
 978                }
 979                canvas.translate(drawableX, drawableY);
 980                key.icon.setBounds(0, 0, drawableWidth, drawableHeight);
 981                key.icon.draw(canvas);
 982                canvas.translate(-drawableX, -drawableY);
 983            }
 984            canvas.translate(-key.x - kbdPaddingLeft, -key.y - kbdPaddingTop);
 985        }
 986        mInvalidatedKey = null;
 987        // Overlay a dark rectangle to dim the keyboard
 988        if (mMiniKeyboard != null) {
 989            paint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24);
 990            canvas.drawRect(0, 0, getWidth(), getHeight(), paint);
 991        }
 992
 993        if (DEBUG) {
 994            if (mShowTouchPoints) {
 995                for (PointerTracker tracker : mPointerTrackers) {
 996                    int startX = tracker.getStartX();
 997                    int startY = tracker.getStartY();
 998                    int lastX = tracker.getLastX();
 999                    int lastY = tracker.getLastY();
1000                    paint.setAlpha(128);
1001                    paint.setColor(0xFFFF0000);
1002                    canvas.drawCircle(startX, startY, 3, paint);
1003                    canvas.drawLine(startX, startY, lastX, lastY, paint);
1004                    paint.setColor(0xFF0000FF);
1005                    canvas.drawCircle(lastX, lastY, 3, paint);
1006                    paint.setColor(0xFF00FF00);
1007                    canvas.drawCircle((startX + lastX) / 2, (startY + lastY) / 2, 2, paint);
1008                }
1009            }
1010        }
1011
1012        mDrawPending = false;
1013        mDirtyRect.setEmpty();
1014    }
1015
1016    // TODO: clean up this method.
1017    private void dismissKeyPreview() {
1018        for (PointerTracker tracker : mPointerTrackers)
1019            tracker.updateKey(NOT_A_KEY);
1020        showPreview(NOT_A_KEY, null);
1021    }
1022
1023    @Override
1024    public void showPreview(int keyIndex, PointerTracker tracker) {
1025        int oldKeyIndex = mOldPreviewKeyIndex;
1026        mOldPreviewKeyIndex = keyIndex;
1027        final boolean isLanguageSwitchEnabled = (mKeyboard instanceof LatinKeyboard)
1028                && ((LatinKeyboard) mKeyboard).isLanguageSwitchEnabled();
1029        // We should re-draw popup preview when 1) we need to hide the preview,
1030        // 2) we will show
1031        // the space key preview and 3) pointer moves off the space key to other
1032        // letter key, we
1033        // should hide the preview of the previous key.
1034        final boolean hidePreviewOrShowSpaceKeyPreview = (tracker == null)
1035                || tracker.isSpaceKey(keyIndex) || tracker.isSpaceKey(oldKeyIndex);
1036        // If key changed and preview is on or the key is space (language switch
1037        // is enabled)
1038        if (oldKeyIndex != keyIndex
1039                && (mShowPreview
1040                || (hidePreviewOrShowSpaceKeyPreview && isLanguageSwitchEnabled))) {
1041            if (keyIndex == NOT_A_KEY) {
1042                mHandler.cancelPopupPreview();
1043                mHandler.dismissPreview(mDelayAfterPreview);
1044            } else if (tracker != null) {
1045                mHandler.popupPreview(mDelayBeforePreview, keyIndex, tracker);
1046            }
1047        }
1048    }
1049
1050    private void showKey(final int keyIndex, PointerTracker tracker) {
1051        Key key = tracker.getKey(keyIndex);
1052        if (key == null)
1053            return;
1054        // Should not draw hint icon in key preview
1055        if (key.icon != null && !shouldDrawLabelAndIcon(key)) {
1056            mPreviewText.setCompoundDrawables(null, null, null,
1057                    key.iconPreview != null ? key.iconPreview : key.icon);
1058            mPreviewText.setText(null);
1059        } else {
1060            mPreviewText.setCompoundDrawables(null, null, null, null);
1061            mPreviewText.setText(adjustCase(tracker.getPreviewText(key)));
1062            if (key.label.length() > 1 && key.codes.length < 2) {
1063                mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mKeyTextSize);
1064                mPreviewText.setTypeface(Typeface.DEFAULT_BOLD);
1065            } else {
1066                mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mPreviewTextSizeLarge);
1067                mPreviewText.setTypeface(mKeyTextStyle);
1068            }
1069        }
1070        mPreviewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
1071                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
1072        int popupWidth = Math.max(mPreviewText.getMeasuredWidth(), key.width
1073                + mPreviewText.getPaddingLeft() + mPreviewText.getPaddingRight());
1074        final int popupHeight = mPreviewHeight;
1075        LayoutParams lp = mPreviewText.getLayoutParams();
1076        if (lp != null) {
1077            lp.width = popupWidth;
1078            lp.height = popupHeight;
1079        }
1080
1081        int popupPreviewX = key.x - (popupWidth - key.width) / 2;
1082        int popupPreviewY = key.y - popupHeight + mPreviewOffset;
1083
1084        mHandler.cancelDismissPreview();
1085        if (mOffsetInWindow == null) {
1086            mOffsetInWindow = new int[2];
1087            getLocationInWindow(mOffsetInWindow);
1088            mOffsetInWindow[0] += mPopupPreviewOffsetX; // Offset may be zero
1089            mOffsetInWindow[1] += mPopupPreviewOffsetY; // Offset may be zero
1090            int[] windowLocation = new int[2];
1091            getLocationOnScreen(windowLocation);
1092            mWindowY = windowLocation[1];
1093        }
1094        // Set the preview background state
1095        mPreviewText.getBackground().setState(
1096                key.popupResId != 0 ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET);
1097        popupPreviewX += mOffsetInWindow[0];
1098        popupPreviewY += mOffsetInWindow[1];
1099
1100        // If the popup cannot be shown above the key, put it on the side
1101        if (popupPreviewY + mWindowY < 0) {
1102            // If the key you're pressing is on the left side of the keyboard,
1103            // show the popup on
1104            // the right, offset by enough to see at least one key to the
1105            // left/right.
1106            if (key.x + key.width <= getWidth() / 2) {
1107                popupPreviewX += (int) (key.width * 2.5);
1108            } else {
1109                popupPreviewX -= (int) (key.width * 2.5);
1110            }
1111            popupPreviewY += popupHeight;
1112        }
1113
1114        if (mPreviewPopup.isShowing()) {
1115            mPreviewPopup.update(popupPreviewX, popupPreviewY, popupWidth, popupHeight);
1116        } else {
1117            mPreviewPopup.setWidth(popupWidth);
1118            mPreviewPopup.setHeight(popupHeight);
1119            mPreviewPopup.showAtLocation(mMiniKeyboardParent, Gravity.NO_GRAVITY,
1120                    popupPreviewX, popupPreviewY);
1121        }
1122        // Record popup preview position to display mini-keyboard later at the
1123        // same positon
1124        mPopupPreviewDisplayedY = popupPreviewY;
1125        mPreviewText.setVisibility(VISIBLE);
1126    }
1127
1128    /**
1129     * Requests a redraw of the entire keyboard. Calling {@link #invalidate} is not sufficient
1130     * because the keyboard renders the keys to an off-screen buffer and an invalidate() only draws
1131     * the cached buffer.
1132     *
1133     * @see #invalidateKey(Key)
1134     */
1135    public void invalidateAllKeys() {
1136        mDirtyRect.union(0, 0, getWidth(), getHeight());
1137        mDrawPending = true;
1138        invalidate();
1139    }
1140
1141    /**
1142     * Invalidates a key so that it will be redrawn on the next repaint. Use this method if only one
1143     * key is changing it's content. Any changes that affect the position or size of the key may not
1144     * be honored.
1145     *
1146     * @param key key in the attached {@link Keyboard}.
1147     * @see #invalidateAllKeys
1148     */
1149    @Override
1150    public void invalidateKey(Key key) {
1151        if (key == null)
1152            return;
1153        mInvalidatedKey = key;
1154        // TODO we should clean up this and record key's region to use in
1155        // onBufferDraw.
1156        mDirtyRect.union(key.x + getPaddingLeft(), key.y + getPaddingTop(),
1157                key.x + key.width + getPaddingLeft(), key.y + key.height + getPaddingTop());
1158        onBufferDraw();
1159        invalidate(key.x + getPaddingLeft(), key.y + getPaddingTop(),
1160                key.x + key.width + getPaddingLeft(), key.y + key.height + getPaddingTop());
1161    }
1162
1163    private boolean openPopupIfRequired(int keyIndex, PointerTracker tracker) {
1164        // Check if we have a popup layout specified first.
1165        if (mPopupLayout == 0) {
1166            return false;
1167        }
1168
1169        Key popupKey = tracker.getKey(keyIndex);
1170        if (popupKey == null)
1171            return false;
1172        boolean result = onLongPress(popupKey);
1173        if (result) {
1174            dismissKeyPreview();
1175            mMiniKeyboardTrackerId = tracker.mPointerId;
1176            // Mark this tracker "already processed" and remove it from the
1177            // pointer queue
1178            tracker.setAlreadyProcessed();
1179            mPointerQueue.remove(tracker);
1180        }
1181        return result;
1182    }
1183
1184    private View inflateMiniKeyboardContainer(Key popupKey) {
1185        int popupKeyboardId = popupKey.popupResId;
1186        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
1187                Context.LAYOUT_INFLATER_SERVICE);
1188        View container = inflater.inflate(mPopupLayout, null);
1189        if (container == null)
1190            throw new NullPointerException();
1191
1192        LatinKeyboardBaseView miniKeyboard =
1193                (LatinKeyboardBaseView) container.findViewById(R.id.LatinKeyboardBaseView);
1194        miniKeyboard.setOnKeyboardActionListener(new OnKeyboardActionListener() {
1195            @Override
1196            public void onKey(int primaryCode, int[] keyCodes, int x, int y) {
1197                mKeyboardActionListener.onKey(primaryCode, keyCodes, x, y);
1198                dismissPopupKeyboard();
1199            }
1200
1201            @Override
1202            public void onText(CharSequence text) {
1203                mKeyboardActionListener.onText(text);
1204                dismissPopupKeyboard();
1205            }
1206
1207            @Override
1208            public void onCancel() {
1209                dismissPopupKeyboard();
1210            }
1211
1212            @Override
1213            public boolean swipeLeft(int pointerCount) {
1214                return false;
1215            }
1216
1217            @Override
1218            public boolean swipeRight(int pointerCount) {
1219                return false;
1220            }
1221
1222            @Override
1223            public boolean swipeUp(int pointerCount) {
1224                return false;
1225            }
1226
1227            @Override
1228            public boolean swipeDown(int pointerCount) {
1229                return false;
1230            }
1231
1232            @Override
1233            public boolean singleTap(int pointerCount) {
1234                return false;
1235            }
1236
1237            @Override
1238            public boolean doubleTap(int pointerCount) {
1239                return false;
1240            }
1241
1242            @Override
1243            public boolean longPress(int pointerCount) {
1244                return false;
1245            }
1246
1247            @Override
1248            public void exploreKeyboardArea() {
1249            }
1250
1251            @Override
1252            public void enteredSegment(int segment) {
1253            }
1254
1255            @Override
1256            public void selectedSegment(int segment) {
1257            }
1258
1259            @Override
1260            public void leftKeyboardArea() {
1261            }
1262
1263            @Override
1264            public void enteredKeyboardArea() {
1265            }
1266
1267            @Override
1268            public void upOutsideKeyboardArea() {
1269            }
1270
1271            @Override
1272            public void onPress(Key key) {
1273                mKeyboardActionListener.onPress(key);
1274            }
1275
1276            @Override
1277            public void onLeaving(Key key) {
1278                mKeyboardActionListener.onLeaving(key);
1279            }
1280
1281            @Override
1282            public void onRelease(Key key) {
1283                mKeyboardActionListener.onRelease(key);
1284            }
1285        });
1286        // Override default ProximityKeyDetector.
1287        miniKeyboard.mKeyDetector = new MiniKeyboardKeyDetector(mMiniKeyboardSlideAllowance);
1288        // Remove gesture detector on mini-keyboard
1289        miniKeyboard.mGestureDetector = null;
1290
1291        Keyboard keyboard;
1292        if (popupKey.popupCharacters != null) {
1293            keyboard = new Keyboard(getContext(), popupKeyboardId, popupKey.popupCharacters,
1294                    -1, getPaddingLeft() + getPaddingRight());
1295        } else {
1296            keyboard = new Keyboard(getContext(), popupKeyboardId);
1297        }
1298        miniKeyboard.setKeyboard(keyboard);
1299        miniKeyboard.setPopupParent(this);
1300
1301        container.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
1302                MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
1303
1304        return container;
1305    }
1306
1307    private static boolean isOneRowKeys(List<Key> keys) {
1308        if (keys.size() == 0)
1309            return false;
1310        final int edgeFlags = keys.get(0).edgeFlags;
1311        // HACK: The first key of mini keyboard which was inflated from xml and
1312        // has multiple rows,
1313        // does not have both top and bo

Large files files are truncated, but you can click here to view the full file