ShadowKeyDrawable.java revision eb5ce83cb400549dea09458e6e3908fae4457fe4
1eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng/* 2eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng * Copyright (C) 2018 The Android Open Source Project 3eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng * 4eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng * Licensed under the Apache License, Version 2.0 (the "License"); 5eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng * you may not use this file except in compliance with the License. 6eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng * You may obtain a copy of the License at 7eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng * 8eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng * http://www.apache.org/licenses/LICENSE-2.0 9eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng * 10eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng * Unless required by applicable law or agreed to in writing, software 11eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng * distributed under the License is distributed on an "AS IS" BASIS, 12eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng * See the License for the specific language governing permissions and 14eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng * limitations under the License 15eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng */ 16eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 17eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ngpackage com.android.systemui.statusbar.phone; 18eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 19eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 20eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ngimport android.graphics.Bitmap; 21eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ngimport android.graphics.BlurMaskFilter; 22eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ngimport android.graphics.BlurMaskFilter.Blur; 23eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ngimport android.graphics.Canvas; 24eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ngimport android.graphics.Color; 25eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ngimport android.graphics.ColorFilter; 26eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ngimport android.graphics.Paint; 27eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ngimport android.graphics.PixelFormat; 28eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ngimport android.graphics.Rect; 29eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ngimport android.graphics.drawable.Drawable; 30eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 31eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ngimport com.android.systemui.R; 32eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 33eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng/** 34eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng * A drawable which adds shadow around a child drawable. 35eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng */ 36eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ngpublic class ShadowKeyDrawable extends Drawable { 37eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); 38eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 39eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng private final ShadowDrawableState mState; 40eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 41eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng public ShadowKeyDrawable(Drawable d) { 42eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng this(d, new ShadowDrawableState()); 43eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 44eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 45eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng private ShadowKeyDrawable(Drawable d, ShadowDrawableState state) { 46eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng mState = state; 47eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng if (d != null) { 48eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng mState.mBaseHeight = d.getIntrinsicHeight(); 49eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng mState.mBaseWidth = d.getIntrinsicWidth(); 50eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng mState.mChangingConfigurations = d.getChangingConfigurations(); 51eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng mState.mChildState = d.getConstantState(); 52eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 53eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 54eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 55eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng public void setRotation(float degrees) { 56eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng if (mState.mRotateDegrees != degrees) { 57eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng mState.mRotateDegrees = degrees; 58eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng mState.mLastDrawnBitmap = null; 59eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng invalidateSelf(); 60eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 61eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 62eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 63eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng public void setShadowProperties(int x, int y, int size, int color) { 64eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng if (mState.mShadowOffsetX != x || mState.mShadowOffsetY != y 65eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng || mState.mShadowSize != size || mState.mShadowColor != color) { 66eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng mState.mShadowOffsetX = x; 67eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng mState.mShadowOffsetY = y; 68eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng mState.mShadowSize = size; 69eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng mState.mShadowColor = color; 70eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng mState.mLastDrawnBitmap = null; 71eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng invalidateSelf(); 72eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 73eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 74eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 75eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng public float getRotation() { 76eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng return mState.mRotateDegrees; 77eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 78eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 79eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng @Override 80eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng public void draw(Canvas canvas) { 81eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng Rect bounds = getBounds(); 82eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng if (bounds.isEmpty()) { 83eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng return; 84eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 85eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng if (mState.mLastDrawnBitmap == null) { 86eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng regenerateBitmapCache(); 87eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 88eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng canvas.drawBitmap(mState.mLastDrawnBitmap, null, bounds, mPaint); 89eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 90eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 91eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng @Override 92eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng public void setTint(int tintColor) { 93eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng super.setTint(tintColor); 94eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 95eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 96eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng @Override 97eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng public void setAlpha(int alpha) { 98eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng mPaint.setAlpha(alpha); 99eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng invalidateSelf(); 100eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 101eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 102eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng @Override 103eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng public void setColorFilter(ColorFilter colorFilter) { 104eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng mPaint.setColorFilter(colorFilter); 105eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng invalidateSelf(); 106eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 107eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 108eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng @Override 109eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng public ConstantState getConstantState() { 110eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng return mState; 111eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 112eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 113eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng @Override 114eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng public int getOpacity() { 115eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng return PixelFormat.TRANSLUCENT; 116eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 117eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 118eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng @Override 119eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng public int getIntrinsicHeight() { 120eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng return mState.mBaseHeight; 121eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 122eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 123eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng @Override 124eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng public int getIntrinsicWidth() { 125eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng return mState.mBaseWidth; 126eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 127eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 128eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng @Override 129eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng public boolean canApplyTheme() { 130eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng return mState.canApplyTheme(); 131eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 132eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 133eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng private void regenerateBitmapCache() { 134eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng final int width = getIntrinsicWidth(); 135eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng final int height = getIntrinsicHeight(); 136eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 137eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng Canvas canvas = new Canvas(bitmap); 138eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng canvas.save(); 139eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng final float radians = (float) (mState.mRotateDegrees * Math.PI / 180); 140eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 141eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng // Rotate canvas before drawing original drawable if no shadow 142eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng if (mState.mShadowSize == 0) { 143eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng canvas.rotate(mState.mRotateDegrees, width / 2, height / 2); 144eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 145eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 146eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng // Call mutate, so that the pixel allocation by the underlying vector drawable is cleared. 147eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng final Drawable d = mState.mChildState.newDrawable().mutate(); 148eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng d.setBounds(0, 0, mState.mBaseWidth, mState.mBaseHeight); 149eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng d.draw(canvas); 150eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 151eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng if (mState.mShadowSize > 0) { 152eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng // Draws the shadow 153eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); 154eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng paint.setMaskFilter(new BlurMaskFilter(mState.mShadowSize, Blur.NORMAL)); 155eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng int[] offset = new int[2]; 156eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 157eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng final Bitmap shadow = bitmap.extractAlpha(paint, offset); 158eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 159eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng paint.setMaskFilter(null); 160eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng paint.setColor(mState.mShadowColor); 161eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng bitmap.eraseColor(Color.TRANSPARENT); 162eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 163eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng canvas.rotate(mState.mRotateDegrees, width / 2, height / 2); 164eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 165eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng final float shadowOffsetX = (float) (Math.sin(radians) * mState.mShadowOffsetY 166eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng + Math.cos(radians) * mState.mShadowOffsetX); 167eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng final float shadowOffsetY = (float) (Math.cos(radians) * mState.mShadowOffsetY 168eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng - Math.sin(radians) * mState.mShadowOffsetX); 169eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 170eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng canvas.drawBitmap(shadow, offset[0] + shadowOffsetX, offset[1] + shadowOffsetY, paint); 171eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng d.draw(canvas); 172eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 173eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 174eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng bitmap = bitmap.copy(Bitmap.Config.HARDWARE, false); 175eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng mState.mLastDrawnBitmap = bitmap; 176eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng canvas.restore(); 177eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 178eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 179eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng private static class ShadowDrawableState extends ConstantState { 180eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng int mChangingConfigurations; 181eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng int mBaseWidth; 182eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng int mBaseHeight; 183eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng float mRotateDegrees; 184eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng int mShadowOffsetX; 185eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng int mShadowOffsetY; 186eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng int mShadowSize; 187eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng int mShadowColor; 188eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 189eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng Bitmap mLastDrawnBitmap; 190eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng ConstantState mChildState; 191eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 192eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng @Override 193eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng public Drawable newDrawable() { 194eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng return new ShadowKeyDrawable(null, this); 195eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 196eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 197eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng @Override 198eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng public int getChangingConfigurations() { 199eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng return mChangingConfigurations; 200eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 201eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng 202eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng @Override 203eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng public boolean canApplyTheme() { 204eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng return true; 205eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 206eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng } 207eb5ce83cb400549dea09458e6e3908fae4457fe4Matthew Ng} 208