15438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly/*
25438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly * Copyright (C) 2011 The Android Open Source Project
35438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly *
45438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly * Licensed under the Apache License, Version 2.0 (the "License");
55438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly * you may not use this file except in compliance with the License.
65438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly * You may obtain a copy of the License at
75438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly *
85438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly *      http://www.apache.org/licenses/LICENSE-2.0
95438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly *
105438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly * Unless required by applicable law or agreed to in writing, software
115438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly * distributed under the License is distributed on an "AS IS" BASIS,
125438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly * See the License for the specific language governing permissions and
145438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly * limitations under the License.
155438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly */
165438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
175438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellypackage com.android.nfc;
185438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
195438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.animation.Animator;
2069ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenenimport android.animation.AnimatorSet;
215438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.animation.ObjectAnimator;
225438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.animation.PropertyValuesHolder;
230c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenenimport android.app.ActivityManager;
240c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenenimport android.app.StatusBarManager;
255438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.content.Context;
2669ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenenimport android.content.pm.ActivityInfo;
2769ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenenimport android.content.res.Configuration;
285438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.graphics.Bitmap;
295438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.graphics.Canvas;
305438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.graphics.Matrix;
315438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.graphics.PixelFormat;
32217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenenimport android.graphics.SurfaceTexture;
335438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.os.Binder;
345438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.util.DisplayMetrics;
350c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenenimport android.util.Log;
365438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.view.Display;
375438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.view.LayoutInflater;
38b2112cff553c3b6658440bf06060c7a08f82df5cNick Pellyimport android.view.MotionEvent;
395438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.view.Surface;
40217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenenimport android.view.TextureView;
415438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.view.View;
425438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.view.ViewGroup;
435438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.view.WindowManager;
440c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenenimport android.view.animation.AccelerateDecelerateInterpolator;
4569ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenenimport android.view.animation.AccelerateInterpolator;
4669ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenenimport android.view.animation.DecelerateInterpolator;
475438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.widget.ImageView;
48be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenenimport android.widget.TextView;
495438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
505438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly/**
515438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly * All methods must be called on UI thread
525438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly */
53217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenenpublic class SendUi implements Animator.AnimatorListener, View.OnTouchListener,
54217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        TextureView.SurfaceTextureListener {
550c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen    private static final String LOG_TAG = "SendUI";
565438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
5769ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    static final float INTERMEDIATE_SCALE = 0.6f;
585438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
5969ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    static final float[] PRE_SCREENSHOT_SCALE = {1.0f, INTERMEDIATE_SCALE};
60674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely    static final int PRE_DURATION_MS = 350;
6169ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
62674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely    static final float[] CLONE_SCREENSHOT_SCALE = {INTERMEDIATE_SCALE, 0.2f};
63674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely    static final int SLOW_SEND_DURATION_MS = 8000; // Stretch out sending over 8s
64674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely    static final int FAST_CLONE_DURATION_MS = 350;
6569ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
6669ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    static final float[] SCALE_UP_SCREENSHOT_SCALE = {INTERMEDIATE_SCALE, 1.0f};
6769ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    static final int SCALE_UP_DURATION_MS = 300;
685438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
69674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely    static final int FADE_IN_DURATION_MS = 250;
70674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely    static final int FADE_IN_START_DELAY_MS = 350;
71674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely
720c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen    static final int SLIDE_OUT_DURATION_MS = 300;
730c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen
74be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen    static final float[] TEXT_HINT_ALPHA_RANGE = {0.0f, 1.0f};
75be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen    static final int TEXT_HINT_ALPHA_DURATION_MS = 500;
76674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely    static final int TEXT_HINT_ALPHA_START_DELAY_MS = 300;
77be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen
780c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen    static final int FINISH_SCALE_UP = 0;
790c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen    static final int FINISH_SLIDE_OUT = 1;
800c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen
815438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    // all members are only used on UI thread
825438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    final WindowManager mWindowManager;
835438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    final Context mContext;
845438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    final Display mDisplay;
855438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    final DisplayMetrics mDisplayMetrics;
865438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    final Matrix mDisplayMatrix;
875438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    final WindowManager.LayoutParams mWindowLayoutParams;
885438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    final LayoutInflater mLayoutInflater;
890c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen    final StatusBarManager mStatusBarManager;
905438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    final View mScreenshotLayout;
915438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    final ImageView mScreenshotView;
92217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    final TextureView mTextureView;
93be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen    final TextView mTextHint;
945438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    final Callback mCallback;
955438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    final ObjectAnimator mPreAnimator;
96217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    final ObjectAnimator mSlowSendAnimator;
9769ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    final ObjectAnimator mFastCloneAnimator;
98674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely    final ObjectAnimator mFadeInAnimator;
99be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen    final ObjectAnimator mHintAnimator;
10069ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    final AnimatorSet mSuccessAnimatorSet;
1010c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen    final boolean mHardwareAccelerated;
1025438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
1035438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    Bitmap mScreenshotBitmap;
1040c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen    ObjectAnimator mSlideoutAnimator;
105674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely    ObjectAnimator mScaleUpAnimator;
106217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    FireflyRenderThread mFireflyRenderThread;
1070c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen
1085438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    boolean mAttached;
1095438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
1105438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    interface Callback {
111b2112cff553c3b6658440bf06060c7a08f82df5cNick Pelly        public void onSendConfirmed();
1125438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    }
1135438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
1145438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    public SendUi(Context context, Callback callback) {
1155438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mContext = context;
1165438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mCallback = callback;
1175438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
1185438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mDisplayMetrics = new DisplayMetrics();
1195438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mDisplayMatrix = new Matrix();
1205438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1210c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        mStatusBarManager = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE);
1220c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen
1235438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mDisplay = mWindowManager.getDefaultDisplay();
1245438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
1255438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mLayoutInflater = (LayoutInflater)
1265438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly                context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
1275438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mScreenshotLayout = mLayoutInflater.inflate(R.layout.screenshot, null);
1285438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.screenshot);
1295438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mScreenshotLayout.setFocusable(true);
1305438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
131be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen        mTextHint = (TextView) mScreenshotLayout.findViewById(R.id.calltoaction);
1320c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen
133217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        mTextureView = (TextureView) mScreenshotLayout.findViewById(R.id.fireflies);
134217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        mTextureView.setSurfaceTextureListener(this);
135217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen
1360c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        // We're only allowed to use hardware acceleration if
1370c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        // isHighEndGfx() returns true - otherwise, we're too limited
1380c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        // on resources to do it.
1390c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        mHardwareAccelerated = ActivityManager.isHighEndGfx(mDisplay);
1400c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        int hwAccelerationFlags = mHardwareAccelerated ?
141f7966d1dadff2d78f01bbdb134e8252b53343179Dianne Hackborn                WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED : 0;
1420c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen
1435438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mWindowLayoutParams = new WindowManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
1440c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen                ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
1450c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen                WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
1465438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly                WindowManager.LayoutParams.FLAG_FULLSCREEN
1470c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen                | hwAccelerationFlags
1485438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly                | WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING
149b2112cff553c3b6658440bf06060c7a08f82df5cNick Pelly                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
1505438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly                PixelFormat.OPAQUE);
1515438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mWindowLayoutParams.token = new Binder();
1525438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
1535438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        PropertyValuesHolder preX = PropertyValuesHolder.ofFloat("scaleX", PRE_SCREENSHOT_SCALE);
1545438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        PropertyValuesHolder preY = PropertyValuesHolder.ofFloat("scaleY", PRE_SCREENSHOT_SCALE);
1555438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mPreAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, preX, preY);
15669ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        mPreAnimator.setInterpolator(new DecelerateInterpolator());
1575438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mPreAnimator.setDuration(PRE_DURATION_MS);
1585438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mPreAnimator.addListener(this);
1595438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
16069ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        PropertyValuesHolder postX = PropertyValuesHolder.ofFloat("scaleX", CLONE_SCREENSHOT_SCALE);
16169ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        PropertyValuesHolder postY = PropertyValuesHolder.ofFloat("scaleY", CLONE_SCREENSHOT_SCALE);
1624dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen        PropertyValuesHolder alphaDown = PropertyValuesHolder.ofFloat("alpha",
1634dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen                new float[]{1.0f, 0.0f});
1644dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen
165217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        mSlowSendAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, postX, postY);
166674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        mSlowSendAnimator.setInterpolator(new DecelerateInterpolator());
167217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        mSlowSendAnimator.setDuration(SLOW_SEND_DURATION_MS);
16869ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
1694dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen        mFastCloneAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, postX,
1704dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen                postY, alphaDown);
171674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        mFastCloneAnimator.setInterpolator(new DecelerateInterpolator());
17269ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        mFastCloneAnimator.setDuration(FAST_CLONE_DURATION_MS);
1734dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen        mFastCloneAnimator.addListener(this);
17469ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
17569ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        PropertyValuesHolder scaleUpX = PropertyValuesHolder.ofFloat("scaleX", SCALE_UP_SCREENSHOT_SCALE);
17669ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        PropertyValuesHolder scaleUpY = PropertyValuesHolder.ofFloat("scaleY", SCALE_UP_SCREENSHOT_SCALE);
1774dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen
17869ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        mScaleUpAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, scaleUpX, scaleUpY);
179674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        mScaleUpAnimator.setInterpolator(new DecelerateInterpolator());
18069ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        mScaleUpAnimator.setDuration(SCALE_UP_DURATION_MS);
18169ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        mScaleUpAnimator.addListener(this);
18269ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
183674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        PropertyValuesHolder fadeIn = PropertyValuesHolder.ofFloat("alpha", 1.0f);
184674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        mFadeInAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, fadeIn);
185674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        mFadeInAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
186674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        mFadeInAnimator.setDuration(FADE_IN_DURATION_MS);
187674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        mFadeInAnimator.setStartDelay(FADE_IN_START_DELAY_MS);
188674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        mFadeInAnimator.addListener(this);
189674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely
190be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen        PropertyValuesHolder alphaUp = PropertyValuesHolder.ofFloat("alpha", TEXT_HINT_ALPHA_RANGE);
191be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen        mHintAnimator = ObjectAnimator.ofPropertyValuesHolder(mTextHint, alphaUp);
192be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen        mHintAnimator.setInterpolator(null);
193be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen        mHintAnimator.setDuration(TEXT_HINT_ALPHA_DURATION_MS);
194674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        mHintAnimator.setStartDelay(TEXT_HINT_ALPHA_START_DELAY_MS);
195be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen
19669ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        mSuccessAnimatorSet = new AnimatorSet();
197674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        mSuccessAnimatorSet.playSequentially(mFastCloneAnimator, mFadeInAnimator);
1985438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
1995438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mAttached = false;
2005438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    }
2015438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
2025438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    public void takeScreenshot() {
2035438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mScreenshotBitmap = createScreenshot();
2045438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    }
2055438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
20669ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    /** Show pre-send animation */
207e31749eae9b242d5019eb5cf5f09069d60df9f71Nick Pelly    public void showPreSend() {
20890e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        // Update display metrics
20990e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        mDisplay.getRealMetrics(mDisplayMetrics);
21090e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen
21190e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        final int statusBarHeight = mContext.getResources().getDimensionPixelSize(
21290e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen                                        com.android.internal.R.dimen.status_bar_height);
21390e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen
2145438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        if (mScreenshotBitmap == null || mAttached) {
2155438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            return;
2165438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        }
21769ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        mScreenshotView.setOnTouchListener(this);
2185438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mScreenshotView.setImageBitmap(mScreenshotBitmap);
2190c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        mScreenshotView.setTranslationX(0f);
2200c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        mScreenshotView.setAlpha(1.0f);
22190e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        mScreenshotView.setPadding(0, statusBarHeight, 0, 0);
22269ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
22390e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        mScreenshotLayout.requestFocus();
22469ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
225e31749eae9b242d5019eb5cf5f09069d60df9f71Nick Pelly        mTextHint.setAlpha(0.0f);
226e31749eae9b242d5019eb5cf5f09069d60df9f71Nick Pelly        mTextHint.setVisibility(View.VISIBLE);
227e31749eae9b242d5019eb5cf5f09069d60df9f71Nick Pelly        mHintAnimator.start();
228be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen
22969ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        // Lock the orientation.
23069ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        // The orientation from the configuration does not specify whether
23169ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        // the orientation is reverse or not (ie landscape or reverse landscape).
23269ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        // So we have to use SENSOR_LANDSCAPE or SENSOR_PORTRAIT to make sure
23369ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        // we lock in portrait / landscape and have the sensor determine
23469ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        // which way is up.
23569ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        int orientation = mContext.getResources().getConfiguration().orientation;
23669ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
23769ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        switch (orientation) {
23869ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen            case Configuration.ORIENTATION_LANDSCAPE:
23969ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen                mWindowLayoutParams.screenOrientation =
24069ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen                        ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
24169ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen                break;
24269ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen            case Configuration.ORIENTATION_PORTRAIT:
24369ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen                mWindowLayoutParams.screenOrientation =
24469ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen                        ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
24569ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen                break;
24669ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen            default:
24769ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen                mWindowLayoutParams.screenOrientation =
24869ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen                        ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
24969ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen                break;
25069ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        }
25169ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
2525438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
2530c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        // Disable statusbar pull-down
2540c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        mStatusBarManager.disable(StatusBarManager.DISABLE_EXPAND);
2550c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen
2565438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mAttached = true;
2575438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mPreAnimator.start();
25869ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    }
2595438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
26069ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    /** Show starting send animation */
26169ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    public void showStartSend() {
26269ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        if (!mAttached) {
26369ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen            return;
26469ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        }
265217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        mSlowSendAnimator.start();
2665438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    }
2675438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
2685438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    /** Show post-send animation */
2695438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    public void showPostSend() {
2705438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        if (!mAttached) {
2715438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            return;
2725438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        }
27369ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
274217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        mSlowSendAnimator.cancel();
275be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen        mTextHint.setVisibility(View.GONE);
27669ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
277217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen
27890e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        float currentScale = mScreenshotView.getScaleX();
279217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen
280217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        // Modify the fast clone parameters to match the current scale
281217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        PropertyValuesHolder postX = PropertyValuesHolder.ofFloat("scaleX",
282217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen                new float[] {currentScale, 0.0f});
283217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        PropertyValuesHolder postY = PropertyValuesHolder.ofFloat("scaleY",
284217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen                new float[] {currentScale, 0.0f});
2854dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen        PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha",
2864dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen                new float[] {1.0f, 0.0f});
2874dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen        mFastCloneAnimator.setValues(postX, postY, alpha);
288674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely
289674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        // Modify the fadeIn parameters to match the current scale
290674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        PropertyValuesHolder fadeIn = PropertyValuesHolder.ofFloat("alpha",
291674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely               new float[] {0.0f, 1.0f});
292674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        mFadeInAnimator.setValues(fadeIn);
293217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen
294217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        if (mFireflyRenderThread != null) {
295217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen            mFireflyRenderThread.fadeOut();
296217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        }
29769ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
29869ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        mSuccessAnimatorSet.start();
29969ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    }
30069ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
30169ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    /** Return to initial state */
3020c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen    public void finish(int finishMode) {
30369ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        if (!mAttached) {
30469ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen            return;
30569ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        }
306be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen        mTextHint.setVisibility(View.GONE);
3070c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        if (finishMode == FINISH_SLIDE_OUT) {
3080c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen            PropertyValuesHolder slideX = PropertyValuesHolder.ofFloat("translationX",
3090c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen                    new float[]{0.0f, mScreenshotView.getWidth()});
3100c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen            mSlideoutAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, slideX);
3110c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen            mSlideoutAnimator.setInterpolator(new AccelerateInterpolator());
3120c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen            mSlideoutAnimator.setDuration(SLIDE_OUT_DURATION_MS);
3130c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen            mSlideoutAnimator.addListener(this);
3140c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen            mSlideoutAnimator.start();
3150c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        } else {
316674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely            float currentScale = mScreenshotView.getScaleX();
317674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely            float currentAlpha = mScreenshotView.getAlpha();
318674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely            PropertyValuesHolder scaleUpX = PropertyValuesHolder.ofFloat("scaleX",
319674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely                    new float[] {currentScale, 1.0f});
320674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely            PropertyValuesHolder scaleUpY = PropertyValuesHolder.ofFloat("scaleY",
321674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely                    new float[] {currentScale, 1.0f});
322674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely            PropertyValuesHolder scaleUpAlpha = PropertyValuesHolder.ofFloat("alpha",
323674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely                    new float[] {currentAlpha, 1.0f});
324674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely            mScaleUpAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, scaleUpX, scaleUpY, scaleUpAlpha);
325674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely            mScaleUpAnimator.setInterpolator(new DecelerateInterpolator());
326674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely            mScaleUpAnimator.setDuration(SCALE_UP_DURATION_MS);
327674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely            mScaleUpAnimator.addListener(this);
3280c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen            mScaleUpAnimator.start();
3290c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        }
3305438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    }
3315438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
3325438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    public void dismiss() {
3335438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        if (!mAttached) {
3345438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            return;
3355438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        }
3364e0ab3174beafb4915388d8c11286a48e362ed7cMartijn Coenen        // Immediately set to false, to prevent .cancel() calls
3374e0ab3174beafb4915388d8c11286a48e362ed7cMartijn Coenen        // below from immediately calling into dismiss() again.
3384e0ab3174beafb4915388d8c11286a48e362ed7cMartijn Coenen        mAttached = false;
3395438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mPreAnimator.cancel();
340217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        mSlowSendAnimator.cancel();
34169ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        mFastCloneAnimator.cancel();
34269ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        mSuccessAnimatorSet.cancel();
34369ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        mScaleUpAnimator.cancel();
3445438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mWindowManager.removeView(mScreenshotLayout);
3450c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        mStatusBarManager.disable(StatusBarManager.DISABLE_NONE);
3465438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        releaseScreenshot();
3475438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    }
3485438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
3495438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    public void releaseScreenshot() {
3505438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mScreenshotBitmap = null;
3515438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    }
3525438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
3535438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    /**
3545438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly     * @return the current display rotation in degrees
3555438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly     */
3565438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    static float getDegreesForRotation(int value) {
3575438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        switch (value) {
3585438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        case Surface.ROTATION_90:
3595438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            return 90f;
3605438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        case Surface.ROTATION_180:
3615438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            return 180f;
3625438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        case Surface.ROTATION_270:
3635438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            return 270f;
3645438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        }
3655438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        return 0f;
3665438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    }
3675438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
3685438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    /**
3695438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly     * Returns a screenshot of the current display contents.
3705438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly     */
3715438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    Bitmap createScreenshot() {
3725438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        // We need to orient the screenshot correctly (and the Surface api seems to
3735438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        // take screenshots only in the natural orientation of the device :!)
3740c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen
3755438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mDisplay.getRealMetrics(mDisplayMetrics);
376e3cfbec7045220294c172d55122daf55ab6cb058Martijn Coenen        boolean hasNavBar =  mContext.getResources().getBoolean(
377e3cfbec7045220294c172d55122daf55ab6cb058Martijn Coenen                com.android.internal.R.bool.config_showNavigationBar);
3780c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen
3795438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels};
3805438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        float degrees = getDegreesForRotation(mDisplay.getRotation());
38190e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        final int statusBarHeight = mContext.getResources().getDimensionPixelSize(
38290e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen                                        com.android.internal.R.dimen.status_bar_height);
383e3cfbec7045220294c172d55122daf55ab6cb058Martijn Coenen
38490e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        // Navbar has different sizes, depending on orientation
385e3cfbec7045220294c172d55122daf55ab6cb058Martijn Coenen        final int navBarHeight = hasNavBar ? mContext.getResources().getDimensionPixelSize(
386e3cfbec7045220294c172d55122daf55ab6cb058Martijn Coenen                                        com.android.internal.R.dimen.navigation_bar_height) : 0;
387e3cfbec7045220294c172d55122daf55ab6cb058Martijn Coenen        final int navBarWidth = hasNavBar ? mContext.getResources().getDimensionPixelSize(
388e3cfbec7045220294c172d55122daf55ab6cb058Martijn Coenen                                        com.android.internal.R.dimen.navigation_bar_width) : 0;
38990e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen
3905438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        boolean requiresRotation = (degrees > 0);
3915438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        if (requiresRotation) {
3925438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            // Get the dimensions of the device in its native orientation
3935438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            mDisplayMatrix.reset();
3945438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            mDisplayMatrix.preRotate(-degrees);
3955438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            mDisplayMatrix.mapPoints(dims);
3965438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            dims[0] = Math.abs(dims[0]);
3975438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            dims[1] = Math.abs(dims[1]);
3985438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        }
3995438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
4005438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        Bitmap bitmap = Surface.screenshot((int) dims[0], (int) dims[1]);
4015438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        // Bail if we couldn't take the screenshot
4025438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        if (bitmap == null) {
4035438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            return null;
4045438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        }
4055438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
40690e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen
4075438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        if (requiresRotation) {
4085438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            // Rotate the screenshot to the current orientation
4095438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,
4105438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly                    mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888);
4115438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            Canvas c = new Canvas(ss);
4125438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            c.translate(ss.getWidth() / 2, ss.getHeight() / 2);
4135438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            c.rotate(360f - degrees);
4145438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            c.translate(-dims[0] / 2, -dims[1] / 2);
4155438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            c.drawBitmap(bitmap, 0, 0, null);
4165438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
4175438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            bitmap = ss;
4185438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        }
4195438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
42090e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        // TODO this is somewhat device-specific; need generic solution.
42190e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        // Crop off the status bar and the nav bar
42290e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        // Portrait: 0, statusBarHeight, width, height - status - nav
42390e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        // Landscape: 0, statusBarHeight, width - navBar, height - status
42490e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        int newLeft = 0;
42590e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        int newTop = statusBarHeight;
42690e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        int newWidth = bitmap.getWidth();
42790e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        int newHeight = bitmap.getHeight();
42890e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        if (bitmap.getWidth() < bitmap.getHeight()) {
42990e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen            // Portrait mode: status bar is at the top, navbar bottom, width unchanged
43090e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen            newHeight = bitmap.getHeight() - statusBarHeight - navBarHeight;
43190e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        } else {
43290e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen            // Landscape mode: status bar is at the top, navbar right
43390e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen            newHeight = bitmap.getHeight() - statusBarHeight;
43490e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen            newWidth = bitmap.getWidth() - navBarWidth;
43590e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        }
43690e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        bitmap = Bitmap.createBitmap(bitmap, newLeft, newTop, newWidth, newHeight);
43790e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen
4385438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        return bitmap;
4395438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    }
4405438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
4415438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    @Override
4425438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    public void onAnimationStart(Animator animation) {  }
4435438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
4445438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    @Override
4455438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    public void onAnimationEnd(Animator animation) {
4460c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        if (animation == mScaleUpAnimator || animation == mSuccessAnimatorSet ||
447674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely            animation == mSlideoutAnimator || animation == mFadeInAnimator) {
4485438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            dismiss();
4494dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen        } else if (animation == mFastCloneAnimator) {
4504dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen            // After cloning is done and we've faded out, reset the scale to 1
4514dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen            // so we can fade it back in.
4524dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen            mScreenshotView.setScaleX(1.0f);
4534dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen            mScreenshotView.setScaleY(1.0f);
4545438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        }
4555438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    }
4565438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
4575438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    @Override
4585438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    public void onAnimationCancel(Animator animation) {  }
4595438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
4605438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    @Override
4615438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    public void onAnimationRepeat(Animator animation) {  }
462b2112cff553c3b6658440bf06060c7a08f82df5cNick Pelly
463b2112cff553c3b6658440bf06060c7a08f82df5cNick Pelly    @Override
464b2112cff553c3b6658440bf06060c7a08f82df5cNick Pelly    public boolean onTouch(View v, MotionEvent event) {
465b2112cff553c3b6658440bf06060c7a08f82df5cNick Pelly        if (!mAttached) {
466b2112cff553c3b6658440bf06060c7a08f82df5cNick Pelly            return false;
467b2112cff553c3b6658440bf06060c7a08f82df5cNick Pelly        }
46869ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        // Ignore future touches
46969ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        mScreenshotView.setOnTouchListener(null);
47069ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
471b2112cff553c3b6658440bf06060c7a08f82df5cNick Pelly        mPreAnimator.end();
472b2112cff553c3b6658440bf06060c7a08f82df5cNick Pelly        mCallback.onSendConfirmed();
473b2112cff553c3b6658440bf06060c7a08f82df5cNick Pelly        return true;
474b2112cff553c3b6658440bf06060c7a08f82df5cNick Pelly    }
475217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen
476217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    @Override
477217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
478217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        if (mHardwareAccelerated) {
479217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen            mFireflyRenderThread = new FireflyRenderThread(mContext, surface, width, height);
480217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen            mFireflyRenderThread.start();
481217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        }
482217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    }
483217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen
484217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    @Override
485217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
486217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        // Since we've disabled orientation changes, we can safely ignore this
487217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    }
488217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen
489217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    @Override
490217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
491217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        if (mFireflyRenderThread != null) {
492217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen            mFireflyRenderThread.finish();
493217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen            try {
494217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen                mFireflyRenderThread.join();
495217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen            } catch (InterruptedException e) {
496217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen                Log.e(LOG_TAG, "Couldn't wait for FireflyRenderThread.");
497217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen            }
498217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen            mFireflyRenderThread = null;
499217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        }
500217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        return true;
501217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    }
502217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen
503217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    @Override
504217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
505217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    }
5065438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly}
507