1 /* 2 * Copyright (C) 2008 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 17 package com.android.launcher3; 18 19 import android.animation.ObjectAnimator; 20 import android.animation.TimeInterpolator; 21 import android.graphics.Bitmap; 22 import android.graphics.Canvas; 23 import android.graphics.Color; 24 import android.graphics.ColorFilter; 25 import android.graphics.ColorMatrix; 26 import android.graphics.ColorMatrixColorFilter; 27 import android.graphics.Paint; 28 import android.graphics.PixelFormat; 29 import android.graphics.PorterDuff; 30 import android.graphics.PorterDuffColorFilter; 31 import android.graphics.Rect; 32 import android.graphics.drawable.Drawable; 33 import android.util.SparseArray; 34 35 public class FastBitmapDrawable extends Drawable { 36 37 static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = new TimeInterpolator() { 38 39 @Override 40 public float getInterpolation(float input) { 41 if (input < 0.05f) { 42 return input / 0.05f; 43 } else if (input < 0.3f){ 44 return 1; 45 } else { 46 return (1 - input) / 0.7f; 47 } 48 } 49 }; 50 static final long CLICK_FEEDBACK_DURATION = 2000; 51 52 private static final int PRESSED_BRIGHTNESS = 100; 53 private static ColorMatrix sGhostModeMatrix; 54 private static final ColorMatrix sTempMatrix = new ColorMatrix(); 55 56 /** 57 * Store the brightness colors filters to optimize animations during icon press. This 58 * only works for non-ghost-mode icons. 59 */ 60 private static final SparseArray<ColorFilter> sCachedBrightnessFilter = 61 new SparseArray<ColorFilter>(); 62 63 private static final int GHOST_MODE_MIN_COLOR_RANGE = 130; 64 65 private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); 66 private final Bitmap mBitmap; 67 private int mAlpha; 68 69 private int mBrightness = 0; 70 private boolean mGhostModeEnabled = false; 71 72 private boolean mPressed = false; 73 private ObjectAnimator mPressedAnimator; 74 FastBitmapDrawable(Bitmap b)75 public FastBitmapDrawable(Bitmap b) { 76 mAlpha = 255; 77 mBitmap = b; 78 setBounds(0, 0, b.getWidth(), b.getHeight()); 79 } 80 81 @Override draw(Canvas canvas)82 public void draw(Canvas canvas) { 83 final Rect r = getBounds(); 84 // Draw the bitmap into the bounding rect 85 canvas.drawBitmap(mBitmap, null, r, mPaint); 86 } 87 88 @Override setColorFilter(ColorFilter cf)89 public void setColorFilter(ColorFilter cf) { 90 // No op 91 } 92 93 @Override getOpacity()94 public int getOpacity() { 95 return PixelFormat.TRANSLUCENT; 96 } 97 98 @Override setAlpha(int alpha)99 public void setAlpha(int alpha) { 100 mAlpha = alpha; 101 mPaint.setAlpha(alpha); 102 } 103 104 @Override setFilterBitmap(boolean filterBitmap)105 public void setFilterBitmap(boolean filterBitmap) { 106 mPaint.setFilterBitmap(filterBitmap); 107 mPaint.setAntiAlias(filterBitmap); 108 } 109 getAlpha()110 public int getAlpha() { 111 return mAlpha; 112 } 113 114 @Override getIntrinsicWidth()115 public int getIntrinsicWidth() { 116 return mBitmap.getWidth(); 117 } 118 119 @Override getIntrinsicHeight()120 public int getIntrinsicHeight() { 121 return mBitmap.getHeight(); 122 } 123 124 @Override getMinimumWidth()125 public int getMinimumWidth() { 126 return getBounds().width(); 127 } 128 129 @Override getMinimumHeight()130 public int getMinimumHeight() { 131 return getBounds().height(); 132 } 133 getBitmap()134 public Bitmap getBitmap() { 135 return mBitmap; 136 } 137 138 /** 139 * When enabled, the icon is grayed out and the contrast is increased to give it a 'ghost' 140 * appearance. 141 */ setGhostModeEnabled(boolean enabled)142 public void setGhostModeEnabled(boolean enabled) { 143 if (mGhostModeEnabled != enabled) { 144 mGhostModeEnabled = enabled; 145 updateFilter(); 146 } 147 } 148 setPressed(boolean pressed)149 public void setPressed(boolean pressed) { 150 if (mPressed != pressed) { 151 mPressed = pressed; 152 if (mPressed) { 153 mPressedAnimator = ObjectAnimator 154 .ofInt(this, "brightness", PRESSED_BRIGHTNESS) 155 .setDuration(CLICK_FEEDBACK_DURATION); 156 mPressedAnimator.setInterpolator(CLICK_FEEDBACK_INTERPOLATOR); 157 mPressedAnimator.start(); 158 } else if (mPressedAnimator != null) { 159 mPressedAnimator.cancel(); 160 setBrightness(0); 161 } 162 } 163 invalidateSelf(); 164 } 165 isGhostModeEnabled()166 public boolean isGhostModeEnabled() { 167 return mGhostModeEnabled; 168 } 169 getBrightness()170 public int getBrightness() { 171 return mBrightness; 172 } 173 setBrightness(int brightness)174 public void setBrightness(int brightness) { 175 if (mBrightness != brightness) { 176 mBrightness = brightness; 177 updateFilter(); 178 invalidateSelf(); 179 } 180 } 181 updateFilter()182 private void updateFilter() { 183 if (mGhostModeEnabled) { 184 if (sGhostModeMatrix == null) { 185 sGhostModeMatrix = new ColorMatrix(); 186 sGhostModeMatrix.setSaturation(0); 187 188 // For ghost mode, set the color range to [GHOST_MODE_MIN_COLOR_RANGE, 255] 189 float range = (255 - GHOST_MODE_MIN_COLOR_RANGE) / 255.0f; 190 sTempMatrix.set(new float[] { 191 range, 0, 0, 0, GHOST_MODE_MIN_COLOR_RANGE, 192 0, range, 0, 0, GHOST_MODE_MIN_COLOR_RANGE, 193 0, 0, range, 0, GHOST_MODE_MIN_COLOR_RANGE, 194 0, 0, 0, 1, 0 }); 195 sGhostModeMatrix.preConcat(sTempMatrix); 196 } 197 198 if (mBrightness == 0) { 199 mPaint.setColorFilter(new ColorMatrixColorFilter(sGhostModeMatrix)); 200 } else { 201 setBrightnessMatrix(sTempMatrix, mBrightness); 202 sTempMatrix.postConcat(sGhostModeMatrix); 203 mPaint.setColorFilter(new ColorMatrixColorFilter(sTempMatrix)); 204 } 205 } else if (mBrightness != 0) { 206 ColorFilter filter = sCachedBrightnessFilter.get(mBrightness); 207 if (filter == null) { 208 filter = new PorterDuffColorFilter(Color.argb(mBrightness, 255, 255, 255), 209 PorterDuff.Mode.SRC_ATOP); 210 sCachedBrightnessFilter.put(mBrightness, filter); 211 } 212 mPaint.setColorFilter(filter); 213 } else { 214 mPaint.setColorFilter(null); 215 } 216 } 217 setBrightnessMatrix(ColorMatrix matrix, int brightness)218 private static void setBrightnessMatrix(ColorMatrix matrix, int brightness) { 219 // Brightness: C-new = C-old*(1-amount) + amount 220 float scale = 1 - brightness / 255.0f; 221 matrix.setScale(scale, scale, scale, 1); 222 float[] array = matrix.getArray(); 223 224 // Add the amount to RGB components of the matrix, as per the above formula. 225 // Fifth elements in the array correspond to the constant being added to 226 // red, blue, green, and alpha channel respectively. 227 array[4] = brightness; 228 array[9] = brightness; 229 array[14] = brightness; 230 } 231 } 232