content: /*
   * Copyright (C) 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.
   */
  
  package com.amnesica.kryptey.inputmethod.keyboard;
  
  import android.util.SparseArray;
  
  import com.amnesica.kryptey.inputmethod.keyboard.internal.KeyVisualAttributes;
  import com.amnesica.kryptey.inputmethod.keyboard.internal.KeyboardIconsSet;
  import com.amnesica.kryptey.inputmethod.keyboard.internal.KeyboardParams;
  import com.amnesica.kryptey.inputmethod.latin.common.Constants;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  
  /**
   * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard
   * consists of rows of keys.
   * <p>The layout file for a keyboard contains XML that looks like the following snippet:</p>
   * <pre>
   * &lt;Keyboard
   *         latin:keyWidth="10%p"
   *         latin:rowHeight="50px"
   *         latin:horizontalGap="2%p"
   *         latin:verticalGap="2%p" &gt;
   *     &lt;Row latin:keyWidth="10%p" &gt;
   *         &lt;Key latin:keyLabel="A" /&gt;
   *         ...
   *     &lt;/Row&gt;
   *     ...
   * &lt;/Keyboard&gt;
   * </pre>
   */
  public class Keyboard {
    public final KeyboardId mId;
  
    /**
     * Total height of the keyboard, including the padding and keys
     */
    public final int mOccupiedHeight;
    /**
     * Total width of the keyboard, including the padding and keys
     */
    public final int mOccupiedWidth;
  
    /**
     * The padding below the keyboard
     */
    public final float mBottomPadding;
    /**
     * The padding above the keyboard
     */
    public final float mTopPadding;
    /**
     * Default gap between rows
     */
    public final float mVerticalGap;
    /**
     * Default gap between columns
     */
    public final float mHorizontalGap;
  
    /**
     * Per keyboard key visual parameters
     */
    public final KeyVisualAttributes mKeyVisualAttributes;
  
    public final int mMostCommonKeyHeight;
    public final int mMostCommonKeyWidth;
  
    /**
     * More keys keyboard template
     */
    public final int mMoreKeysTemplate;
  
    /**
     * List of keys in this keyboard
     */
    private final List<Key> mSortedKeys;
    public final List<Key> mShiftKeys;
    public final List<Key> mAltCodeKeysWhileTyping;
    public final KeyboardIconsSet mIconsSet;
  
    private final SparseArray<Key> mKeyCache = new SparseArray<>();
  
    private final ProximityInfo mProximityInfo;
  
    public Keyboard(final KeyboardParams params) {
      mId = params.mId;
      mOccupiedHeight = params.mOccupiedHeight;
      mOccupiedWidth = params.mOccupiedWidth;
      mMostCommonKeyHeight = params.mMostCommonKeyHeight;
      mMostCommonKeyWidth = params.mMostCommonKeyWidth;
      mMoreKeysTemplate = params.mMoreKeysTemplate;
      mKeyVisualAttributes = params.mKeyVisualAttributes;
      mBottomPadding = params.mBottomPadding;
      mTopPadding = params.mTopPadding;
      mVerticalGap = params.mVerticalGap;
      mHorizontalGap = params.mHorizontalGap;
  
      mSortedKeys = Collections.unmodifiableList(new ArrayList<>(params.mSortedKeys));
      mShiftKeys = Collections.unmodifiableList(params.mShiftKeys);
      mAltCodeKeysWhileTyping = Collections.unmodifiableList(params.mAltCodeKeysWhileTyping);
      mIconsSet = params.mIconsSet;
  
      mProximityInfo = new ProximityInfo(params.mGridWidth, params.mGridHeight,
          mOccupiedWidth, mOccupiedHeight, mSortedKeys);
    }
  
    /**
     * Return the sorted list of keys of this keyboard.
     * The keys are sorted from top-left to bottom-right order.
     * The list may contain {@link Key.Spacer} object as well.
     *
     * @return the sorted unmodifiable list of {@link Key}s of this keyboard.
     */
    public List<Key> getSortedKeys() {
      return mSortedKeys;
    }
  
    public Key getKey(final int code) {
      if (code == Constants.CODE_UNSPECIFIED) {
        return null;
      }
      synchronized (mKeyCache) {
        final int index = mKeyCache.indexOfKey(code);
        if (index >= 0) {
          return mKeyCache.valueAt(index);
        }
  
        for (final Key key : getSortedKeys()) {
          if (key.getCode() == code) {
            mKeyCache.put(code, key);
            return key;
          }
        }
        mKeyCache.put(code, null);
        return null;
      }
    }
  
    public boolean hasKey(final Key aKey) {
      if (mKeyCache.indexOfValue(aKey) >= 0) {
        return true;
      }
  
      for (final Key key : getSortedKeys()) {
        if (key == aKey) {
          mKeyCache.put(key.getCode(), key);
          return true;
        }
      }
      return false;
    }
  
    @Override
    public String toString() {
      return mId.toString();
    }
  
    /**
     * Returns the array of the keys that are closest to the given point.
     *
     * @param x the x-coordinate of the point
     * @param y the y-coordinate of the point
     * @return the list of the nearest keys to the given point. If the given
     * point is out of range, then an array of size zero is returned.
     */
    public List<Key> getNearestKeys(final int x, final int y) {
      // Avoid dead pixels at edges of the keyboard
      final int adjustedX = Math.max(0, Math.min(x, mOccupiedWidth - 1));
      final int adjustedY = Math.max(0, Math.min(y, mOccupiedHeight - 1));
      return mProximityInfo.getNearestKeys(adjustedX, adjustedY);
    }
  }
  