| // Copyright 2010-2014, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| package org.mozc.android.inputmethod.japanese.view; |
| |
| import org.mozc.android.inputmethod.japanese.keyboard.BackgroundDrawableFactory; |
| import org.mozc.android.inputmethod.japanese.resources.R; |
| import com.google.common.base.Optional; |
| import com.google.common.base.Preconditions; |
| |
| import android.content.res.Resources; |
| import android.graphics.Canvas; |
| import android.graphics.LinearGradient; |
| import android.graphics.Paint; |
| import android.graphics.Path; |
| import android.graphics.Path.Direction; |
| import android.graphics.Rect; |
| import android.graphics.RectF; |
| import android.graphics.Shader; |
| import android.graphics.Shader.TileMode; |
| import android.graphics.drawable.Drawable; |
| import android.graphics.drawable.LayerDrawable; |
| |
| /** |
| * Factory to produce the buttons for SymbolMajorCategories. |
| * |
| */ |
| public class SymbolMajorCategoryButtonDrawableFactory { |
| |
| private interface PathFactory { |
| Path newInstance(Rect bounds); |
| } |
| |
| private static class LeftButtonPathFactory implements PathFactory { |
| private final float padding; |
| private final float round; |
| |
| // Padding is applied to left, top and bottom. |
| // Right doesn't have padding in order to center left/right buttons. |
| LeftButtonPathFactory(float padding, float round) { |
| this.padding = padding; |
| this.round = round; |
| } |
| |
| @Override |
| public Path newInstance(Rect bounds) { |
| Preconditions.checkNotNull(bounds); |
| float left = bounds.left + padding; |
| float top = bounds.top + padding; |
| float right = bounds.right - 2; |
| float bottom = bounds.bottom - 1 - padding; |
| |
| Path path = new Path(); |
| path.moveTo(right, bottom); |
| path.arcTo(new RectF(left, bottom - round * 2, left + round * 2, bottom), 90, 90); |
| path.arcTo(new RectF(left, top, left + round + 2, top + round * 2), 180, 90); |
| path.lineTo(right, top); |
| path.close(); |
| |
| return path; |
| } |
| } |
| |
| private static class CenterButtonPathFactory implements PathFactory { |
| private final float padding; |
| |
| // Padding is applied only to top and bottom. |
| CenterButtonPathFactory(float padding) { |
| this.padding = padding; |
| } |
| |
| @Override |
| public Path newInstance(Rect bounds) { |
| Preconditions.checkNotNull(bounds); |
| float left = bounds.left; |
| float top = bounds.top + padding; |
| float right = bounds.right - 2; |
| float bottom = bounds.bottom - 1 - padding; |
| |
| Path path = new Path(); |
| path.addRect(left, top, right, bottom, Direction.CW); |
| return path; |
| } |
| } |
| |
| private static class RightButtonPathFactory implements PathFactory { |
| private final float padding; |
| private final float round; |
| |
| // Padding is applied to right, top and bottom. |
| // Left doesn't have padding in order to center left/right buttons. |
| RightButtonPathFactory(float padding, float round) { |
| this.padding = padding; |
| this.round = round; |
| } |
| |
| @Override |
| public Path newInstance(Rect bounds) { |
| Preconditions.checkNotNull(bounds); |
| float left = bounds.left; |
| float top = bounds.top + padding; |
| float right = bounds.right - 1 - padding; |
| float bottom = bounds.bottom - 1 - padding; |
| |
| Path path = new Path(); |
| path.moveTo(left, top); |
| path.arcTo(new RectF(right - round * 2, top, right, top + round * 2), 270, 90); |
| path.arcTo(new RectF(right - round * 2, bottom - round * 2, right, bottom), 0, 90); |
| path.lineTo(left, bottom); |
| path.close(); |
| |
| return path; |
| } |
| } |
| |
| private static class ButtonDrawable extends BaseBackgroundDrawable { |
| |
| private final PathFactory pathFactory; |
| private final int topColor; |
| private final int bottomColor; |
| private final int shadowColor; |
| |
| private final Paint paint = new Paint(); |
| private Optional<Path> path = Optional.absent(); |
| private Optional<Shader> shader = Optional.absent(); |
| |
| ButtonDrawable(PathFactory pathFactory, int topColor, int bottomColor, int shadowColor) { |
| super(0, 0, 0, 0); // No padding. |
| this.pathFactory = Preconditions.checkNotNull(pathFactory); |
| this.topColor = topColor; |
| this.bottomColor = bottomColor; |
| this.shadowColor = shadowColor; |
| } |
| |
| @Override |
| public void draw(Canvas canvas) { |
| if (!path.isPresent()) { |
| return; |
| } |
| |
| Paint paint = this.paint; |
| if ((shadowColor & 0xFF000000) != 0) { |
| paint.reset(); |
| paint.setAntiAlias(true); |
| paint.setColor(shadowColor); |
| int saveCount = canvas.save(); |
| try { |
| canvas.translate(1, 1); |
| canvas.drawPath(path.get(), paint); |
| } finally { |
| canvas.restoreToCount(saveCount); |
| } |
| } |
| |
| paint.reset(); |
| paint.setAntiAlias(true); |
| if (shader.isPresent()) { |
| paint.setShader(shader.get()); |
| } |
| canvas.drawPath(path.get(), paint); |
| } |
| |
| @Override |
| protected void onBoundsChange(Rect bounds) { |
| super.onBoundsChange(bounds); |
| |
| if (isCanvasRectEmpty()) { |
| path = Optional.absent(); |
| shader = Optional.absent(); |
| return; |
| } |
| |
| path = Optional.of(pathFactory.newInstance(bounds)); |
| shader = Optional.<Shader>of(new LinearGradient( |
| 0, bounds.top, 0, bounds.bottom - 1, topColor, bottomColor, TileMode.CLAMP)); |
| } |
| } |
| |
| private static class EmojiDisableIconDrawable extends BaseBackgroundDrawable { |
| |
| private final int size; |
| private final Drawable sourceDrawable; |
| |
| EmojiDisableIconDrawable(Resources resources, Drawable sourceDrawable) { |
| super(0, 0, 0, 0); |
| size = Preconditions.checkNotNull(resources).getDimensionPixelSize( |
| R.dimen.symbol_major_emoji_disable_icon_height); |
| sourceDrawable.setBounds(0, 0, size, size); |
| this.sourceDrawable = Preconditions.checkNotNull(sourceDrawable); |
| } |
| |
| @Override |
| public void draw(Canvas canvas) { |
| Rect bounds = getBounds(); |
| |
| |
| int saveCount = canvas.save(); |
| try { |
| canvas.translate(bounds.right - size - 3, bounds.bottom - size - 3); |
| canvas.clipRect(0, 0, size, size); |
| sourceDrawable.draw(canvas); |
| } finally { |
| canvas.restoreToCount(saveCount); |
| } |
| } |
| } |
| |
| private Skin skin = Skin.getFallbackInstance(); |
| |
| private final Resources resources; |
| |
| public SymbolMajorCategoryButtonDrawableFactory(Resources resources) { |
| this.resources = Preconditions.checkNotNull(resources); |
| } |
| |
| public Drawable createLeftButtonDrawable() { |
| return createSelectableDrawableWithPathFactory( |
| new LeftButtonPathFactory(skin.symbolMajorButtonPaddingDimension, |
| skin.symbolMajorButtonRoundDimension)); |
| } |
| |
| public Drawable createCenterButtonDrawable() { |
| return createSelectableDrawableWithPathFactory( |
| new CenterButtonPathFactory(skin.symbolMajorButtonPaddingDimension)); |
| } |
| |
| public Drawable createRightButtonDrawable(boolean emojiEnabled) { |
| Drawable drawable = createSelectableDrawableWithPathFactory( |
| new RightButtonPathFactory(skin.symbolMajorButtonPaddingDimension, |
| skin.symbolMajorButtonRoundDimension)); |
| if (emojiEnabled) { |
| return drawable; |
| } |
| return new LayerDrawable(new Drawable[] { |
| drawable, |
| new EmojiDisableIconDrawable( |
| resources, skin.getDrawable(resources, R.raw.emoji_disable_icon)), |
| }); |
| } |
| |
| private Drawable createSelectableDrawableWithPathFactory(PathFactory pathFactory) { |
| return BackgroundDrawableFactory.createSelectableDrawable( |
| new ButtonDrawable(pathFactory, |
| skin.symbolMajorButtonSelectedTopColor, |
| skin.symbolMajorButtonSelectedBottomColor, 0), |
| Optional.<Drawable>of(BackgroundDrawableFactory.createPressableDrawable( |
| new ButtonDrawable(pathFactory, |
| skin.symbolMajorButtonPressedTopColor, |
| skin.symbolMajorButtonPressedBottomColor, 0), |
| Optional.<Drawable>of(new ButtonDrawable(pathFactory, |
| skin.symbolMajorButtonTopColor, |
| skin.symbolMajorButtonBottomColor, |
| skin.symbolMajorButtonShadowColor))))); |
| } |
| |
| public void setSkin(Skin skin) { |
| this.skin = Preconditions.checkNotNull(skin); |
| } |
| } |