1427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu/* 2427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * Copyright (C) 2014 The Android Open Source Project 3427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * 4427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * Licensed under the Apache License, Version 2.0 (the "License"); 5427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * you may not use this file except in compliance with the License. 6427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * You may obtain a copy of the License at 7427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * 8427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * http://www.apache.org/licenses/LICENSE-2.0 9427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * 10427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * Unless required by applicable law or agreed to in writing, software 11427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * distributed under the License is distributed on an "AS IS" BASIS, 12427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * See the License for the specific language governing permissions and 14427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * limitations under the License. 15427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu */ 16427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 17427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hupackage com.android.camera.widget; 18427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 19427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Huimport android.animation.Animator; 20427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Huimport android.animation.AnimatorListenerAdapter; 21427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Huimport android.animation.AnimatorSet; 22427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Huimport android.animation.ValueAnimator; 23427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Huimport android.content.Context; 24427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Huimport android.graphics.Bitmap; 25427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Huimport android.graphics.BitmapShader; 26427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Huimport android.graphics.Canvas; 27427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Huimport android.graphics.Color; 28427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Huimport android.graphics.Matrix; 29427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Huimport android.graphics.Paint; 30427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Huimport android.graphics.RectF; 31427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Huimport android.graphics.Shader; 32427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Huimport android.util.AttributeSet; 33427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Huimport android.view.View; 34427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Huimport android.view.animation.AccelerateDecelerateInterpolator; 35427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Huimport android.view.animation.AnimationUtils; 36427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Huimport android.view.animation.Interpolator; 37427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 3830eda38142fd279e0f10796e39f469d97f2a5b43Senpo Huimport com.android.camera.async.MainThread; 39427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Huimport com.android.camera.debug.Log; 4054ac03ba6ae3e739df74c6d9e35fda9017be07d7Senpo Huimport com.android.camera.ui.motion.InterpolatorHelper; 41427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Huimport com.android.camera.util.ApiHelper; 42427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Huimport com.android.camera2.R; 4330eda38142fd279e0f10796e39f469d97f2a5b43Senpo Huimport com.google.common.base.Optional; 4430eda38142fd279e0f10796e39f469d97f2a5b43Senpo Hu 45427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu/** 46427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * A view that shows a pop-out effect for a thumbnail image as the new capture indicator design for 47427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * Haleakala. When a photo is taken, this view will appear in the bottom right corner of the view 48427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * finder to indicate the capture is done. 49427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * 50427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * Thumbnail cropping: 51427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * (1) 100% width and vertically centered for portrait. 52427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * (2) 100% height and horizontally centered for landscape. 53427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * 54427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * General behavior spec: Hide the capture indicator by fading out using fast_out_linear_in (150ms): 55427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * (1) User open filmstrip. 56427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * (2) User switch module. 57427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * (3) User switch front/back camera. 58427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * (4) User close app. 59427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * 60427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * Visual spec: 61427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * (1) A 12dp spacing between mode option overlay and thumbnail. 62427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * (2) A circular mask that excludes the corners of the preview image. 63427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * (3) A solid white layer that sits on top of the preview and is also masked by 2). 64427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * (4) The preview thumbnail image. 65427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * (5) A 'ripple' which is just a white circular stroke. 66427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * 67427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * Animation spec: 68427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * - For (2) only the scale animates, from 50%(24dp) to 114%(54dp) in 200ms then falls back to 69427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * 100%(48dp) in 200ms. Both steps use the same easing: fast_out_slow_in. 70427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * - For (3), change opacity from 50% to 0% over 150ms, easing is exponential. 71427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * - For (4), doesn't animate. 72427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * - For (5), starts animating after 100ms, when (1) is at its peak radius and all animations take 73427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * 200ms, using linear_out_slow in. Opacity goes from 40% to 0%, radius goes from 40dp to 70dp, 74427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * stroke width goes from 5dp to 1dp. 75427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu */ 76427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hupublic class RoundedThumbnailView extends View { 77427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private static final Log.Tag TAG = new Log.Tag("RoundedThumbnailView"); 78427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 79ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling // Configurations for the thumbnail pop-out effect. 80427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private static final long THUMBNAIL_STRETCH_DURATION_MS = 200; 81427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private static final long THUMBNAIL_SHRINK_DURATION_MS = 200; 82427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private static final float THUMBNAIL_REVEAL_CIRCLE_OPACITY_BEGIN = 0.5f; 83427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private static final float THUMBNAIL_REVEAL_CIRCLE_OPACITY_END = 0.0f; 84ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling 85ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling // Configurations for the ripple effect. 86427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private static final long RIPPLE_DURATION_MS = 200; 87427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private static final float RIPPLE_OPACITY_BEGIN = 0.4f; 88427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private static final float RIPPLE_OPACITY_END = 0.0f; 89ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling 90ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling // Configurations for the hit-state effect. 91cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu private static final float HIT_STATE_CIRCLE_OPACITY_HIDDEN = -1.0f; 92cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu private static final float HIT_STATE_CIRCLE_OPACITY_BEGIN = 0.7f; 93cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu private static final float HIT_STATE_CIRCLE_OPACITY_END = 0.0f; 94cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu private static final long HIT_STATE_DURATION_MS = 150; 95cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu 96ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** Defines call events. */ 97cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu public interface Callback { 98cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu public void onHitStateFinished(); 99cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu } 100cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu 101cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu /** The registered callback. */ 102cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu private Optional<Callback> mCallback; 103427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 104ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling // Fields for view layout. 105427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private float mThumbnailPadding; 106d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde private RectF mViewRect; 107427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 108ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling // Fields for the thumbnail pop-out effect. 109ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** The animators to move the thumbnail. */ 110cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu private AnimatorSet mThumbnailAnimatorSet; 111ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** The current diameter for the thumbnail image. */ 112cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu private float mCurrentThumbnailDiameter; 113ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** The current reveal circle opacity. */ 114cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu private float mCurrentRevealCircleOpacity; 115ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** The duration of the stretch phase in thumbnail pop-out effect. */ 116427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private long mThumbnailStretchDurationMs; 117ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** The duration of the shrink phase in thumbnail pop-out effect. */ 118427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private long mThumbnailShrinkDurationMs; 119ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** 120ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling * The beginning diameter of the thumbnail for the stretch phase in 121ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling * thumbnail pop-out effect. 122ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling */ 123427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private float mThumbnailStretchDiameterBegin; 124ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** 125ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling * The ending diameter of the thumbnail for the stretch phase in thumbnail 126ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling * pop-out effect. 127ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling */ 128427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private float mThumbnailStretchDiameterEnd; 129ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** 130ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling * The beginning diameter of the thumbnail for the shrink phase in thumbnail 131ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling * pop-out effect. 132ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling */ 133427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private float mThumbnailShrinkDiameterBegin; 134ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** 135ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling * The ending diameter of the thumbnail for the shrink phase in thumbnail 136ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling * pop-out effect. 137ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling */ 138427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private float mThumbnailShrinkDiameterEnd; 139ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** Paint object for the reveal circle. */ 140d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde private final Paint mRevealCirclePaint; 141427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 142ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling // Fields for the ripple effect. 143ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** The start delay of the ripple effect. */ 144427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private long mRippleStartDelayMs; 145ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** The duration of the ripple effect. */ 146427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private long mRippleDurationMs; 147ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** The beginning diameter of the ripple ring. */ 148427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private float mRippleRingDiameterBegin; 149ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** The ending diameter of the ripple ring. */ 150427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private float mRippleRingDiameterEnd; 151ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** The beginning thickness of the ripple ring. */ 152427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private float mRippleRingThicknessBegin; 153ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** The ending thickness of the ripple ring. */ 154427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private float mRippleRingThicknessEnd; 155ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** A lazily loaded animator for the ripple effect. */ 156427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private ValueAnimator mRippleAnimator; 157ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** 158ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling * The current ripple ring diameter which is updated by the ripple animator 159ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling * and used by onDraw(). 160ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling */ 161427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private float mCurrentRippleRingDiameter; 162ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** 163ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling * The current ripple ring thickness which is updated by the ripple animator 164ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling * and used by onDraw(). 165ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling */ 166427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private float mCurrentRippleRingThickness; 167ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** 168ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling * The current ripple ring opacity which is updated by the ripple animator 169ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling * and used by onDraw(). 170ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling */ 171427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private float mCurrentRippleRingOpacity; 172ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** The paint used for drawing the ripple effect. */ 173d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde private final Paint mRipplePaint; 174427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 175ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling // Fields for the hit state effect. 176ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** The paint to draw hit state circle. */ 177ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling private final Paint mHitStateCirclePaint; 178cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu /** 179ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling * The current hit state circle opacity (0.0 - 1.0) which is updated by the 180ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling * hit state animator. If -1, the hit state circle won't be drawn. 181cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu */ 182cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu private float mCurrentHitStateCircleOpacity; 183cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu 184ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** 185b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde * The pending reveal request. This is created when start is called, but is 186b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde * not drawn until the thumbnail is available. Once the bitmap is available 187b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde * it is swapped into the foreground request. 188ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling */ 189b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde private RevealRequest mPendingRequest; 190427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 191b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde /** The currently animating reveal request. */ 192b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde private RevealRequest mForegroundRequest; 193427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 194ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling /** 195b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde * The latest finished reveal request. Its thumbnail will be shown until 196b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde * a newer one replace it. 197ee669af495f2d3d22c97ab53fc5804c41e807e67Sascha Haeberling */ 198b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde private RevealRequest mBackgroundRequest; 199427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 200cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu private View.OnClickListener mOnClickListener = new View.OnClickListener() { 201cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu @Override 202cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu public void onClick(View v) { 203cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu // Trigger the hit state animation. Fade out the hit state white 204cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu // circle by changing the alpha. 205cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu final ValueAnimator hitStateAnimator = ValueAnimator.ofFloat( 206cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu HIT_STATE_CIRCLE_OPACITY_BEGIN, HIT_STATE_CIRCLE_OPACITY_END); 207cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu hitStateAnimator.setDuration(HIT_STATE_DURATION_MS); 208cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu hitStateAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); 209cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu hitStateAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 210cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu @Override 211cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu public void onAnimationUpdate(ValueAnimator valueAnimator) { 212cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu mCurrentHitStateCircleOpacity = (Float) valueAnimator.getAnimatedValue(); 213cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu invalidate(); 214cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu } 215cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu }); 216cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu hitStateAnimator.addListener(new AnimatorListenerAdapter() { 217cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu @Override 218cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu public void onAnimationEnd(Animator animation) { 219cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu super.onAnimationEnd(animation); 220cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu mCurrentHitStateCircleOpacity = HIT_STATE_CIRCLE_OPACITY_HIDDEN; 221cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu if (mCallback.isPresent()) { 222cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu mCallback.get().onHitStateFinished(); 223cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu } 224cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu } 225cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu }); 226cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu hitStateAnimator.start(); 227cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu } 228cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu }; 229cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu 230427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu /** 231427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * Constructs a RoundedThumbnailView. 232427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu */ 233427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu public RoundedThumbnailView(Context context, AttributeSet attrs) { 234427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu super(context, attrs); 235427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 236cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu mCallback = Optional.absent(); 237cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu 238427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Make the view clickable. 239427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu setClickable(true); 240cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu setOnClickListener(mOnClickListener); 241427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 242427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mThumbnailPadding = getResources().getDimension(R.dimen.rounded_thumbnail_padding); 243427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 244427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Load thumbnail pop-out effect constants. 245427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mThumbnailStretchDurationMs = THUMBNAIL_STRETCH_DURATION_MS; 246427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mThumbnailShrinkDurationMs = THUMBNAIL_SHRINK_DURATION_MS; 247427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mThumbnailStretchDiameterBegin = 248427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu getResources().getDimension(R.dimen.rounded_thumbnail_diameter_min); 249427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mThumbnailStretchDiameterEnd = 250427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu getResources().getDimension(R.dimen.rounded_thumbnail_diameter_max); 251427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mThumbnailShrinkDiameterBegin = mThumbnailStretchDiameterEnd; 252427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mThumbnailShrinkDiameterEnd = 253427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu getResources().getDimension(R.dimen.rounded_thumbnail_diameter_normal); 254427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Load ripple effect constants. 255427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu float startDelayRatio = 0.5f; 256427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mRippleStartDelayMs = (long) (mThumbnailStretchDurationMs * startDelayRatio); 257427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mRippleDurationMs = RIPPLE_DURATION_MS; 258427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mRippleRingDiameterEnd = 259427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu getResources().getDimension(R.dimen.rounded_thumbnail_ripple_ring_diameter_max); 260d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde 261d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde mViewRect = new RectF(0, 0, mRippleRingDiameterEnd, mRippleRingDiameterEnd); 262d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde 263427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mRippleRingDiameterBegin = 264427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu getResources().getDimension(R.dimen.rounded_thumbnail_ripple_ring_diameter_min); 265427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mRippleRingThicknessBegin = 266427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu getResources().getDimension(R.dimen.rounded_thumbnail_ripple_ring_thick_max); 267427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mRippleRingThicknessEnd = 268427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu getResources().getDimension(R.dimen.rounded_thumbnail_ripple_ring_thick_min); 26930eda38142fd279e0f10796e39f469d97f2a5b43Senpo Hu 270cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu mCurrentHitStateCircleOpacity = HIT_STATE_CIRCLE_OPACITY_HIDDEN; 271cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu // Draw the reveal while circle. 272cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu mHitStateCirclePaint = new Paint(); 273cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu mHitStateCirclePaint.setAntiAlias(true); 274cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu mHitStateCirclePaint.setColor(Color.WHITE); 275cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu mHitStateCirclePaint.setStyle(Paint.Style.FILL); 276cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu 277d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde mRipplePaint = new Paint(); 278d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde mRipplePaint.setAntiAlias(true); 279d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde mRipplePaint.setColor(Color.WHITE); 280d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde mRipplePaint.setStyle(Paint.Style.STROKE); 281d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde 282d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde mRevealCirclePaint = new Paint(); 283d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde mRevealCirclePaint.setAntiAlias(true); 284d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde mRevealCirclePaint.setColor(Color.WHITE); 285d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde mRevealCirclePaint.setStyle(Paint.Style.FILL); 286427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 287427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 288427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu @Override 289427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 290427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Ignore the spec since the size should be fixed. 291427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu int desiredSize = (int) mRippleRingDiameterEnd; 292427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu setMeasuredDimension(desiredSize, desiredSize); 293427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 294427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 295427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu @Override 296427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu protected void onDraw(Canvas canvas) { 297427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu super.onDraw(canvas); 298427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 299cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu final float centerX = canvas.getWidth() / 2; 300cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu final float centerY = canvas.getHeight() / 2; 301cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu 302cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu final float viewDiameter = mRippleRingDiameterEnd; 303cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu final float finalDiameter = mThumbnailShrinkDiameterEnd; 304d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde 305d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde canvas.clipRect(mViewRect); 306427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 307427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Draw the thumbnail of latest finished reveal request. 308b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde if (mBackgroundRequest != null) { 309b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde Paint thumbnailPaint = mBackgroundRequest.getThumbnailPaint(); 310427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu if (thumbnailPaint != null) { 311427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Draw the old thumbnail with the final diameter. 312cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu float scaleRatio = finalDiameter / viewDiameter; 313427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 314427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu canvas.save(); 315427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu canvas.scale(scaleRatio, scaleRatio, centerX, centerY); 316427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu canvas.drawRoundRect( 317d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde mViewRect, 318427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu centerX, 319427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu centerY, 320427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu thumbnailPaint); 321427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu canvas.restore(); 322427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 323427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 324427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 325427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Draw animated parts (thumbnail and ripple) if there exists a reveal request. 326b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde if (mForegroundRequest != null) { 327427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Draw ripple ring first or the ring will cover thumbnail. 328427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu if (mCurrentRippleRingThickness > 0) { 329427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Draw the ripple ring. 330d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde mRipplePaint.setAlpha((int) (mCurrentRippleRingOpacity * 255)); 331d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde mRipplePaint.setStrokeWidth(mCurrentRippleRingThickness); 332427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 333427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu canvas.save(); 334d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde canvas.drawCircle(centerX, centerY, mCurrentRippleRingDiameter / 2, mRipplePaint); 335427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu canvas.restore(); 336427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 337427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 338427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Achieve the animation effect by scaling the transformation matrix. 339427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu float scaleRatio = mCurrentThumbnailDiameter / mRippleRingDiameterEnd; 340427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 341427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu canvas.save(); 342427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu canvas.scale(scaleRatio, scaleRatio, centerX, centerY); 343427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 344427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Draw the new popping up thumbnail. 345b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde Paint thumbnailPaint = mForegroundRequest.getThumbnailPaint(); 346427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu if (thumbnailPaint != null) { 347427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu canvas.drawRoundRect( 348d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde mViewRect, 349427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu centerX, 350427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu centerY, 351427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu thumbnailPaint); 352427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 353427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 354427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Draw the reveal while circle. 355d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde mRevealCirclePaint.setAlpha((int) (mCurrentRevealCircleOpacity * 255)); 356427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu canvas.drawCircle(centerX, centerY, 357d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde mRippleRingDiameterEnd / 2, mRevealCirclePaint); 358427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 359427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu canvas.restore(); 360427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 361cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu 362cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu // Draw hit state circle if necessary. 363cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu if (mCurrentHitStateCircleOpacity != HIT_STATE_CIRCLE_OPACITY_HIDDEN) { 364cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu canvas.save(); 365cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu final float scaleRatio = finalDiameter / viewDiameter; 366cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu canvas.scale(scaleRatio, scaleRatio, centerX, centerY); 367cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu 368cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu // Draw the hit state while circle. 369cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu mHitStateCirclePaint.setAlpha((int) (mCurrentHitStateCircleOpacity * 255)); 370cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu canvas.drawCircle(centerX, centerY, 371cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu mRippleRingDiameterEnd / 2, mHitStateCirclePaint); 372cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu canvas.restore(); 373cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu } 374cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu } 375cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu 376cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu /** 377cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu * Sets the callback. 378cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu * 379cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu * @param callback The callback to be set. 380cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu */ 381cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu public void setCallback(Callback callback) { 382cb8f66b4953e4fed4a0cd5133c2914b26097c133Senpo Hu mCallback = Optional.of(callback); 383427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 384427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 385427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu /** 38654ac03ba6ae3e739df74c6d9e35fda9017be07d7Senpo Hu * Gets the padding size with mode options and preview edges. 387427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * 38854ac03ba6ae3e739df74c6d9e35fda9017be07d7Senpo Hu * @return The padding size with mode options and preview edges. 389427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu */ 39054ac03ba6ae3e739df74c6d9e35fda9017be07d7Senpo Hu public float getThumbnailPadding() { 39154ac03ba6ae3e739df74c6d9e35fda9017be07d7Senpo Hu return mThumbnailPadding; 39254ac03ba6ae3e739df74c6d9e35fda9017be07d7Senpo Hu } 39354ac03ba6ae3e739df74c6d9e35fda9017be07d7Senpo Hu 39454ac03ba6ae3e739df74c6d9e35fda9017be07d7Senpo Hu /** 39554ac03ba6ae3e739df74c6d9e35fda9017be07d7Senpo Hu * Gets the diameter of the thumbnail image after the revealing animation. 39654ac03ba6ae3e739df74c6d9e35fda9017be07d7Senpo Hu * 39754ac03ba6ae3e739df74c6d9e35fda9017be07d7Senpo Hu * @return The diameter of the thumbnail image after the revealing animation. 39854ac03ba6ae3e739df74c6d9e35fda9017be07d7Senpo Hu */ 39954ac03ba6ae3e739df74c6d9e35fda9017be07d7Senpo Hu public float getThumbnailFinalDiameter() { 40054ac03ba6ae3e739df74c6d9e35fda9017be07d7Senpo Hu return mThumbnailShrinkDiameterEnd; 401427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 402427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 403427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu /** 404427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * Starts the thumbnail revealing animation. 405427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * 406427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * @param accessibilityString An accessibility String to be announced during the revealing 407427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * animation. 408427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu */ 409427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu public void startRevealThumbnailAnimation(String accessibilityString) { 41030eda38142fd279e0f10796e39f469d97f2a5b43Senpo Hu MainThread.checkMainThread(); 411427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Create a new request. 412b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mPendingRequest = new RevealRequest(getMeasuredWidth(), accessibilityString); 413427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 414427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 415427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu /** 416427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * Updates the thumbnail image. 417427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * 418427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * @param thumbnailBitmap The thumbnail image to be shown. 41904aab100674dfdd11bffabc46e6a199f5e9a5267Paul Rohde * @param rotation The orientation of the image in degrees. 420427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu */ 42104aab100674dfdd11bffabc46e6a199f5e9a5267Paul Rohde public void setThumbnail(final Bitmap thumbnailBitmap, final int rotation) { 42230eda38142fd279e0f10796e39f469d97f2a5b43Senpo Hu MainThread.checkMainThread(); 423b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde 424b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde if(mPendingRequest != null) { 425b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mPendingRequest.setThumbnailBitmap(thumbnailBitmap, rotation); 426b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde 427b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde runPendingRequestAnimation(); 428427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } else { 429b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde Log.e(TAG, "Pending thumb was null!"); 430427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 431427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 432427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 433427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu /** 434427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * Hide the thumbnail. 435427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu */ 436427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu public void hideThumbnail() { 43730eda38142fd279e0f10796e39f469d97f2a5b43Senpo Hu MainThread.checkMainThread(); 438427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Make this view invisible. 439427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu setVisibility(GONE); 440427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 441b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde clearAnimations(); 442b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde 443b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde // Remove all pending reveal requests. 444b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mPendingRequest = null; 445b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mForegroundRequest = null; 446b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mBackgroundRequest = null; 447b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde } 448b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde 449b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde /** 450b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde * Stop currently running animators. 451b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde */ 452b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde private void clearAnimations() { 453427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Stop currently running animators. 454427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu if (mThumbnailAnimatorSet != null && mThumbnailAnimatorSet.isRunning()) { 45568d834ff57c8e20a8c040cdcbf50d0e3332d6385Senpo Hu mThumbnailAnimatorSet.removeAllListeners(); 456daea74c9c1708908ae801d53ef3288500cc9532fSenpo Hu mThumbnailAnimatorSet.cancel(); 4572eb1f8531b4879b836206cbde0e57335e300b445I-Jong Lin // Release the animator so that a new instance will be created and 4582eb1f8531b4879b836206cbde0e57335e300b445I-Jong Lin // its listeners properly reconnected. Fix for b/19034435 4592eb1f8531b4879b836206cbde0e57335e300b445I-Jong Lin mThumbnailAnimatorSet = null; 460427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 4612eb1f8531b4879b836206cbde0e57335e300b445I-Jong Lin 462427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu if (mRippleAnimator != null && mRippleAnimator.isRunning()) { 46368d834ff57c8e20a8c040cdcbf50d0e3332d6385Senpo Hu mRippleAnimator.removeAllListeners(); 464daea74c9c1708908ae801d53ef3288500cc9532fSenpo Hu mRippleAnimator.cancel(); 4652eb1f8531b4879b836206cbde0e57335e300b445I-Jong Lin // Release the animator so that a new instance will be created and 4662eb1f8531b4879b836206cbde0e57335e300b445I-Jong Lin // its listeners properly reconnected. Fix for b/19034435 4672eb1f8531b4879b836206cbde0e57335e300b445I-Jong Lin mRippleAnimator = null; 468427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 469427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 470427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 471427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu /** 472b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde * Set the foreground request to the background, complete it, and run the 473b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde * animation for the pending thumbnail. 474427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu */ 475b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde private void runPendingRequestAnimation() { 476b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde // Shift foreground to background, and pending to foreground. 477b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde if (mForegroundRequest != null) { 478b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mBackgroundRequest = mForegroundRequest; 479b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mBackgroundRequest.finishRippleAnimation(); 480b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mBackgroundRequest.finishThumbnailAnimation(); 481427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 482427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 483b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mForegroundRequest = mPendingRequest; 484b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mPendingRequest = null; 485427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 486427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Make this view visible. 487427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu setVisibility(VISIBLE); 488427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 489b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde // Ensure there are no running animations. 490b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde clearAnimations(); 491427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 492b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde Interpolator stretchInterpolator; 493b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde if (ApiHelper.isLOrHigher()) { 494b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde // Both phases use fast_out_flow_in interpolator. 495b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde stretchInterpolator = AnimationUtils.loadInterpolator( 496b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde getContext(), android.R.interpolator.fast_out_slow_in); 497b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde } else { 498b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde stretchInterpolator = new AccelerateDecelerateInterpolator(); 499b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde } 500427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 501b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde // The first phase of thumbnail animation. Stretch the thumbnail to the maximal size. 502b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde ValueAnimator stretchAnimator = ValueAnimator.ofFloat( 503b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mThumbnailStretchDiameterBegin, mThumbnailStretchDiameterEnd); 504b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde stretchAnimator.setDuration(mThumbnailStretchDurationMs); 505b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde stretchAnimator.setInterpolator(stretchInterpolator); 506b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde stretchAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 507b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde @Override 508b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde public void onAnimationUpdate(ValueAnimator valueAnimator) { 509b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mCurrentThumbnailDiameter = (Float) valueAnimator.getAnimatedValue(); 510b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde float fraction = valueAnimator.getAnimatedFraction(); 511b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde float opacityDiff = THUMBNAIL_REVEAL_CIRCLE_OPACITY_END - 512b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde THUMBNAIL_REVEAL_CIRCLE_OPACITY_BEGIN; 513b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mCurrentRevealCircleOpacity = 514b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde THUMBNAIL_REVEAL_CIRCLE_OPACITY_BEGIN + fraction * opacityDiff; 515b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde invalidate(); 516b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde } 517b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde }); 518b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde 519b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde // The second phase of thumbnail animation. Shrink the thumbnail to the final size. 520b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde Interpolator shrinkInterpolator = stretchInterpolator; 521b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde ValueAnimator shrinkAnimator = ValueAnimator.ofFloat( 522b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mThumbnailShrinkDiameterBegin, mThumbnailShrinkDiameterEnd); 523b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde shrinkAnimator.setDuration(mThumbnailShrinkDurationMs); 524b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde shrinkAnimator.setInterpolator(shrinkInterpolator); 525b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde shrinkAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 526b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde @Override 527b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde public void onAnimationUpdate(ValueAnimator valueAnimator) { 528b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mCurrentThumbnailDiameter = (Float) valueAnimator.getAnimatedValue(); 529b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde invalidate(); 530b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde } 531b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde }); 532b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde 533b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde // The stretch and shrink animators play sequentially. 534b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mThumbnailAnimatorSet = new AnimatorSet(); 535b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mThumbnailAnimatorSet.playSequentially(stretchAnimator, shrinkAnimator); 536b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mThumbnailAnimatorSet.addListener(new AnimatorListenerAdapter() { 537b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde @Override 538b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde public void onAnimationEnd(Animator animation) { 539b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde if (mForegroundRequest != null) { 540b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde // Mark the thumbnail animation as finished. 541b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mForegroundRequest.finishThumbnailAnimation(); 542b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde processRevealRequests(); 543427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 544b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde } 545b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde }); 546427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 547427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Start thumbnail animation immediately. 548427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mThumbnailAnimatorSet.start(); 549427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 550427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Lazily load the ripple animator. 551b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde // Ripple effect uses linear_out_slow_in interpolator. 552b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde Interpolator rippleInterpolator = 553b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde InterpolatorHelper.getLinearOutSlowInInterpolator(getContext()); 554b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde 555b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde // When start shrinking the thumbnail, a ripple effect is triggered at the same time. 556b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mRippleAnimator = 557b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde ValueAnimator.ofFloat(mRippleRingDiameterBegin, mRippleRingDiameterEnd); 558b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mRippleAnimator.setDuration(mRippleDurationMs); 559b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mRippleAnimator.setInterpolator(rippleInterpolator); 560b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mRippleAnimator.addListener(new AnimatorListenerAdapter() { 561b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde @Override 562b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde public void onAnimationEnd(Animator animation) { 563b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde if (mForegroundRequest != null) { 564b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mForegroundRequest.finishRippleAnimation(); 565b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde processRevealRequests(); 566427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 567b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde } 568b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde }); 569b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mRippleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 570b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde @Override 571b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde public void onAnimationUpdate(ValueAnimator valueAnimator) { 572b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mCurrentRippleRingDiameter = (Float) valueAnimator.getAnimatedValue(); 573b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde float fraction = valueAnimator.getAnimatedFraction(); 574b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mCurrentRippleRingThickness = mRippleRingThicknessBegin + 575b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde fraction * (mRippleRingThicknessEnd - mRippleRingThicknessBegin); 576b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mCurrentRippleRingOpacity = RIPPLE_OPACITY_BEGIN + 577b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde fraction * (RIPPLE_OPACITY_END - RIPPLE_OPACITY_BEGIN); 578b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde invalidate(); 579b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde } 580b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde }); 581b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde 582427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Start ripple animation after delay. 583427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mRippleAnimator.setStartDelay(mRippleStartDelayMs); 584427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mRippleAnimator.start(); 585427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 586427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Announce the accessibility string. 587b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde announceForAccessibility(mForegroundRequest.getAccessibilityString()); 588b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde } 589b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde 590b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde private void processRevealRequests() { 591b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde if(mForegroundRequest != null && mForegroundRequest.isFinished()) { 592b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mBackgroundRequest = mForegroundRequest; 593b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde mForegroundRequest = null; 594b759b67a8574fa30ea04d2340ecdfc2979ec0304Paul Rohde } 595427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 596427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 597d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde @Override 598d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde public boolean hasOverlappingRendering() { 599d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde return true; 600d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde } 601d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde 602427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu /** 603427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * Encapsulates necessary information for a complete thumbnail reveal animation. 604427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu */ 605427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private static class RevealRequest { 606427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // The size of the thumbnail. 607427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private float mViewSize; 608427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 609427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // The accessibility string. 610427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private String mAccessibilityString; 611427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 612427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // The cached Paint object to draw the thumbnail. 613427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private Paint mThumbnailPaint; 614427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 615427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // The flag to indicate if thumbnail animation of this request is full-filled. 616427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private boolean mThumbnailAnimationFinished; 617427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 618427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // The flag to indicate if ripple animation of this request is full-filled. 619427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private boolean mRippleAnimationFinished; 620427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 621427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu /** 622427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * Constructs a reveal request. Use setThumbnailBitmap() to specify a source bitmap for the 623427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * thumbnail. 624427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * 625427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * @param viewSize The size of the capture indicator view. 626427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * @param accessibilityString The accessibility string of the request. 627427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu */ 628427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu public RevealRequest(float viewSize, String accessibilityString) { 629427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mAccessibilityString = accessibilityString; 630427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mViewSize = viewSize; 631427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 632427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 633427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu /** 634427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * Returns the accessibility string. 635427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * 636427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * @return the accessibility string. 637427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu */ 638427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu public String getAccessibilityString() { 639427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu return mAccessibilityString; 640427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 641427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 642427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu /** 643427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * Returns the paint object which can be used to draw the thumbnail on a Canvas. 644427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * 645427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * @return the paint object which can be used to draw the thumbnail on a Canvas. 646427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu */ 647427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu public Paint getThumbnailPaint() { 648d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde return mThumbnailPaint; 649d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde } 650d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde 651d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde /** 652d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde * Used to precompute the thumbnail paint from the given source bitmap. 653d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde */ 65404aab100674dfdd11bffabc46e6a199f5e9a5267Paul Rohde private void precomputeThumbnailPaint(Bitmap srcBitmap, int rotation) { 655427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Lazy loading the thumbnail paint object. 656427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu if (mThumbnailPaint == null) { 657427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Can't create a paint object until the thumbnail bitmap is available. 658d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde if (srcBitmap == null) { 659d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde return; 660427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 661a45abcb3cfc48f1718b77a616e000d1678fbd85bSenpo Hu // The original bitmap should be a square shape. 662d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde if (srcBitmap.getWidth() != srcBitmap.getHeight()) { 663d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde return; 664a45abcb3cfc48f1718b77a616e000d1678fbd85bSenpo Hu } 665427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 666427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Create a bitmap shader for the paint. 667427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu BitmapShader shader = new BitmapShader( 668d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde srcBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); 669d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde if (srcBitmap.getWidth() != mViewSize) { 670a45abcb3cfc48f1718b77a616e000d1678fbd85bSenpo Hu // Create a transformation matrix for the bitmap shader if the size is not 671a45abcb3cfc48f1718b77a616e000d1678fbd85bSenpo Hu // matched. 672a45abcb3cfc48f1718b77a616e000d1678fbd85bSenpo Hu RectF srcRect = new RectF( 673d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde 0.0f, 0.0f, srcBitmap.getWidth(), srcBitmap.getHeight()); 674a45abcb3cfc48f1718b77a616e000d1678fbd85bSenpo Hu RectF dstRect = new RectF(0.0f, 0.0f, mViewSize, mViewSize); 67504aab100674dfdd11bffabc46e6a199f5e9a5267Paul Rohde 676a45abcb3cfc48f1718b77a616e000d1678fbd85bSenpo Hu Matrix shaderMatrix = new Matrix(); 67704aab100674dfdd11bffabc46e6a199f5e9a5267Paul Rohde 67804aab100674dfdd11bffabc46e6a199f5e9a5267Paul Rohde // Scale the shader to fit the destination view size. 679a45abcb3cfc48f1718b77a616e000d1678fbd85bSenpo Hu shaderMatrix.setRectToRect(srcRect, dstRect, Matrix.ScaleToFit.FILL); 68004aab100674dfdd11bffabc46e6a199f5e9a5267Paul Rohde 68104aab100674dfdd11bffabc46e6a199f5e9a5267Paul Rohde // Rotate the image around the given source rect point. 68204aab100674dfdd11bffabc46e6a199f5e9a5267Paul Rohde shaderMatrix.preRotate(rotation, 68304aab100674dfdd11bffabc46e6a199f5e9a5267Paul Rohde srcRect.width() / 2, 68404aab100674dfdd11bffabc46e6a199f5e9a5267Paul Rohde srcRect.height() / 2); 68504aab100674dfdd11bffabc46e6a199f5e9a5267Paul Rohde 686a45abcb3cfc48f1718b77a616e000d1678fbd85bSenpo Hu shader.setLocalMatrix(shaderMatrix); 687a45abcb3cfc48f1718b77a616e000d1678fbd85bSenpo Hu } 688427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 689427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Create the paint for drawing the thumbnail in a circle. 690427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mThumbnailPaint = new Paint(); 691427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mThumbnailPaint.setAntiAlias(true); 692427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mThumbnailPaint.setShader(shader); 693427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 694427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 695427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 696427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu /** 697427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * Checks if the request is full-filled. 698427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * 699427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * @return True if both thumbnail animation and ripple animation are finished 700427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu */ 701427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu public boolean isFinished() { 702427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu return mThumbnailAnimationFinished && mRippleAnimationFinished; 703427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 704427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 705427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu /** 706427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * Marks the thumbnail animation is finished. 707427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu */ 708427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu public void finishThumbnailAnimation() { 709427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mThumbnailAnimationFinished = true; 710427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 711427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 712427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu /** 713427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * Marks the ripple animation is finished. 714427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu */ 715427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu public void finishRippleAnimation() { 716427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu mRippleAnimationFinished = true; 717427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 718427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 719427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu /** 720427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * Updates the thumbnail image. 721427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * 722427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * @param thumbnailBitmap The thumbnail image to be shown. 72304aab100674dfdd11bffabc46e6a199f5e9a5267Paul Rohde * @param rotation The orientation of the image in degrees. 724427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu */ 72504aab100674dfdd11bffabc46e6a199f5e9a5267Paul Rohde public void setThumbnailBitmap(Bitmap thumbnailBitmap, int rotation) { 726d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde Bitmap originalBitmap = thumbnailBitmap; 727427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu // Crop the image if it is not square. 728d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde if (originalBitmap.getWidth() != originalBitmap.getHeight()) { 729d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde originalBitmap = cropCenterBitmap(originalBitmap); 730427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 731d6fad4a44e0afca5164704fcd906601744e68fb7Paul Rohde 73204aab100674dfdd11bffabc46e6a199f5e9a5267Paul Rohde precomputeThumbnailPaint(originalBitmap, rotation); 733427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 734427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu 735427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu /** 736427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * Obtains a square bitmap by cropping the center of a bitmap. If the given image is 737427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * portrait, the cropped image keeps 100% original width and vertically centered to the 738427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * original image. If the given image is landscape, the cropped image keeps 100% original 739427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * height and horizontally centered to the original image. 740427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * 741427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * @param srcBitmap the bitmap image to be cropped in the center. 742427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu * @return a result square bitmap. 743427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu */ 744427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu private Bitmap cropCenterBitmap(Bitmap srcBitmap) { 745427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu int srcWidth = srcBitmap.getWidth(); 746427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu int srcHeight = srcBitmap.getHeight(); 747427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu Bitmap dstBitmap; 748427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu if (srcWidth >= srcHeight) { 749427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu dstBitmap = Bitmap.createBitmap( 750427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu srcBitmap, srcWidth / 2 - srcHeight / 2, 0, srcHeight, srcHeight); 751427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } else { 752427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu dstBitmap = Bitmap.createBitmap( 753427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu srcBitmap, 0, srcHeight / 2 - srcWidth / 2, srcWidth, srcWidth); 754427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 755427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu return dstBitmap; 756427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 757427a915be4bcc84fa1c32d9e9e1b7473c522f732Senpo Hu } 75891bdb7fa1084af44c36dd7072033a90f4823ef5aSenpo Hu} 759