@SuppressWarnings("ReferenceEquality")
  @Override
  public void setKeyboardTheme(@NonNull KeyboardTheme theme) {
    if (theme == mLastSetTheme) return;

    clearKeyIconsCache(true);
    mKeysIconBuilders.clear();
    mTextWidthCache.clear();
    mLastSetTheme = theme;
    if (mKeyboard != null) setWillNotDraw(false);

    // the new theme might be of a different size
    requestLayout();
    // Hint to reallocate the buffer if the size changed
    mKeyboardChanged = true;
    invalidateAllKeys();

    final int keyboardThemeStyleResId = getKeyboardStyleResId(theme);

    final int[] remoteKeyboardThemeStyleable =
        theme
            .getResourceMapping()
            .getRemoteStyleableArrayFromLocal(R.styleable.AnyKeyboardViewTheme);
    final int[] remoteKeyboardIconsThemeStyleable =
        theme
            .getResourceMapping()
            .getRemoteStyleableArrayFromLocal(R.styleable.AnyKeyboardViewIconsTheme);

    final int[] padding = new int[] {0, 0, 0, 0};

    int keyTypeFunctionAttrId = R.attr.key_type_function;
    int keyActionAttrId = R.attr.key_type_action;
    int keyActionTypeDoneAttrId = R.attr.action_done;
    int keyActionTypeSearchAttrId = R.attr.action_search;
    int keyActionTypeGoAttrId = R.attr.action_go;

    HashSet<Integer> doneLocalAttributeIds = new HashSet<>();
    TypedArray a =
        theme
            .getPackageContext()
            .obtainStyledAttributes(keyboardThemeStyleResId, remoteKeyboardThemeStyleable);
    final int n = a.getIndexCount();
    for (int i = 0; i < n; i++) {
      final int remoteIndex = a.getIndex(i);
      final int localAttrId =
          theme.getResourceMapping().getLocalAttrId(remoteKeyboardThemeStyleable[remoteIndex]);

      if (setValueFromThemeInternal(a, padding, localAttrId, remoteIndex)) {
        doneLocalAttributeIds.add(localAttrId);
        if (localAttrId == R.attr.keyBackground) {
          // keyTypeFunctionAttrId and keyActionAttrId are remote
          final int[] keyStateAttributes =
              theme.getResourceMapping().getRemoteStyleableArrayFromLocal(KEY_TYPES);
          keyTypeFunctionAttrId = keyStateAttributes[0];
          keyActionAttrId = keyStateAttributes[1];
        }
      }
    }
    a.recycle();
    // taking icons
    int iconSetStyleRes = getKeyboardIconsStyleResId(theme);
    if (iconSetStyleRes != 0) {
      a =
          theme
              .getPackageContext()
              .obtainStyledAttributes(iconSetStyleRes, remoteKeyboardIconsThemeStyleable);
      final int iconsCount = a.getIndexCount();
      for (int i = 0; i < iconsCount; i++) {
        final int remoteIndex = a.getIndex(i);
        final int localAttrId =
            theme
                .getResourceMapping()
                .getLocalAttrId(remoteKeyboardIconsThemeStyleable[remoteIndex]);

        if (setKeyIconValueFromTheme(theme, a, localAttrId, remoteIndex)) {
          doneLocalAttributeIds.add(localAttrId);
          if (localAttrId == R.attr.iconKeyAction) {
            // keyActionTypeDoneAttrId and keyActionTypeSearchAttrId and
            // keyActionTypeGoAttrId are remote
            final int[] keyStateAttributes =
                theme.getResourceMapping().getRemoteStyleableArrayFromLocal(ACTION_KEY_TYPES);
            keyActionTypeDoneAttrId = keyStateAttributes[0];
            keyActionTypeSearchAttrId = keyStateAttributes[1];
            keyActionTypeGoAttrId = keyStateAttributes[2];
          }
        }
      }
      a.recycle();
    }
    // filling what's missing
    KeyboardTheme fallbackTheme = getKeyboardThemeFactory(getContext()).getFallbackTheme();
    final int keyboardFallbackThemeStyleResId = getKeyboardStyleResId(fallbackTheme);
    a =
        fallbackTheme
            .getPackageContext()
            .obtainStyledAttributes(
                keyboardFallbackThemeStyleResId, R.styleable.AnyKeyboardViewTheme);

    final int fallbackCount = a.getIndexCount();
    for (int i = 0; i < fallbackCount; i++) {
      final int index = a.getIndex(i);
      final int attrId = R.styleable.AnyKeyboardViewTheme[index];
      if (doneLocalAttributeIds.contains(attrId)) {
        continue;
      }
      setValueFromThemeInternal(a, padding, attrId, index);
    }
    a.recycle();
    // taking missing icons
    int fallbackIconSetStyleId = fallbackTheme.getIconsThemeResId();
    a =
        fallbackTheme
            .getPackageContext()
            .obtainStyledAttributes(fallbackIconSetStyleId, R.styleable.AnyKeyboardViewIconsTheme);

    final int fallbackIconsCount = a.getIndexCount();
    for (int i = 0; i < fallbackIconsCount; i++) {
      final int index = a.getIndex(i);
      final int attrId = R.styleable.AnyKeyboardViewIconsTheme[index];
      if (doneLocalAttributeIds.contains(attrId)) {
        continue;
      }
      setKeyIconValueFromTheme(fallbackTheme, a, attrId, index);
    }
    a.recycle();
    // creating the key-drawable state provider, as we suppose to have the entire data now
    mDrawableStatesProvider =
        new KeyDrawableStateProvider(
            keyTypeFunctionAttrId,
            keyActionAttrId,
            keyActionTypeDoneAttrId,
            keyActionTypeSearchAttrId,
            keyActionTypeGoAttrId);

    // settings.
    // don't forget that there are THREE padding,
    // the theme's and the
    // background image's padding and the
    // View
    Drawable keyboardBackground = super.getBackground();
    if (keyboardBackground != null) {
      Rect backgroundPadding = new Rect();
      keyboardBackground.getPadding(backgroundPadding);
      padding[0] += backgroundPadding.left;
      padding[1] += backgroundPadding.top;
      padding[2] += backgroundPadding.right;
      padding[3] += backgroundPadding.bottom;
    }
    setPadding(padding[0], padding[1], padding[2], padding[3]);

    final Resources res = getResources();
    final int viewWidth = (getWidth() > 0) ? getWidth() : res.getDisplayMetrics().widthPixels;
    mKeyboardDimens.setKeyboardMaxWidth(viewWidth - padding[0] - padding[2]);
    mPaint.setTextSize(mKeyTextSize);
  }
@Test
  public void testDoesNotCrashWhenSettingTheme() {
    final KeyboardThemeFactory keyboardThemeFactory =
        AnyApplication.getKeyboardThemeFactory(getApplicationContext());
    mUnderTest.setKeyboardTheme(keyboardThemeFactory.getAllAddOns().get(2));
    mUnderTest.setKeyboardTheme(keyboardThemeFactory.getAllAddOns().get(5));
    mUnderTest.setKeyboardTheme(keyboardThemeFactory.getAllAddOns().get(1));
  }