1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.nfc; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorSet; 21 import android.animation.ObjectAnimator; 22 import android.animation.PropertyValuesHolder; 23 import android.animation.TimeAnimator; 24 import android.app.ActivityManager; 25 import android.app.StatusBarManager; 26 import android.content.Context; 27 import android.content.pm.ActivityInfo; 28 import android.content.res.Configuration; 29 import android.graphics.Bitmap; 30 import android.graphics.Canvas; 31 import android.graphics.Matrix; 32 import android.graphics.PixelFormat; 33 import android.graphics.SurfaceTexture; 34 import android.os.Binder; 35 import android.util.DisplayMetrics; 36 import android.view.Display; 37 import android.view.LayoutInflater; 38 import android.view.MotionEvent; 39 import android.view.Surface; 40 import android.view.TextureView; 41 import android.view.View; 42 import android.view.ViewGroup; 43 import android.view.WindowManager; 44 import android.view.animation.AccelerateDecelerateInterpolator; 45 import android.view.animation.DecelerateInterpolator; 46 import android.widget.ImageView; 47 import android.widget.TextView; 48 import android.widget.Toast; 49 50 /** 51 * This class is responsible for handling the UI animation 52 * around Android Beam. The animation consists of the following 53 * animators: 54 * 55 * mPreAnimator: scales the screenshot down to INTERMEDIATE_SCALE 56 * mSlowSendAnimator: scales the screenshot down to 0.2f (used as a "send in progress" animation) 57 * mFastSendAnimator: quickly scales the screenshot down to 0.0f (used for send success) 58 * mFadeInAnimator: fades the current activity back in (used after mFastSendAnimator completes) 59 * mScaleUpAnimator: scales the screenshot back up to full screen (used for failure or receiving) 60 * mHintAnimator: Slowly turns up the alpha of the "Touch to Beam" hint 61 * 62 * Possible sequences are: 63 * 64 * mPreAnimator => mSlowSendAnimator => mFastSendAnimator => mFadeInAnimator (send success) 65 * mPreAnimator => mSlowSendAnimator => mScaleUpAnimator (send failure) 66 * mPreAnimator => mScaleUpAnimator (p2p link broken, or data received) 67 * 68 * Note that mFastSendAnimator and mFadeInAnimator are combined in a set, as they 69 * are an atomic animation that cannot be interrupted. 70 * 71 * All methods of this class must be called on the UI thread 72 */ 73 public class SendUi implements Animator.AnimatorListener, View.OnTouchListener, 74 TimeAnimator.TimeListener, TextureView.SurfaceTextureListener { 75 static final float INTERMEDIATE_SCALE = 0.6f; 76 77 static final float[] PRE_SCREENSHOT_SCALE = {1.0f, INTERMEDIATE_SCALE}; 78 static final int PRE_DURATION_MS = 350; 79 80 static final float[] SEND_SCREENSHOT_SCALE = {INTERMEDIATE_SCALE, 0.2f}; 81 static final int SLOW_SEND_DURATION_MS = 8000; // Stretch out sending over 8s 82 static final int FAST_SEND_DURATION_MS = 350; 83 84 static final float[] SCALE_UP_SCREENSHOT_SCALE = {INTERMEDIATE_SCALE, 1.0f}; 85 static final int SCALE_UP_DURATION_MS = 300; 86 87 static final int FADE_IN_DURATION_MS = 250; 88 static final int FADE_IN_START_DELAY_MS = 350; 89 90 static final int SLIDE_OUT_DURATION_MS = 300; 91 92 static final float[] TEXT_HINT_ALPHA_RANGE = {0.0f, 1.0f}; 93 static final int TEXT_HINT_ALPHA_DURATION_MS = 500; 94 static final int TEXT_HINT_ALPHA_START_DELAY_MS = 300; 95 96 static final int FINISH_SCALE_UP = 0; 97 static final int FINISH_SEND_SUCCESS = 1; 98 99 // all members are only used on UI thread 100 final WindowManager mWindowManager; 101 final Context mContext; 102 final Display mDisplay; 103 final DisplayMetrics mDisplayMetrics; 104 final Matrix mDisplayMatrix; 105 final WindowManager.LayoutParams mWindowLayoutParams; 106 final LayoutInflater mLayoutInflater; 107 final StatusBarManager mStatusBarManager; 108 final View mScreenshotLayout; 109 final ImageView mScreenshotView; 110 final TextureView mTextureView; 111 final TextView mTextHint; 112 final Callback mCallback; 113 114 // The mFrameCounter animation is purely used to count down a certain 115 // number of (vsync'd) frames. This is needed because the first 3 116 // times the animation internally calls eglSwapBuffers(), large buffers 117 // are allocated by the graphics drivers. This causes the animation 118 // to look janky. So on platforms where we can use hardware acceleration, 119 // the animation order is: 120 // Wait for hw surface => start frame counter => start pre-animation after 3 frames 121 // For platforms where no hw acceleration can be used, the pre-animation 122 // is started immediately. 123 final TimeAnimator mFrameCounterAnimator; 124 125 final ObjectAnimator mPreAnimator; 126 final ObjectAnimator mSlowSendAnimator; 127 final ObjectAnimator mFastSendAnimator; 128 final ObjectAnimator mFadeInAnimator; 129 final ObjectAnimator mHintAnimator; 130 final ObjectAnimator mScaleUpAnimator; 131 final AnimatorSet mSuccessAnimatorSet; 132 133 // Besides animating the screenshot, the Beam UI also renders 134 // fireflies on platforms where we can do hardware-acceleration. 135 // Firefly rendering is only started once the initial 136 // "pre-animation" has scaled down the screenshot, to avoid 137 // that animation becoming janky. Likewise, the fireflies are 138 // stopped in their tracks as soon as we finish the animation, 139 // to make the finishing animation smooth. 140 final boolean mHardwareAccelerated; 141 final FireflyRenderer mFireflyRenderer; 142 143 String mToastString; 144 Bitmap mScreenshotBitmap; 145 146 boolean mAttached; 147 boolean mSending; 148 149 int mRenderedFrames; 150 151 // Used for holding the surface 152 SurfaceTexture mSurface; 153 int mSurfaceWidth; 154 int mSurfaceHeight; 155 156 interface Callback { 157 public void onSendConfirmed(); 158 } 159 160 public SendUi(Context context, Callback callback) { 161 mContext = context; 162 mCallback = callback; 163 164 mDisplayMetrics = new DisplayMetrics(); 165 mDisplayMatrix = new Matrix(); 166 mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 167 mStatusBarManager = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE); 168 169 mDisplay = mWindowManager.getDefaultDisplay(); 170 171 mLayoutInflater = (LayoutInflater) 172 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 173 mScreenshotLayout = mLayoutInflater.inflate(R.layout.screenshot, null); 174 175 mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.screenshot); 176 mScreenshotLayout.setFocusable(true); 177 178 mTextHint = (TextView) mScreenshotLayout.findViewById(R.id.calltoaction); 179 180 mTextureView = (TextureView) mScreenshotLayout.findViewById(R.id.fireflies); 181 mTextureView.setSurfaceTextureListener(this); 182 183 // We're only allowed to use hardware acceleration if 184 // isHighEndGfx() returns true - otherwise, we're too limited 185 // on resources to do it. 186 mHardwareAccelerated = ActivityManager.isHighEndGfx(); 187 int hwAccelerationFlags = mHardwareAccelerated ? 188 WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED : 0; 189 190 mWindowLayoutParams = new WindowManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 191 ViewGroup.LayoutParams.MATCH_PARENT, 0, 0, 192 WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, 193 WindowManager.LayoutParams.FLAG_FULLSCREEN 194 | hwAccelerationFlags 195 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, 196 PixelFormat.OPAQUE); 197 mWindowLayoutParams.privateFlags |= 198 WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; 199 mWindowLayoutParams.token = new Binder(); 200 201 mFrameCounterAnimator = new TimeAnimator(); 202 mFrameCounterAnimator.setTimeListener(this); 203 204 PropertyValuesHolder preX = PropertyValuesHolder.ofFloat("scaleX", PRE_SCREENSHOT_SCALE); 205 PropertyValuesHolder preY = PropertyValuesHolder.ofFloat("scaleY", PRE_SCREENSHOT_SCALE); 206 mPreAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, preX, preY); 207 mPreAnimator.setInterpolator(new DecelerateInterpolator()); 208 mPreAnimator.setDuration(PRE_DURATION_MS); 209 mPreAnimator.addListener(this); 210 211 PropertyValuesHolder postX = PropertyValuesHolder.ofFloat("scaleX", SEND_SCREENSHOT_SCALE); 212 PropertyValuesHolder postY = PropertyValuesHolder.ofFloat("scaleY", SEND_SCREENSHOT_SCALE); 213 PropertyValuesHolder alphaDown = PropertyValuesHolder.ofFloat("alpha", 214 new float[]{1.0f, 0.0f}); 215 216 mSlowSendAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, postX, postY); 217 mSlowSendAnimator.setInterpolator(new DecelerateInterpolator()); 218 mSlowSendAnimator.setDuration(SLOW_SEND_DURATION_MS); 219 220 mFastSendAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, postX, 221 postY, alphaDown); 222 mFastSendAnimator.setInterpolator(new DecelerateInterpolator()); 223 mFastSendAnimator.setDuration(FAST_SEND_DURATION_MS); 224 mFastSendAnimator.addListener(this); 225 226 PropertyValuesHolder scaleUpX = PropertyValuesHolder.ofFloat("scaleX", SCALE_UP_SCREENSHOT_SCALE); 227 PropertyValuesHolder scaleUpY = PropertyValuesHolder.ofFloat("scaleY", SCALE_UP_SCREENSHOT_SCALE); 228 229 mScaleUpAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, scaleUpX, scaleUpY); 230 mScaleUpAnimator.setInterpolator(new DecelerateInterpolator()); 231 mScaleUpAnimator.setDuration(SCALE_UP_DURATION_MS); 232 mScaleUpAnimator.addListener(this); 233 234 PropertyValuesHolder fadeIn = PropertyValuesHolder.ofFloat("alpha", 1.0f); 235 mFadeInAnimator = ObjectAnimator.ofPropertyValuesHolder(mScreenshotView, fadeIn); 236 mFadeInAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); 237 mFadeInAnimator.setDuration(FADE_IN_DURATION_MS); 238 mFadeInAnimator.setStartDelay(FADE_IN_START_DELAY_MS); 239 mFadeInAnimator.addListener(this); 240 241 PropertyValuesHolder alphaUp = PropertyValuesHolder.ofFloat("alpha", TEXT_HINT_ALPHA_RANGE); 242 mHintAnimator = ObjectAnimator.ofPropertyValuesHolder(mTextHint, alphaUp); 243 mHintAnimator.setInterpolator(null); 244 mHintAnimator.setDuration(TEXT_HINT_ALPHA_DURATION_MS); 245 mHintAnimator.setStartDelay(TEXT_HINT_ALPHA_START_DELAY_MS); 246 247 mSuccessAnimatorSet = new AnimatorSet(); 248 mSuccessAnimatorSet.playSequentially(mFastSendAnimator, mFadeInAnimator); 249 250 if (mHardwareAccelerated) { 251 mFireflyRenderer = new FireflyRenderer(context); 252 } else { 253 mFireflyRenderer = null; 254 } 255 mAttached = false; 256 } 257 258 public void takeScreenshot() { 259 mScreenshotBitmap = createScreenshot(); 260 } 261 262 /** Show pre-send animation */ 263 public void showPreSend() { 264 // Update display metrics 265 mDisplay.getRealMetrics(mDisplayMetrics); 266 267 final int statusBarHeight = mContext.getResources().getDimensionPixelSize( 268 com.android.internal.R.dimen.status_bar_height); 269 270 if (mScreenshotBitmap == null || mAttached) { 271 return; 272 } 273 mScreenshotView.setOnTouchListener(this); 274 mScreenshotView.setImageBitmap(mScreenshotBitmap); 275 mScreenshotView.setTranslationX(0f); 276 mScreenshotView.setAlpha(1.0f); 277 mScreenshotView.setPadding(0, statusBarHeight, 0, 0); 278 279 mScreenshotLayout.requestFocus(); 280 281 mTextHint.setAlpha(0.0f); 282 mTextHint.setVisibility(View.VISIBLE); 283 mHintAnimator.start(); 284 285 // Lock the orientation. 286 // The orientation from the configuration does not specify whether 287 // the orientation is reverse or not (ie landscape or reverse landscape). 288 // So we have to use SENSOR_LANDSCAPE or SENSOR_PORTRAIT to make sure 289 // we lock in portrait / landscape and have the sensor determine 290 // which way is up. 291 int orientation = mContext.getResources().getConfiguration().orientation; 292 293 switch (orientation) { 294 case Configuration.ORIENTATION_LANDSCAPE: 295 mWindowLayoutParams.screenOrientation = 296 ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; 297 break; 298 case Configuration.ORIENTATION_PORTRAIT: 299 mWindowLayoutParams.screenOrientation = 300 ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; 301 break; 302 default: 303 mWindowLayoutParams.screenOrientation = 304 ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; 305 break; 306 } 307 308 mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams); 309 // Disable statusbar pull-down 310 mStatusBarManager.disable(StatusBarManager.DISABLE_EXPAND); 311 312 mToastString = null; 313 mSending = false; 314 mAttached = true; 315 316 if (!mHardwareAccelerated) { 317 mPreAnimator.start(); 318 } // else, we will start the animation once we get the hardware surface 319 } 320 321 /** Show starting send animation */ 322 public void showStartSend() { 323 if (!mAttached) return; 324 325 // Update the starting scale - touchscreen-mashers may trigger 326 // this before the pre-animation completes. 327 float currentScale = mScreenshotView.getScaleX(); 328 PropertyValuesHolder postX = PropertyValuesHolder.ofFloat("scaleX", 329 new float[] {currentScale, 0.0f}); 330 PropertyValuesHolder postY = PropertyValuesHolder.ofFloat("scaleY", 331 new float[] {currentScale, 0.0f}); 332 333 mSlowSendAnimator.setValues(postX, postY); 334 mSlowSendAnimator.start(); 335 } 336 337 public void finishAndToast(int finishMode, String toast) { 338 if (!mAttached) return; 339 mToastString = toast; 340 341 finish(finishMode); 342 } 343 344 /** Return to initial state */ 345 public void finish(int finishMode) { 346 if (!mAttached) return; 347 348 // Stop rendering the fireflies 349 if (mFireflyRenderer != null) { 350 mFireflyRenderer.stop(); 351 } 352 353 mTextHint.setVisibility(View.GONE); 354 355 float currentScale = mScreenshotView.getScaleX(); 356 float currentAlpha = mScreenshotView.getAlpha(); 357 358 if (finishMode == FINISH_SCALE_UP) { 359 PropertyValuesHolder scaleUpX = PropertyValuesHolder.ofFloat("scaleX", 360 new float[] {currentScale, 1.0f}); 361 PropertyValuesHolder scaleUpY = PropertyValuesHolder.ofFloat("scaleY", 362 new float[] {currentScale, 1.0f}); 363 PropertyValuesHolder scaleUpAlpha = PropertyValuesHolder.ofFloat("alpha", 364 new float[] {currentAlpha, 1.0f}); 365 mScaleUpAnimator.setValues(scaleUpX, scaleUpY, scaleUpAlpha); 366 367 mScaleUpAnimator.start(); 368 } else if (finishMode == FINISH_SEND_SUCCESS){ 369 // Modify the fast send parameters to match the current scale 370 PropertyValuesHolder postX = PropertyValuesHolder.ofFloat("scaleX", 371 new float[] {currentScale, 0.0f}); 372 PropertyValuesHolder postY = PropertyValuesHolder.ofFloat("scaleY", 373 new float[] {currentScale, 0.0f}); 374 PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 375 new float[] {1.0f, 0.0f}); 376 mFastSendAnimator.setValues(postX, postY, alpha); 377 378 // Reset the fadeIn parameters to start from alpha 1 379 PropertyValuesHolder fadeIn = PropertyValuesHolder.ofFloat("alpha", 380 new float[] {0.0f, 1.0f}); 381 mFadeInAnimator.setValues(fadeIn); 382 383 mSlowSendAnimator.cancel(); 384 mSuccessAnimatorSet.start(); 385 } 386 } 387 388 public void dismiss() { 389 if (!mAttached) return; 390 391 // Immediately set to false, to prevent .cancel() calls 392 // below from immediately calling into dismiss() again. 393 mAttached = false; 394 mSurface = null; 395 mFrameCounterAnimator.cancel(); 396 mPreAnimator.cancel(); 397 mSlowSendAnimator.cancel(); 398 mFastSendAnimator.cancel(); 399 mSuccessAnimatorSet.cancel(); 400 mScaleUpAnimator.cancel(); 401 mWindowManager.removeView(mScreenshotLayout); 402 mStatusBarManager.disable(StatusBarManager.DISABLE_NONE); 403 releaseScreenshot(); 404 if (mToastString != null) { 405 Toast.makeText(mContext, mToastString, Toast.LENGTH_LONG).show(); 406 } 407 mToastString = null; 408 } 409 410 public void releaseScreenshot() { 411 mScreenshotBitmap = null; 412 } 413 414 /** 415 * @return the current display rotation in degrees 416 */ 417 static float getDegreesForRotation(int value) { 418 switch (value) { 419 case Surface.ROTATION_90: 420 return 90f; 421 case Surface.ROTATION_180: 422 return 180f; 423 case Surface.ROTATION_270: 424 return 270f; 425 } 426 return 0f; 427 } 428 429 /** 430 * Returns a screenshot of the current display contents. 431 */ 432 Bitmap createScreenshot() { 433 // We need to orient the screenshot correctly (and the Surface api seems to 434 // take screenshots only in the natural orientation of the device :!) 435 436 mDisplay.getRealMetrics(mDisplayMetrics); 437 boolean hasNavBar = mContext.getResources().getBoolean( 438 com.android.internal.R.bool.config_showNavigationBar); 439 440 float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels}; 441 float degrees = getDegreesForRotation(mDisplay.getRotation()); 442 final int statusBarHeight = mContext.getResources().getDimensionPixelSize( 443 com.android.internal.R.dimen.status_bar_height); 444 445 // Navbar has different sizes, depending on orientation 446 final int navBarHeight = hasNavBar ? mContext.getResources().getDimensionPixelSize( 447 com.android.internal.R.dimen.navigation_bar_height) : 0; 448 final int navBarHeightLandscape = hasNavBar ? mContext.getResources().getDimensionPixelSize( 449 com.android.internal.R.dimen.navigation_bar_height_landscape) : 0; 450 451 final int navBarWidth = hasNavBar ? mContext.getResources().getDimensionPixelSize( 452 com.android.internal.R.dimen.navigation_bar_width) : 0; 453 454 boolean requiresRotation = (degrees > 0); 455 if (requiresRotation) { 456 // Get the dimensions of the device in its native orientation 457 mDisplayMatrix.reset(); 458 mDisplayMatrix.preRotate(-degrees); 459 mDisplayMatrix.mapPoints(dims); 460 dims[0] = Math.abs(dims[0]); 461 dims[1] = Math.abs(dims[1]); 462 } 463 464 Bitmap bitmap = Surface.screenshot((int) dims[0], (int) dims[1]); 465 // Bail if we couldn't take the screenshot 466 if (bitmap == null) { 467 return null; 468 } 469 470 if (requiresRotation) { 471 // Rotate the screenshot to the current orientation 472 Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels, 473 mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888); 474 Canvas c = new Canvas(ss); 475 c.translate(ss.getWidth() / 2, ss.getHeight() / 2); 476 c.rotate(360f - degrees); 477 c.translate(-dims[0] / 2, -dims[1] / 2); 478 c.drawBitmap(bitmap, 0, 0, null); 479 480 bitmap = ss; 481 } 482 483 // TODO this is somewhat device-specific; need generic solution. 484 // Crop off the status bar and the nav bar 485 // Portrait: 0, statusBarHeight, width, height - status - nav 486 // Landscape: 0, statusBarHeight, width - navBar, height - status 487 int newLeft = 0; 488 int newTop = statusBarHeight; 489 int newWidth = bitmap.getWidth(); 490 int newHeight = bitmap.getHeight(); 491 float smallestWidth = (float)Math.min(newWidth, newHeight); 492 float smallestWidthDp = smallestWidth / (mDisplayMetrics.densityDpi / 160f); 493 if (bitmap.getWidth() < bitmap.getHeight()) { 494 // Portrait mode: status bar is at the top, navbar bottom, width unchanged 495 newHeight = bitmap.getHeight() - statusBarHeight - navBarHeight; 496 } else { 497 // Landscape mode: status bar is at the top 498 // Navbar: bottom on >599dp width devices, otherwise to the side 499 if (smallestWidthDp > 599) { 500 newHeight = bitmap.getHeight() - statusBarHeight - navBarHeightLandscape; 501 } else { 502 newHeight = bitmap.getHeight() - statusBarHeight; 503 newWidth = bitmap.getWidth() - navBarWidth; 504 } 505 } 506 bitmap = Bitmap.createBitmap(bitmap, newLeft, newTop, newWidth, newHeight); 507 508 return bitmap; 509 } 510 511 @Override 512 public void onAnimationStart(Animator animation) { } 513 514 @Override 515 public void onAnimationEnd(Animator animation) { 516 if (animation == mScaleUpAnimator || animation == mSuccessAnimatorSet || 517 animation == mFadeInAnimator) { 518 // These all indicate the end of the animation 519 dismiss(); 520 } else if (animation == mFastSendAnimator) { 521 // After sending is done and we've faded out, reset the scale to 1 522 // so we can fade it back in. 523 mScreenshotView.setScaleX(1.0f); 524 mScreenshotView.setScaleY(1.0f); 525 } else if (animation == mPreAnimator) { 526 if (mHardwareAccelerated && mAttached && !mSending) { 527 mFireflyRenderer.start(mSurface, mSurfaceWidth, mSurfaceHeight); 528 } 529 } 530 } 531 532 @Override 533 public void onAnimationCancel(Animator animation) { } 534 535 @Override 536 public void onAnimationRepeat(Animator animation) { } 537 538 @Override 539 public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) { 540 // This gets called on animation vsync 541 if (++mRenderedFrames < 4) { 542 // For the first 3 frames, call invalidate(); this calls eglSwapBuffers 543 // on the surface, which will allocate large buffers the first three calls 544 // as Android uses triple buffering. 545 mScreenshotLayout.invalidate(); 546 } else { 547 // Buffers should be allocated, start the real animation 548 mFrameCounterAnimator.cancel(); 549 mPreAnimator.start(); 550 } 551 } 552 553 @Override 554 public boolean onTouch(View v, MotionEvent event) { 555 if (!mAttached) { 556 return false; 557 } 558 mSending = true; 559 // Ignore future touches 560 mScreenshotView.setOnTouchListener(null); 561 562 // Cancel any ongoing animations 563 mFrameCounterAnimator.cancel(); 564 mPreAnimator.cancel(); 565 566 mCallback.onSendConfirmed(); 567 return true; 568 } 569 570 @Override 571 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 572 if (mHardwareAccelerated && !mSending) { 573 mRenderedFrames = 0; 574 575 mFrameCounterAnimator.start(); 576 mSurface = surface; 577 mSurfaceWidth = width; 578 mSurfaceHeight = height; 579 } 580 } 581 582 @Override 583 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 584 // Since we've disabled orientation changes, we can safely ignore this 585 } 586 587 @Override 588 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 589 mSurface = null; 590 591 return true; 592 } 593 594 @Override 595 public void onSurfaceTextureUpdated(SurfaceTexture surface) { } 596 597 } 598