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;
23f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenenimport android.animation.TimeAnimator;
240c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenenimport android.app.ActivityManager;
250c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenenimport android.app.StatusBarManager;
265438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.content.Context;
2769ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenenimport android.content.pm.ActivityInfo;
2869ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenenimport android.content.res.Configuration;
295438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.graphics.Bitmap;
305438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.graphics.Canvas;
315438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.graphics.Matrix;
325438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.graphics.PixelFormat;
33217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenenimport android.graphics.SurfaceTexture;
3457a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenenimport android.os.AsyncTask;
355438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.os.Binder;
365438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.util.DisplayMetrics;
37595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenenimport android.util.Log;
385438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.view.Display;
395438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.view.LayoutInflater;
40b2112cff553c3b6658440bf06060c7a08f82df5cNick Pellyimport android.view.MotionEvent;
415438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.view.Surface;
4282792d9a73138f209d1f46f156604c4c65f69c0bMathias Agopianimport android.view.SurfaceControl;
43217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenenimport android.view.TextureView;
445438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.view.View;
455438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.view.ViewGroup;
465438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.view.WindowManager;
470c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenenimport android.view.animation.AccelerateDecelerateInterpolator;
4869ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenenimport android.view.animation.DecelerateInterpolator;
495438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pellyimport android.widget.ImageView;
50be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenenimport android.widget.TextView;
51a3715d12aeba988c1104726e935462ea4f39eca7Martijn Coenenimport android.widget.Toast;
525438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
535438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly/**
54f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen * This class is responsible for handling the UI animation
55f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen * around Android Beam. The animation consists of the following
56f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen * animators:
57f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen *
58f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen * mPreAnimator: scales the screenshot down to INTERMEDIATE_SCALE
59f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen * mSlowSendAnimator: scales the screenshot down to 0.2f (used as a "send in progress" animation)
60f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen * mFastSendAnimator: quickly scales the screenshot down to 0.0f (used for send success)
61f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen * mFadeInAnimator: fades the current activity back in (used after mFastSendAnimator completes)
62f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen * mScaleUpAnimator: scales the screenshot back up to full screen (used for failure or receiving)
63f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen * mHintAnimator: Slowly turns up the alpha of the "Touch to Beam" hint
64f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen *
65f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen * Possible sequences are:
66f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen *
67f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen * mPreAnimator => mSlowSendAnimator => mFastSendAnimator => mFadeInAnimator (send success)
68f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen * mPreAnimator => mSlowSendAnimator => mScaleUpAnimator (send failure)
69f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen * mPreAnimator => mScaleUpAnimator (p2p link broken, or data received)
70f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen *
71f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen * Note that mFastSendAnimator and mFadeInAnimator are combined in a set, as they
72f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen * are an atomic animation that cannot be interrupted.
73f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen *
74f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen * All methods of this class must be called on the UI thread
755438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly */
76217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenenpublic class SendUi implements Animator.AnimatorListener, View.OnTouchListener,
77f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        TimeAnimator.TimeListener, TextureView.SurfaceTextureListener {
78595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen    static final String TAG = "SendUi";
79595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen
8069ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    static final float INTERMEDIATE_SCALE = 0.6f;
815438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
8269ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    static final float[] PRE_SCREENSHOT_SCALE = {1.0f, INTERMEDIATE_SCALE};
83674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely    static final int PRE_DURATION_MS = 350;
8469ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
85f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    static final float[] SEND_SCREENSHOT_SCALE = {INTERMEDIATE_SCALE, 0.2f};
86674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely    static final int SLOW_SEND_DURATION_MS = 8000; // Stretch out sending over 8s
87f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    static final int FAST_SEND_DURATION_MS = 350;
8869ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
8969ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    static final float[] SCALE_UP_SCREENSHOT_SCALE = {INTERMEDIATE_SCALE, 1.0f};
9069ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    static final int SCALE_UP_DURATION_MS = 300;
915438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
92674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely    static final int FADE_IN_DURATION_MS = 250;
93674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely    static final int FADE_IN_START_DELAY_MS = 350;
94674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely
950c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen    static final int SLIDE_OUT_DURATION_MS = 300;
960c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen
971a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen    static final float[] BLACK_LAYER_ALPHA_DOWN_RANGE = {0.9f, 0.0f};
981a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen    static final float[] BLACK_LAYER_ALPHA_UP_RANGE = {0.0f, 0.9f};
991a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen
100be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen    static final float[] TEXT_HINT_ALPHA_RANGE = {0.0f, 1.0f};
101be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen    static final int TEXT_HINT_ALPHA_DURATION_MS = 500;
102674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely    static final int TEXT_HINT_ALPHA_START_DELAY_MS = 300;
103be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen
1040c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen    static final int FINISH_SCALE_UP = 0;
105f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    static final int FINISH_SEND_SUCCESS = 1;
1060c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen
10757a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen    static final int STATE_IDLE = 0;
108595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen    static final int STATE_W4_SCREENSHOT = 1;
109595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen    static final int STATE_W4_SCREENSHOT_PRESEND_REQUESTED = 2;
110595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen    static final int STATE_W4_SCREENSHOT_THEN_STOP = 3;
111595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen    static final int STATE_W4_PRESEND = 4;
112595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen    static final int STATE_W4_CONFIRM = 5;
113595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen    static final int STATE_SENDING = 6;
114595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen    static final int STATE_COMPLETE = 7;
11557a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen
1165438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    // all members are only used on UI thread
1175438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    final WindowManager mWindowManager;
1185438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    final Context mContext;
1195438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    final Display mDisplay;
1205438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    final DisplayMetrics mDisplayMetrics;
1215438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    final Matrix mDisplayMatrix;
1225438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    final WindowManager.LayoutParams mWindowLayoutParams;
1235438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    final LayoutInflater mLayoutInflater;
1240c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen    final StatusBarManager mStatusBarManager;
1255438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    final View mScreenshotLayout;
1265438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    final ImageView mScreenshotView;
1271a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen    final ImageView mBlackLayer;
128217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    final TextureView mTextureView;
129be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen    final TextView mTextHint;
1301a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen    final TextView mTextRetry;
1315438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    final Callback mCallback;
132f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen
133f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    // The mFrameCounter animation is purely used to count down a certain
134f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    // number of (vsync'd) frames. This is needed because the first 3
135f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    // times the animation internally calls eglSwapBuffers(), large buffers
136f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    // are allocated by the graphics drivers. This causes the animation
137f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    // to look janky. So on platforms where we can use hardware acceleration,
138f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    // the animation order is:
139f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    // Wait for hw surface => start frame counter => start pre-animation after 3 frames
140f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    // For platforms where no hw acceleration can be used, the pre-animation
141f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    // is started immediately.
142f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    final TimeAnimator mFrameCounterAnimator;
143f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen
1445438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    final ObjectAnimator mPreAnimator;
145217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    final ObjectAnimator mSlowSendAnimator;
146f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    final ObjectAnimator mFastSendAnimator;
147674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely    final ObjectAnimator mFadeInAnimator;
148be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen    final ObjectAnimator mHintAnimator;
149f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    final ObjectAnimator mScaleUpAnimator;
1501a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen    final ObjectAnimator mAlphaDownAnimator;
1511a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen    final ObjectAnimator mAlphaUpAnimator;
15269ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    final AnimatorSet mSuccessAnimatorSet;
153f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen
154f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    // Besides animating the screenshot, the Beam UI also renders
155f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    // fireflies on platforms where we can do hardware-acceleration.
156f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    // Firefly rendering is only started once the initial
157f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    // "pre-animation" has scaled down the screenshot, to avoid
158f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    // that animation becoming janky. Likewise, the fireflies are
159f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    // stopped in their tracks as soon as we finish the animation,
160f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    // to make the finishing animation smooth.
1610c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen    final boolean mHardwareAccelerated;
162f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    final FireflyRenderer mFireflyRenderer;
1635438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
164a3715d12aeba988c1104726e935462ea4f39eca7Martijn Coenen    String mToastString;
1655438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    Bitmap mScreenshotBitmap;
1660c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen
16757a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen    int mState;
168f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    int mRenderedFrames;
169f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen
170f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    // Used for holding the surface
171f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    SurfaceTexture mSurface;
172f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    int mSurfaceWidth;
173f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    int mSurfaceHeight;
1745438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
1755438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    interface Callback {
176b2112cff553c3b6658440bf06060c7a08f82df5cNick Pelly        public void onSendConfirmed();
1775438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    }
1785438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
1795438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    public SendUi(Context context, Callback callback) {
1805438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mContext = context;
1815438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mCallback = callback;
1825438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
1835438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mDisplayMetrics = new DisplayMetrics();
1845438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mDisplayMatrix = new Matrix();
1855438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1860c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        mStatusBarManager = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE);
1870c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen
1885438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mDisplay = mWindowManager.getDefaultDisplay();
1895438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
1905438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mLayoutInflater = (LayoutInflater)
1915438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly                context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
1925438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mScreenshotLayout = mLayoutInflater.inflate(R.layout.screenshot, null);
193f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen
1945438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.screenshot);
1955438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mScreenshotLayout.setFocusable(true);
1965438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
197be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen        mTextHint = (TextView) mScreenshotLayout.findViewById(R.id.calltoaction);
1981a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mTextRetry = (TextView) mScreenshotLayout.findViewById(R.id.retrytext);
1991a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mBlackLayer = (ImageView) mScreenshotLayout.findViewById(R.id.blacklayer);
200217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        mTextureView = (TextureView) mScreenshotLayout.findViewById(R.id.fireflies);
201217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        mTextureView.setSurfaceTextureListener(this);
202217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen
2030c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        // We're only allowed to use hardware acceleration if
2040c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        // isHighEndGfx() returns true - otherwise, we're too limited
2050c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        // on resources to do it.
206525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        mHardwareAccelerated = ActivityManager.isHighEndGfx();
2070c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        int hwAccelerationFlags = mHardwareAccelerated ?
208f7966d1dadff2d78f01bbdb134e8252b53343179Dianne Hackborn                WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED : 0;
2090c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen
2105438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mWindowLayoutParams = new WindowManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
2110c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen                ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
2120c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen                WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
2135438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly                WindowManager.LayoutParams.FLAG_FULLSCREEN
2140c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen                | hwAccelerationFlags
215b2112cff553c3b6658440bf06060c7a08f82df5cNick Pelly                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
2165438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly                PixelFormat.OPAQUE);
217525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        mWindowLayoutParams.privateFlags |=
218525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project                WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
2195438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mWindowLayoutParams.token = new Binder();
2205438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
221f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        mFrameCounterAnimator = new TimeAnimator();
222f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        mFrameCounterAnimator.setTimeListener(this);
223f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen
2245438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        PropertyValuesHolder preX = PropertyValuesHolder.ofFloat("scaleX", PRE_SCREENSHOT_SCALE);
2255438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        PropertyValuesHolder preY = PropertyValuesHolder.ofFloat("scaleY", PRE_SCREENSHOT_SCALE);
2265438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mPreAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, preX, preY);
22769ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        mPreAnimator.setInterpolator(new DecelerateInterpolator());
2285438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mPreAnimator.setDuration(PRE_DURATION_MS);
2295438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mPreAnimator.addListener(this);
2305438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
231f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        PropertyValuesHolder postX = PropertyValuesHolder.ofFloat("scaleX", SEND_SCREENSHOT_SCALE);
232f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        PropertyValuesHolder postY = PropertyValuesHolder.ofFloat("scaleY", SEND_SCREENSHOT_SCALE);
2334dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen        PropertyValuesHolder alphaDown = PropertyValuesHolder.ofFloat("alpha",
2344dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen                new float[]{1.0f, 0.0f});
2354dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen
236217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        mSlowSendAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, postX, postY);
237674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        mSlowSendAnimator.setInterpolator(new DecelerateInterpolator());
238217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        mSlowSendAnimator.setDuration(SLOW_SEND_DURATION_MS);
23969ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
240f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        mFastSendAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, postX,
2414dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen                postY, alphaDown);
242f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        mFastSendAnimator.setInterpolator(new DecelerateInterpolator());
243f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        mFastSendAnimator.setDuration(FAST_SEND_DURATION_MS);
244f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        mFastSendAnimator.addListener(this);
24569ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
24669ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        PropertyValuesHolder scaleUpX = PropertyValuesHolder.ofFloat("scaleX", SCALE_UP_SCREENSHOT_SCALE);
24769ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        PropertyValuesHolder scaleUpY = PropertyValuesHolder.ofFloat("scaleY", SCALE_UP_SCREENSHOT_SCALE);
2484dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen
24969ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        mScaleUpAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, scaleUpX, scaleUpY);
250674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        mScaleUpAnimator.setInterpolator(new DecelerateInterpolator());
25169ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        mScaleUpAnimator.setDuration(SCALE_UP_DURATION_MS);
25269ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        mScaleUpAnimator.addListener(this);
25369ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
254674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        PropertyValuesHolder fadeIn = PropertyValuesHolder.ofFloat("alpha", 1.0f);
255674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        mFadeInAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, fadeIn);
256674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        mFadeInAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
257674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        mFadeInAnimator.setDuration(FADE_IN_DURATION_MS);
258674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        mFadeInAnimator.setStartDelay(FADE_IN_START_DELAY_MS);
259674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        mFadeInAnimator.addListener(this);
260674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely
261be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen        PropertyValuesHolder alphaUp = PropertyValuesHolder.ofFloat("alpha", TEXT_HINT_ALPHA_RANGE);
262be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen        mHintAnimator = ObjectAnimator.ofPropertyValuesHolder(mTextHint, alphaUp);
263be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen        mHintAnimator.setInterpolator(null);
264be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen        mHintAnimator.setDuration(TEXT_HINT_ALPHA_DURATION_MS);
265674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely        mHintAnimator.setStartDelay(TEXT_HINT_ALPHA_START_DELAY_MS);
266be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen
2671a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        alphaDown = PropertyValuesHolder.ofFloat("alpha", BLACK_LAYER_ALPHA_DOWN_RANGE);
2681a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mAlphaDownAnimator = ObjectAnimator.ofPropertyValuesHolder(mBlackLayer, alphaDown);
2691a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mAlphaDownAnimator.setInterpolator(new DecelerateInterpolator());
2701a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mAlphaDownAnimator.setDuration(400);
2711a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen
2721a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        alphaUp = PropertyValuesHolder.ofFloat("alpha", BLACK_LAYER_ALPHA_UP_RANGE);
2731a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mAlphaUpAnimator = ObjectAnimator.ofPropertyValuesHolder(mBlackLayer, alphaUp);
2741a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mAlphaUpAnimator.setInterpolator(new DecelerateInterpolator());
2751a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mAlphaUpAnimator.setDuration(200);
2761a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen
27769ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        mSuccessAnimatorSet = new AnimatorSet();
278f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        mSuccessAnimatorSet.playSequentially(mFastSendAnimator, mFadeInAnimator);
2795438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
280f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        if (mHardwareAccelerated) {
281f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            mFireflyRenderer = new FireflyRenderer(context);
282f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        } else {
283f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            mFireflyRenderer = null;
284f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        }
28557a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen        mState = STATE_IDLE;
2865438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    }
2875438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
2885438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    public void takeScreenshot() {
28957a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen        // There's no point in taking the screenshot if
29057a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen        // we're still finishing the previous animation.
291595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen        if (mState >= STATE_W4_CONFIRM) {
29257a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen            return;
29357a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen        }
294595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen        mState = STATE_W4_SCREENSHOT;
29557a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen        new ScreenshotTask().execute();
2965438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    }
2975438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
29869ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    /** Show pre-send animation */
299e31749eae9b242d5019eb5cf5f09069d60df9f71Nick Pelly    public void showPreSend() {
300595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen        switch (mState) {
301595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen            case STATE_IDLE:
302595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                Log.e(TAG, "Unexpected showPreSend() in STATE_IDLE");
303595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                return;
304595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen            case STATE_W4_SCREENSHOT:
305595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                // Still waiting for screenshot, store request in state
306595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                // and wait for screenshot completion.
307595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                mState = STATE_W4_SCREENSHOT_PRESEND_REQUESTED;
308595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                return;
309595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen            case STATE_W4_SCREENSHOT_PRESEND_REQUESTED:
310595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                Log.e(TAG, "Unexpected showPreSend() in STATE_W4_SCREENSHOT_PRESEND_REQUESTED");
311595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                return;
312595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen            case STATE_W4_PRESEND:
313595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                // Expected path, continue below
314595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                break;
315595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen            default:
316595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                Log.e(TAG, "Unexpected showPreSend() in state " + Integer.toString(mState));
317595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                return;
31857a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen        }
31990e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        // Update display metrics
32090e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        mDisplay.getRealMetrics(mDisplayMetrics);
32190e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen
32290e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        final int statusBarHeight = mContext.getResources().getDimensionPixelSize(
32390e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen                                        com.android.internal.R.dimen.status_bar_height);
32490e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen
3251a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mBlackLayer.setVisibility(View.GONE);
3261a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mBlackLayer.setAlpha(0f);
327595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen        mScreenshotLayout.setOnTouchListener(this);
3285438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mScreenshotView.setImageBitmap(mScreenshotBitmap);
3290c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        mScreenshotView.setTranslationX(0f);
3300c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        mScreenshotView.setAlpha(1.0f);
33190e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        mScreenshotView.setPadding(0, statusBarHeight, 0, 0);
33269ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
33390e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        mScreenshotLayout.requestFocus();
33469ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
3351a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mTextHint.setText(mContext.getResources().getString(R.string.touch));
336e31749eae9b242d5019eb5cf5f09069d60df9f71Nick Pelly        mTextHint.setAlpha(0.0f);
337e31749eae9b242d5019eb5cf5f09069d60df9f71Nick Pelly        mTextHint.setVisibility(View.VISIBLE);
338e31749eae9b242d5019eb5cf5f09069d60df9f71Nick Pelly        mHintAnimator.start();
339be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen
34069ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        // Lock the orientation.
34169ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        // The orientation from the configuration does not specify whether
34269ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        // the orientation is reverse or not (ie landscape or reverse landscape).
34369ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        // So we have to use SENSOR_LANDSCAPE or SENSOR_PORTRAIT to make sure
34469ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        // we lock in portrait / landscape and have the sensor determine
34569ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        // which way is up.
34669ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        int orientation = mContext.getResources().getConfiguration().orientation;
34769ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
34869ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        switch (orientation) {
34969ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen            case Configuration.ORIENTATION_LANDSCAPE:
35069ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen                mWindowLayoutParams.screenOrientation =
35169ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen                        ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
35269ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen                break;
35369ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen            case Configuration.ORIENTATION_PORTRAIT:
35469ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen                mWindowLayoutParams.screenOrientation =
35569ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen                        ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
35669ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen                break;
35769ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen            default:
35869ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen                mWindowLayoutParams.screenOrientation =
35969ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen                        ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
36069ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen                break;
36169ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        }
36269ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
3635438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
3640c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        // Disable statusbar pull-down
3650c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        mStatusBarManager.disable(StatusBarManager.DISABLE_EXPAND);
3660c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen
367a3715d12aeba988c1104726e935462ea4f39eca7Martijn Coenen        mToastString = null;
368f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen
369f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        if (!mHardwareAccelerated) {
370f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            mPreAnimator.start();
371f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        } // else, we will start the animation once we get the hardware surface
372595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen        mState = STATE_W4_CONFIRM;
37369ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    }
3745438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
37569ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    /** Show starting send animation */
37669ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    public void showStartSend() {
37757a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen        if (mState < STATE_SENDING) return;
378217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen
3791a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mTextRetry.setVisibility(View.GONE);
380f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        // Update the starting scale - touchscreen-mashers may trigger
381f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        // this before the pre-animation completes.
38290e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        float currentScale = mScreenshotView.getScaleX();
383217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        PropertyValuesHolder postX = PropertyValuesHolder.ofFloat("scaleX",
384217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen                new float[] {currentScale, 0.0f});
385217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        PropertyValuesHolder postY = PropertyValuesHolder.ofFloat("scaleY",
386217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen                new float[] {currentScale, 0.0f});
387217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen
388f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        mSlowSendAnimator.setValues(postX, postY);
3891a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen
3901a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        float currentAlpha = mBlackLayer.getAlpha();
3911a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        if (mBlackLayer.isShown() && currentAlpha > 0.0f) {
3921a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen            PropertyValuesHolder alphaDown = PropertyValuesHolder.ofFloat("alpha",
3931a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen                    new float[] {currentAlpha, 0.0f});
3941a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen            mAlphaDownAnimator.setValues(alphaDown);
3951a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen            mAlphaDownAnimator.start();
3961a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        }
397f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        mSlowSendAnimator.start();
39869ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    }
39969ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
400a3715d12aeba988c1104726e935462ea4f39eca7Martijn Coenen    public void finishAndToast(int finishMode, String toast) {
401a3715d12aeba988c1104726e935462ea4f39eca7Martijn Coenen        mToastString = toast;
402a3715d12aeba988c1104726e935462ea4f39eca7Martijn Coenen
403a3715d12aeba988c1104726e935462ea4f39eca7Martijn Coenen        finish(finishMode);
404a3715d12aeba988c1104726e935462ea4f39eca7Martijn Coenen    }
405a3715d12aeba988c1104726e935462ea4f39eca7Martijn Coenen
40669ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen    /** Return to initial state */
4070c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen    public void finish(int finishMode) {
408595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen        switch (mState) {
409595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen            case STATE_IDLE:
410595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                return;
411595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen            case STATE_W4_SCREENSHOT:
412595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen            case STATE_W4_SCREENSHOT_PRESEND_REQUESTED:
413595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                // Screenshot is still being captured on a separate thread.
414595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                // Update state, and stop everything when the capture is done.
415595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                mState = STATE_W4_SCREENSHOT_THEN_STOP;
416595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                return;
417595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen            case STATE_W4_SCREENSHOT_THEN_STOP:
418595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                Log.e(TAG, "Unexpected call to finish() in STATE_W4_SCREENSHOT_THEN_STOP");
419595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                return;
420595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen            case STATE_W4_PRESEND:
421595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                // We didn't build up any animation state yet, but
422595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                // did store the bitmap. Clear out the bitmap, reset
423595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                // state and bail.
424595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                mScreenshotBitmap = null;
425595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                mState = STATE_IDLE;
426595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                return;
427595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen            default:
428595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                // We've started animations and attached a view; tear stuff down below.
429595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                break;
430595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen        }
431f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen
432f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        // Stop rendering the fireflies
433f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        if (mFireflyRenderer != null) {
434f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            mFireflyRenderer.stop();
43569ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        }
436f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen
437be7d37a1c6622c8907869fd30e9e6da73fd77290Martijn Coenen        mTextHint.setVisibility(View.GONE);
4381a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mTextRetry.setVisibility(View.GONE);
439f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen
440f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        float currentScale = mScreenshotView.getScaleX();
441f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        float currentAlpha = mScreenshotView.getAlpha();
442f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen
443f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        if (finishMode == FINISH_SCALE_UP) {
4441a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen            mBlackLayer.setVisibility(View.GONE);
445674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely            PropertyValuesHolder scaleUpX = PropertyValuesHolder.ofFloat("scaleX",
446674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely                    new float[] {currentScale, 1.0f});
447674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely            PropertyValuesHolder scaleUpY = PropertyValuesHolder.ofFloat("scaleY",
448674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely                    new float[] {currentScale, 1.0f});
449674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely            PropertyValuesHolder scaleUpAlpha = PropertyValuesHolder.ofFloat("alpha",
450674cd01fd74b403a4625efbfd2743e179c3d9ecaBrandon Keely                    new float[] {currentAlpha, 1.0f});
451f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            mScaleUpAnimator.setValues(scaleUpX, scaleUpY, scaleUpAlpha);
452f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen
4530c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen            mScaleUpAnimator.start();
454f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        } else if (finishMode == FINISH_SEND_SUCCESS){
455f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            // Modify the fast send parameters to match the current scale
456f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            PropertyValuesHolder postX = PropertyValuesHolder.ofFloat("scaleX",
457f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen                    new float[] {currentScale, 0.0f});
458f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            PropertyValuesHolder postY = PropertyValuesHolder.ofFloat("scaleY",
459f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen                    new float[] {currentScale, 0.0f});
460f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha",
4611a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen                    new float[] {currentAlpha, 0.0f});
462f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            mFastSendAnimator.setValues(postX, postY, alpha);
463f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen
464f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            // Reset the fadeIn parameters to start from alpha 1
465f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            PropertyValuesHolder fadeIn = PropertyValuesHolder.ofFloat("alpha",
466f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen                    new float[] {0.0f, 1.0f});
467f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            mFadeInAnimator.setValues(fadeIn);
468f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen
469f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            mSlowSendAnimator.cancel();
470f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            mSuccessAnimatorSet.start();
4710c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        }
47257a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen        mState = STATE_COMPLETE;
4735438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    }
4745438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
475595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen    void dismiss() {
476595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen        if (mState < STATE_W4_CONFIRM) return;
47757a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen        // Immediately set to IDLE, to prevent .cancel() calls
4784e0ab3174beafb4915388d8c11286a48e362ed7cMartijn Coenen        // below from immediately calling into dismiss() again.
47957a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen        // (They can do so on the same thread).
48057a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen        mState = STATE_IDLE;
481f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        mSurface = null;
482f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        mFrameCounterAnimator.cancel();
4835438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mPreAnimator.cancel();
484217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        mSlowSendAnimator.cancel();
485f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        mFastSendAnimator.cancel();
48669ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        mSuccessAnimatorSet.cancel();
48769ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        mScaleUpAnimator.cancel();
4881a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mAlphaUpAnimator.cancel();
4891a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mAlphaDownAnimator.cancel();
4905438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mWindowManager.removeView(mScreenshotLayout);
4910c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        mStatusBarManager.disable(StatusBarManager.DISABLE_NONE);
49257a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen        mScreenshotBitmap = null;
493a3715d12aeba988c1104726e935462ea4f39eca7Martijn Coenen        if (mToastString != null) {
494a3715d12aeba988c1104726e935462ea4f39eca7Martijn Coenen            Toast.makeText(mContext, mToastString, Toast.LENGTH_LONG).show();
495a3715d12aeba988c1104726e935462ea4f39eca7Martijn Coenen        }
496a3715d12aeba988c1104726e935462ea4f39eca7Martijn Coenen        mToastString = null;
4975438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    }
4985438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
4995438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    /**
5005438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly     * @return the current display rotation in degrees
5015438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly     */
5025438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    static float getDegreesForRotation(int value) {
5035438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        switch (value) {
5045438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        case Surface.ROTATION_90:
5055438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            return 90f;
5065438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        case Surface.ROTATION_180:
5075438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            return 180f;
5085438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        case Surface.ROTATION_270:
5095438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            return 270f;
5105438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        }
5115438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        return 0f;
5125438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    }
5135438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
51457a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen    final class ScreenshotTask extends AsyncTask<Void, Void, Bitmap> {
51557a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen        @Override
51657a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen        protected Bitmap doInBackground(Void... params) {
51757a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen            return createScreenshot();
51857a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen        }
51957a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen
52057a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen        @Override
52157a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen        protected void onPostExecute(Bitmap result) {
522595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen            if (mState == STATE_W4_SCREENSHOT) {
523595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                // Screenshot done, wait for request to start preSend anim
524595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                mState = STATE_W4_PRESEND;
525595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen            } else if (mState == STATE_W4_SCREENSHOT_THEN_STOP) {
526595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                // We were asked to finish, move to idle state and exit
527595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                mState = STATE_IDLE;
528595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen            } else if (mState == STATE_W4_SCREENSHOT_PRESEND_REQUESTED) {
529595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                if (result != null) {
530595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                    mScreenshotBitmap = result;
531595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                    mState = STATE_W4_PRESEND;
532595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                    showPreSend();
533595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                } else {
534595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                    // Failed to take screenshot; reset state to idle
535595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                    // and don't do anything
536595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                    Log.e(TAG, "Failed to create screenshot");
537595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                    mState = STATE_IDLE;
538595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                }
539595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen            } else {
540595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen                Log.e(TAG, "Invalid state on screenshot completion: " + Integer.toString(mState));
54157a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen            }
54257a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen        }
54357a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen    };
54457a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen
5455438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    /**
5465438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly     * Returns a screenshot of the current display contents.
5475438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly     */
5485438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    Bitmap createScreenshot() {
5495438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        // We need to orient the screenshot correctly (and the Surface api seems to
5505438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        // take screenshots only in the natural orientation of the device :!)
5510c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen
5525438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        mDisplay.getRealMetrics(mDisplayMetrics);
553e3cfbec7045220294c172d55122daf55ab6cb058Martijn Coenen        boolean hasNavBar =  mContext.getResources().getBoolean(
554e3cfbec7045220294c172d55122daf55ab6cb058Martijn Coenen                com.android.internal.R.bool.config_showNavigationBar);
5550c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen
5565438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels};
5575438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        float degrees = getDegreesForRotation(mDisplay.getRotation());
55890e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        final int statusBarHeight = mContext.getResources().getDimensionPixelSize(
55990e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen                                        com.android.internal.R.dimen.status_bar_height);
560e3cfbec7045220294c172d55122daf55ab6cb058Martijn Coenen
56190e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        // Navbar has different sizes, depending on orientation
562e3cfbec7045220294c172d55122daf55ab6cb058Martijn Coenen        final int navBarHeight = hasNavBar ? mContext.getResources().getDimensionPixelSize(
563e3cfbec7045220294c172d55122daf55ab6cb058Martijn Coenen                                        com.android.internal.R.dimen.navigation_bar_height) : 0;
564525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        final int navBarHeightLandscape = hasNavBar ? mContext.getResources().getDimensionPixelSize(
565525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project                                        com.android.internal.R.dimen.navigation_bar_height_landscape) : 0;
566525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project
567e3cfbec7045220294c172d55122daf55ab6cb058Martijn Coenen        final int navBarWidth = hasNavBar ? mContext.getResources().getDimensionPixelSize(
568e3cfbec7045220294c172d55122daf55ab6cb058Martijn Coenen                                        com.android.internal.R.dimen.navigation_bar_width) : 0;
56990e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen
5705438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        boolean requiresRotation = (degrees > 0);
5715438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        if (requiresRotation) {
5725438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            // Get the dimensions of the device in its native orientation
5735438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            mDisplayMatrix.reset();
5745438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            mDisplayMatrix.preRotate(-degrees);
5755438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            mDisplayMatrix.mapPoints(dims);
5765438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            dims[0] = Math.abs(dims[0]);
5775438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            dims[1] = Math.abs(dims[1]);
5785438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        }
5795438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
58082792d9a73138f209d1f46f156604c4c65f69c0bMathias Agopian        Bitmap bitmap = SurfaceControl.screenshot((int) dims[0], (int) dims[1]);
5815438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        // Bail if we couldn't take the screenshot
5825438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        if (bitmap == null) {
5835438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            return null;
5845438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        }
5855438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
5865438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        if (requiresRotation) {
5875438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            // Rotate the screenshot to the current orientation
5885438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,
5895438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly                    mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888);
5905438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            Canvas c = new Canvas(ss);
5915438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            c.translate(ss.getWidth() / 2, ss.getHeight() / 2);
5925438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            c.rotate(360f - degrees);
5935438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            c.translate(-dims[0] / 2, -dims[1] / 2);
5945438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            c.drawBitmap(bitmap, 0, 0, null);
5955438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
5965438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            bitmap = ss;
5975438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        }
5985438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
59990e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        // TODO this is somewhat device-specific; need generic solution.
60090e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        // Crop off the status bar and the nav bar
60190e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        // Portrait: 0, statusBarHeight, width, height - status - nav
60290e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        // Landscape: 0, statusBarHeight, width - navBar, height - status
60390e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        int newLeft = 0;
60490e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        int newTop = statusBarHeight;
60590e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        int newWidth = bitmap.getWidth();
60690e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        int newHeight = bitmap.getHeight();
607525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        float smallestWidth = (float)Math.min(newWidth, newHeight);
608525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project        float smallestWidthDp = smallestWidth / (mDisplayMetrics.densityDpi / 160f);
60990e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        if (bitmap.getWidth() < bitmap.getHeight()) {
61090e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen            // Portrait mode: status bar is at the top, navbar bottom, width unchanged
61190e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen            newHeight = bitmap.getHeight() - statusBarHeight - navBarHeight;
61290e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        } else {
613525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project            // Landscape mode: status bar is at the top
614525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project            // Navbar: bottom on >599dp width devices, otherwise to the side
615525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project            if (smallestWidthDp > 599) {
616525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project                newHeight = bitmap.getHeight() - statusBarHeight - navBarHeightLandscape;
617525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project            } else {
618525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project                newHeight = bitmap.getHeight() - statusBarHeight;
619525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project                newWidth = bitmap.getWidth() - navBarWidth;
620525c260303268a83da4c3413b953d13c9084e834The Android Open Source Project            }
62190e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        }
62290e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen        bitmap = Bitmap.createBitmap(bitmap, newLeft, newTop, newWidth, newHeight);
62390e1b000b385d7fa11481ddca4d61d6b425d7579Martijn Coenen
6245438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        return bitmap;
6255438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    }
6265438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
6275438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    @Override
6285438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    public void onAnimationStart(Animator animation) {  }
6295438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
6305438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    @Override
6315438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    public void onAnimationEnd(Animator animation) {
6320c4fce176e6acb1a1a74e9248cf83eb0314490aaMartijn Coenen        if (animation == mScaleUpAnimator || animation == mSuccessAnimatorSet ||
633f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            animation == mFadeInAnimator) {
634f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            // These all indicate the end of the animation
6355438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly            dismiss();
636f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        } else if (animation == mFastSendAnimator) {
637f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            // After sending is done and we've faded out, reset the scale to 1
6384dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen            // so we can fade it back in.
6394dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen            mScreenshotView.setScaleX(1.0f);
6404dd3c020b434aec0ccca030afeb57a861bc9a2f3Martijn Coenen            mScreenshotView.setScaleY(1.0f);
641f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        } else if (animation == mPreAnimator) {
642595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen            if (mHardwareAccelerated && (mState == STATE_W4_CONFIRM)) {
643f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen                mFireflyRenderer.start(mSurface, mSurfaceWidth, mSurfaceHeight);
644f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            }
6455438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly        }
6465438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    }
6475438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
6485438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    @Override
6495438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    public void onAnimationCancel(Animator animation) {  }
6505438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly
6515438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    @Override
6525438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly    public void onAnimationRepeat(Animator animation) {  }
653b2112cff553c3b6658440bf06060c7a08f82df5cNick Pelly
654b2112cff553c3b6658440bf06060c7a08f82df5cNick Pelly    @Override
655f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
656f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        // This gets called on animation vsync
657f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        if (++mRenderedFrames < 4) {
658f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            // For the first 3 frames, call invalidate(); this calls eglSwapBuffers
659f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            // on the surface, which will allocate large buffers the first three calls
660f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            // as Android uses triple buffering.
661f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            mScreenshotLayout.invalidate();
662f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        } else {
663f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            // Buffers should be allocated, start the real animation
664f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            mFrameCounterAnimator.cancel();
665f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            mPreAnimator.start();
666f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        }
667f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    }
668f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen
669f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    @Override
670b2112cff553c3b6658440bf06060c7a08f82df5cNick Pelly    public boolean onTouch(View v, MotionEvent event) {
671595ab7e9788f61125fa14d1ff2768f995ac7b7e7Martijn Coenen        if (mState != STATE_W4_CONFIRM) {
672b2112cff553c3b6658440bf06060c7a08f82df5cNick Pelly            return false;
673b2112cff553c3b6658440bf06060c7a08f82df5cNick Pelly        }
67457a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen        mState = STATE_SENDING;
67569ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        // Ignore future touches
67669ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen        mScreenshotView.setOnTouchListener(null);
67769ca6627125c91c44b9e0d8bfa5df83281c2ebe1Martijn Coenen
678f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        // Cancel any ongoing animations
679f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        mFrameCounterAnimator.cancel();
680f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        mPreAnimator.cancel();
681f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen
682b2112cff553c3b6658440bf06060c7a08f82df5cNick Pelly        mCallback.onSendConfirmed();
683b2112cff553c3b6658440bf06060c7a08f82df5cNick Pelly        return true;
684b2112cff553c3b6658440bf06060c7a08f82df5cNick Pelly    }
685217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen
686217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    @Override
687217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
68857a44d07a3de327e8cdbbcd622118aa517313dbeMartijn Coenen        if (mHardwareAccelerated && mState < STATE_COMPLETE) {
689f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            mRenderedFrames = 0;
690f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen
691f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            mFrameCounterAnimator.start();
692f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            mSurface = surface;
693f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            mSurfaceWidth = width;
694f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen            mSurfaceHeight = height;
695217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        }
696217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    }
697217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen
698217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    @Override
699217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
700217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        // Since we've disabled orientation changes, we can safely ignore this
701217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    }
702217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen
703217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    @Override
704217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
705f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen        mSurface = null;
706f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen
707217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen        return true;
708217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    }
709217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen
710217edd31f93c3917a8bb02420366b88d27c431c2Martijn Coenen    @Override
711f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen    public void onSurfaceTextureUpdated(SurfaceTexture surface) { }
712f570dca6b0fa0ce883f5c950815e43acae4f51b2Martijn Coenen
7131a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen    public void showSendHint() {
7141a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        if (mAlphaDownAnimator.isRunning()) {
7151a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen           mAlphaDownAnimator.cancel();
7161a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        }
7171a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        if (mSlowSendAnimator.isRunning()) {
7181a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen            mSlowSendAnimator.cancel();
7191a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        }
7201a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mBlackLayer.setScaleX(mScreenshotView.getScaleX());
7211a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mBlackLayer.setScaleY(mScreenshotView.getScaleY());
7221a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mBlackLayer.setVisibility(View.VISIBLE);
7231a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mTextHint.setVisibility(View.GONE);
7241a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen
7251a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mTextRetry.setText(mContext.getResources().getString(R.string.beam_try_again));
7261a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mTextRetry.setVisibility(View.VISIBLE);
7271a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen
7281a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        PropertyValuesHolder alphaUp = PropertyValuesHolder.ofFloat("alpha",
7291a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen                new float[] {mBlackLayer.getAlpha(), 0.9f});
7301a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mAlphaUpAnimator.setValues(alphaUp);
7311a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen        mAlphaUpAnimator.start();
7321a158c7f354d1e1b031fba88928dbaa022e0d0dfMartijn Coenen    }
7335438ab0e17064e20870ac893a2dd2b9e1219eef1Nick Pelly}
734