/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
https://github.com/aizuzi/platform_frameworks_base · Java · 679 lines · 513 code · 72 blank · 94 comment · 23 complexity · cfa6ae06eeb56a3eb764f530cc385945 MD5 · raw file
- /*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.android.systemui.screenshot;
- import android.animation.Animator;
- import android.animation.AnimatorListenerAdapter;
- import android.animation.AnimatorSet;
- import android.animation.ValueAnimator;
- import android.animation.ValueAnimator.AnimatorUpdateListener;
- import android.app.Notification;
- import android.app.Notification.BigPictureStyle;
- import android.app.NotificationManager;
- import android.app.PendingIntent;
- import android.content.ContentResolver;
- import android.content.ContentValues;
- import android.content.Context;
- import android.content.Intent;
- import android.content.res.Resources;
- import android.graphics.Bitmap;
- import android.graphics.Canvas;
- import android.graphics.ColorMatrix;
- import android.graphics.ColorMatrixColorFilter;
- import android.graphics.Matrix;
- import android.graphics.Paint;
- import android.graphics.PixelFormat;
- import android.graphics.PointF;
- import android.media.MediaActionSound;
- import android.net.Uri;
- import android.os.AsyncTask;
- import android.os.Environment;
- import android.os.Process;
- import android.provider.MediaStore;
- import android.util.DisplayMetrics;
- import android.view.Display;
- import android.view.LayoutInflater;
- import android.view.MotionEvent;
- import android.view.Surface;
- import android.view.SurfaceControl;
- import android.view.View;
- import android.view.ViewGroup;
- import android.view.WindowManager;
- import android.view.animation.Interpolator;
- import android.widget.ImageView;
- import com.android.systemui.R;
- import java.io.File;
- import java.io.OutputStream;
- import java.text.DateFormat;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- /**
- * POD used in the AsyncTask which saves an image in the background.
- */
- class SaveImageInBackgroundData {
- Context context;
- Bitmap image;
- Uri imageUri;
- Runnable finisher;
- int iconSize;
- int result;
- void clearImage() {
- image = null;
- imageUri = null;
- iconSize = 0;
- }
- void clearContext() {
- context = null;
- }
- }
- /**
- * An AsyncTask that saves an image to the media store in the background.
- */
- class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Void,
- SaveImageInBackgroundData> {
- private static final String TAG = "SaveImageInBackgroundTask";
- private static final String SCREENSHOTS_DIR_NAME = "Screenshots";
- private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png";
- private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
- private final int mNotificationId;
- private final NotificationManager mNotificationManager;
- private final Notification.Builder mNotificationBuilder;
- private final File mScreenshotDir;
- private final String mImageFileName;
- private final String mImageFilePath;
- private final long mImageTime;
- private final BigPictureStyle mNotificationStyle;
- private final int mImageWidth;
- private final int mImageHeight;
- // WORKAROUND: We want the same notification across screenshots that we update so that we don't
- // spam a user's notification drawer. However, we only show the ticker for the saving state
- // and if the ticker text is the same as the previous notification, then it will not show. So
- // for now, we just add and remove a space from the ticker text to trigger the animation when
- // necessary.
- private static boolean mTickerAddSpace;
- SaveImageInBackgroundTask(Context context, SaveImageInBackgroundData data,
- NotificationManager nManager, int nId) {
- Resources r = context.getResources();
- // Prepare all the output metadata
- mImageTime = System.currentTimeMillis();
- String imageDate = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date(mImageTime));
- mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate);
- mScreenshotDir = new File(Environment.getExternalStoragePublicDirectory(
- Environment.DIRECTORY_PICTURES), SCREENSHOTS_DIR_NAME);
- mImageFilePath = new File(mScreenshotDir, mImageFileName).getAbsolutePath();
- // Create the large notification icon
- mImageWidth = data.image.getWidth();
- mImageHeight = data.image.getHeight();
- int iconSize = data.iconSize;
- final int shortSide = mImageWidth < mImageHeight ? mImageWidth : mImageHeight;
- Bitmap preview = Bitmap.createBitmap(shortSide, shortSide, data.image.getConfig());
- Canvas c = new Canvas(preview);
- Paint paint = new Paint();
- ColorMatrix desat = new ColorMatrix();
- desat.setSaturation(0.25f);
- paint.setColorFilter(new ColorMatrixColorFilter(desat));
- Matrix matrix = new Matrix();
- matrix.postTranslate((shortSide - mImageWidth) / 2,
- (shortSide - mImageHeight) / 2);
- c.drawBitmap(data.image, matrix, paint);
- c.drawColor(0x40FFFFFF);
- c.setBitmap(null);
- Bitmap croppedIcon = Bitmap.createScaledBitmap(preview, iconSize, iconSize, true);
- // Show the intermediate notification
- mTickerAddSpace = !mTickerAddSpace;
- mNotificationId = nId;
- mNotificationManager = nManager;
- mNotificationBuilder = new Notification.Builder(context)
- .setTicker(r.getString(R.string.screenshot_saving_ticker)
- + (mTickerAddSpace ? " " : ""))
- .setContentTitle(r.getString(R.string.screenshot_saving_title))
- .setContentText(r.getString(R.string.screenshot_saving_text))
- .setSmallIcon(R.drawable.stat_notify_image)
- .setWhen(System.currentTimeMillis());
- mNotificationStyle = new Notification.BigPictureStyle()
- .bigPicture(preview);
- mNotificationBuilder.setStyle(mNotificationStyle);
- Notification n = mNotificationBuilder.build();
- n.flags |= Notification.FLAG_NO_CLEAR;
- mNotificationManager.notify(nId, n);
- // On the tablet, the large icon makes the notification appear as if it is clickable (and
- // on small devices, the large icon is not shown) so defer showing the large icon until
- // we compose the final post-save notification below.
- mNotificationBuilder.setLargeIcon(croppedIcon);
- // But we still don't set it for the expanded view, allowing the smallIcon to show here.
- mNotificationStyle.bigLargeIcon(null);
- }
- @Override
- protected SaveImageInBackgroundData doInBackground(SaveImageInBackgroundData... params) {
- if (params.length != 1) return null;
- if (isCancelled()) {
- params[0].clearImage();
- params[0].clearContext();
- return null;
- }
- // By default, AsyncTask sets the worker thread to have background thread priority, so bump
- // it back up so that we save a little quicker.
- Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
- Context context = params[0].context;
- Bitmap image = params[0].image;
- Resources r = context.getResources();
- try {
- // Create screenshot directory if it doesn't exist
- mScreenshotDir.mkdirs();
- // media provider uses seconds for DATE_MODIFIED and DATE_ADDED, but milliseconds
- // for DATE_TAKEN
- long dateSeconds = mImageTime / 1000;
- // Save the screenshot to the MediaStore
- ContentValues values = new ContentValues();
- ContentResolver resolver = context.getContentResolver();
- values.put(MediaStore.Images.ImageColumns.DATA, mImageFilePath);
- values.put(MediaStore.Images.ImageColumns.TITLE, mImageFileName);
- values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, mImageFileName);
- values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, mImageTime);
- values.put(MediaStore.Images.ImageColumns.DATE_ADDED, dateSeconds);
- values.put(MediaStore.Images.ImageColumns.DATE_MODIFIED, dateSeconds);
- values.put(MediaStore.Images.ImageColumns.MIME_TYPE, "image/png");
- values.put(MediaStore.Images.ImageColumns.WIDTH, mImageWidth);
- values.put(MediaStore.Images.ImageColumns.HEIGHT, mImageHeight);
- Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
- String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime));
- String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate);
- Intent sharingIntent = new Intent(Intent.ACTION_SEND);
- sharingIntent.setType("image/png");
- sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
- sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
- Intent chooserIntent = Intent.createChooser(sharingIntent, null);
- chooserIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK
- | Intent.FLAG_ACTIVITY_NEW_TASK);
- mNotificationBuilder.addAction(R.drawable.ic_menu_share,
- r.getString(com.android.internal.R.string.share),
- PendingIntent.getActivity(context, 0, chooserIntent,
- PendingIntent.FLAG_CANCEL_CURRENT));
- OutputStream out = resolver.openOutputStream(uri);
- image.compress(Bitmap.CompressFormat.PNG, 100, out);
- out.flush();
- out.close();
- // update file size in the database
- values.clear();
- values.put(MediaStore.Images.ImageColumns.SIZE, new File(mImageFilePath).length());
- resolver.update(uri, values, null, null);
- params[0].imageUri = uri;
- params[0].image = null;
- params[0].result = 0;
- } catch (Exception e) {
- // IOException/UnsupportedOperationException may be thrown if external storage is not
- // mounted
- params[0].clearImage();
- params[0].result = 1;
- }
- // Recycle the bitmap data
- if (image != null) {
- image.recycle();
- }
- return params[0];
- }
- @Override
- protected void onPostExecute(SaveImageInBackgroundData params) {
- if (isCancelled()) {
- params.finisher.run();
- params.clearImage();
- params.clearContext();
- return;
- }
- if (params.result > 0) {
- // Show a message that we've failed to save the image to disk
- GlobalScreenshot.notifyScreenshotError(params.context, mNotificationManager);
- } else {
- // Show the final notification to indicate screenshot saved
- Resources r = params.context.getResources();
- // Create the intent to show the screenshot in gallery
- Intent launchIntent = new Intent(Intent.ACTION_VIEW);
- launchIntent.setDataAndType(params.imageUri, "image/png");
- launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mNotificationBuilder
- .setContentTitle(r.getString(R.string.screenshot_saved_title))
- .setContentText(r.getString(R.string.screenshot_saved_text))
- .setContentIntent(PendingIntent.getActivity(params.context, 0, launchIntent, 0))
- .setWhen(System.currentTimeMillis())
- .setAutoCancel(true);
- Notification n = mNotificationBuilder.build();
- n.flags &= ~Notification.FLAG_NO_CLEAR;
- mNotificationManager.notify(mNotificationId, n);
- }
- params.finisher.run();
- params.clearContext();
- }
- }
- /**
- * TODO:
- * - Performance when over gl surfaces? Ie. Gallery
- * - what do we say in the Toast? Which icon do we get if the user uses another
- * type of gallery?
- */
- class GlobalScreenshot {
- private static final String TAG = "GlobalScreenshot";
- private static final int SCREENSHOT_NOTIFICATION_ID = 789;
- private static final int SCREENSHOT_FLASH_TO_PEAK_DURATION = 130;
- private static final int SCREENSHOT_DROP_IN_DURATION = 430;
- private static final int SCREENSHOT_DROP_OUT_DELAY = 500;
- private static final int SCREENSHOT_DROP_OUT_DURATION = 430;
- private static final int SCREENSHOT_DROP_OUT_SCALE_DURATION = 370;
- private static final int SCREENSHOT_FAST_DROP_OUT_DURATION = 320;
- private static final float BACKGROUND_ALPHA = 0.5f;
- private static final float SCREENSHOT_SCALE = 1f;
- private static final float SCREENSHOT_DROP_IN_MIN_SCALE = SCREENSHOT_SCALE * 0.725f;
- private static final float SCREENSHOT_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.45f;
- private static final float SCREENSHOT_FAST_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.6f;
- private static final float SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET = 0f;
- private Context mContext;
- private WindowManager mWindowManager;
- private WindowManager.LayoutParams mWindowLayoutParams;
- private NotificationManager mNotificationManager;
- private Display mDisplay;
- private DisplayMetrics mDisplayMetrics;
- private Matrix mDisplayMatrix;
- private Bitmap mScreenBitmap;
- private View mScreenshotLayout;
- private ImageView mBackgroundView;
- private ImageView mScreenshotView;
- private ImageView mScreenshotFlash;
- private AnimatorSet mScreenshotAnimation;
- private int mNotificationIconSize;
- private float mBgPadding;
- private float mBgPaddingScale;
- private AsyncTask<SaveImageInBackgroundData, Void, SaveImageInBackgroundData> mSaveInBgTask;
- private MediaActionSound mCameraSound;
- /**
- * @param context everything needs a context :(
- */
- public GlobalScreenshot(Context context) {
- Resources r = context.getResources();
- mContext = context;
- LayoutInflater layoutInflater = (LayoutInflater)
- context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- // Inflate the screenshot layout
- mDisplayMatrix = new Matrix();
- mScreenshotLayout = layoutInflater.inflate(R.layout.global_screenshot, null);
- mBackgroundView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_background);
- mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot);
- mScreenshotFlash = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_flash);
- mScreenshotLayout.setFocusable(true);
- mScreenshotLayout.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- // Intercept and ignore all touch events
- return true;
- }
- });
- // Setup the window that we are going to use
- mWindowLayoutParams = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
- WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY,
- WindowManager.LayoutParams.FLAG_FULLSCREEN
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
- PixelFormat.TRANSLUCENT);
- mWindowLayoutParams.setTitle("ScreenshotAnimation");
- mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- mNotificationManager =
- (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- mDisplay = mWindowManager.getDefaultDisplay();
- mDisplayMetrics = new DisplayMetrics();
- mDisplay.getRealMetrics(mDisplayMetrics);
- // Get the various target sizes
- mNotificationIconSize =
- r.getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
- // Scale has to account for both sides of the bg
- mBgPadding = (float) r.getDimensionPixelSize(R.dimen.global_screenshot_bg_padding);
- mBgPaddingScale = mBgPadding / mDisplayMetrics.widthPixels;
- // Setup the Camera shutter sound
- mCameraSound = new MediaActionSound();
- mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
- }
- /**
- * Creates a new worker thread and saves the screenshot to the media store.
- */
- private void saveScreenshotInWorkerThread(Runnable finisher) {
- SaveImageInBackgroundData data = new SaveImageInBackgroundData();
- data.context = mContext;
- data.image = mScreenBitmap;
- data.iconSize = mNotificationIconSize;
- data.finisher = finisher;
- if (mSaveInBgTask != null) {
- mSaveInBgTask.cancel(false);
- }
- mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data, mNotificationManager,
- SCREENSHOT_NOTIFICATION_ID).execute(data);
- }
- /**
- * @return the current display rotation in degrees
- */
- private float getDegreesForRotation(int value) {
- switch (value) {
- case Surface.ROTATION_90:
- return 360f - 90f;
- case Surface.ROTATION_180:
- return 360f - 180f;
- case Surface.ROTATION_270:
- return 360f - 270f;
- }
- return 0f;
- }
- /**
- * Takes a screenshot of the current display and shows an animation.
- */
- void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {
- // We need to orient the screenshot correctly (and the Surface api seems to take screenshots
- // only in the natural orientation of the device :!)
- mDisplay.getRealMetrics(mDisplayMetrics);
- float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels};
- float degrees = getDegreesForRotation(mDisplay.getRotation());
- boolean requiresRotation = (degrees > 0);
- if (requiresRotation) {
- // Get the dimensions of the device in its native orientation
- mDisplayMatrix.reset();
- mDisplayMatrix.preRotate(-degrees);
- mDisplayMatrix.mapPoints(dims);
- dims[0] = Math.abs(dims[0]);
- dims[1] = Math.abs(dims[1]);
- }
- // Take the screenshot
- mScreenBitmap = SurfaceControl.screenshot((int) dims[0], (int) dims[1]);
- if (mScreenBitmap == null) {
- notifyScreenshotError(mContext, mNotificationManager);
- finisher.run();
- return;
- }
- if (requiresRotation) {
- // Rotate the screenshot to the current orientation
- Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,
- mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(ss);
- c.translate(ss.getWidth() / 2, ss.getHeight() / 2);
- c.rotate(degrees);
- c.translate(-dims[0] / 2, -dims[1] / 2);
- c.drawBitmap(mScreenBitmap, 0, 0, null);
- c.setBitmap(null);
- // Recycle the previous bitmap
- mScreenBitmap.recycle();
- mScreenBitmap = ss;
- }
- // Optimizations
- mScreenBitmap.setHasAlpha(false);
- mScreenBitmap.prepareToDraw();
- // Start the post-screenshot animation
- startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
- statusBarVisible, navBarVisible);
- }
- /**
- * Starts the animation after taking the screenshot
- */
- private void startAnimation(final Runnable finisher, int w, int h, boolean statusBarVisible,
- boolean navBarVisible) {
- // Add the view for the animation
- mScreenshotView.setImageBitmap(mScreenBitmap);
- mScreenshotLayout.requestFocus();
- // Setup the animation with the screenshot just taken
- if (mScreenshotAnimation != null) {
- mScreenshotAnimation.end();
- mScreenshotAnimation.removeAllListeners();
- }
- mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
- ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation();
- ValueAnimator screenshotFadeOutAnim = createScreenshotDropOutAnimation(w, h,
- statusBarVisible, navBarVisible);
- mScreenshotAnimation = new AnimatorSet();
- mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim);
- mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // Save the screenshot once we have a bit of time now
- saveScreenshotInWorkerThread(finisher);
- mWindowManager.removeView(mScreenshotLayout);
- // Clear any references to the bitmap
- mScreenBitmap = null;
- mScreenshotView.setImageBitmap(null);
- }
- });
- mScreenshotLayout.post(new Runnable() {
- @Override
- public void run() {
- // Play the shutter sound to notify that we've taken a screenshot
- mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
- mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- mScreenshotView.buildLayer();
- mScreenshotAnimation.start();
- }
- });
- }
- private ValueAnimator createScreenshotDropInAnimation() {
- final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
- / SCREENSHOT_DROP_IN_DURATION);
- final float flashDurationPct = 2f * flashPeakDurationPct;
- final Interpolator flashAlphaInterpolator = new Interpolator() {
- @Override
- public float getInterpolation(float x) {
- // Flash the flash view in and out quickly
- if (x <= flashDurationPct) {
- return (float) Math.sin(Math.PI * (x / flashDurationPct));
- }
- return 0;
- }
- };
- final Interpolator scaleInterpolator = new Interpolator() {
- @Override
- public float getInterpolation(float x) {
- // We start scaling when the flash is at it's peak
- if (x < flashPeakDurationPct) {
- return 0;
- }
- return (x - flashDurationPct) / (1f - flashDurationPct);
- }
- };
- ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
- anim.setDuration(SCREENSHOT_DROP_IN_DURATION);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mBackgroundView.setAlpha(0f);
- mBackgroundView.setVisibility(View.VISIBLE);
- mScreenshotView.setAlpha(0f);
- mScreenshotView.setTranslationX(0f);
- mScreenshotView.setTranslationY(0f);
- mScreenshotView.setScaleX(SCREENSHOT_SCALE + mBgPaddingScale);
- mScreenshotView.setScaleY(SCREENSHOT_SCALE + mBgPaddingScale);
- mScreenshotView.setVisibility(View.VISIBLE);
- mScreenshotFlash.setAlpha(0f);
- mScreenshotFlash.setVisibility(View.VISIBLE);
- }
- @Override
- public void onAnimationEnd(android.animation.Animator animation) {
- mScreenshotFlash.setVisibility(View.GONE);
- }
- });
- anim.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = (Float) animation.getAnimatedValue();
- float scaleT = (SCREENSHOT_SCALE + mBgPaddingScale)
- - scaleInterpolator.getInterpolation(t)
- * (SCREENSHOT_SCALE - SCREENSHOT_DROP_IN_MIN_SCALE);
- mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);
- mScreenshotView.setAlpha(t);
- mScreenshotView.setScaleX(scaleT);
- mScreenshotView.setScaleY(scaleT);
- mScreenshotFlash.setAlpha(flashAlphaInterpolator.getInterpolation(t));
- }
- });
- return anim;
- }
- private ValueAnimator createScreenshotDropOutAnimation(int w, int h, boolean statusBarVisible,
- boolean navBarVisible) {
- ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
- anim.setStartDelay(SCREENSHOT_DROP_OUT_DELAY);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mBackgroundView.setVisibility(View.GONE);
- mScreenshotView.setVisibility(View.GONE);
- mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
- }
- });
- if (!statusBarVisible || !navBarVisible) {
- // There is no status bar/nav bar, so just fade the screenshot away in place
- anim.setDuration(SCREENSHOT_FAST_DROP_OUT_DURATION);
- anim.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = (Float) animation.getAnimatedValue();
- float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
- - t * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_FAST_DROP_OUT_MIN_SCALE);
- mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
- mScreenshotView.setAlpha(1f - t);
- mScreenshotView.setScaleX(scaleT);
- mScreenshotView.setScaleY(scaleT);
- }
- });
- } else {
- // In the case where there is a status bar, animate to the origin of the bar (top-left)
- final float scaleDurationPct = (float) SCREENSHOT_DROP_OUT_SCALE_DURATION
- / SCREENSHOT_DROP_OUT_DURATION;
- final Interpolator scaleInterpolator = new Interpolator() {
- @Override
- public float getInterpolation(float x) {
- if (x < scaleDurationPct) {
- // Decelerate, and scale the input accordingly
- return (float) (1f - Math.pow(1f - (x / scaleDurationPct), 2f));
- }
- return 1f;
- }
- };
- // Determine the bounds of how to scale
- float halfScreenWidth = (w - 2f * mBgPadding) / 2f;
- float halfScreenHeight = (h - 2f * mBgPadding) / 2f;
- final float offsetPct = SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET;
- final PointF finalPos = new PointF(
- -halfScreenWidth + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenWidth,
- -halfScreenHeight + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenHeight);
- // Animate the screenshot to the status bar
- anim.setDuration(SCREENSHOT_DROP_OUT_DURATION);
- anim.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = (Float) animation.getAnimatedValue();
- float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
- - scaleInterpolator.getInterpolation(t)
- * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_DROP_OUT_MIN_SCALE);
- mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
- mScreenshotView.setAlpha(1f - scaleInterpolator.getInterpolation(t));
- mScreenshotView.setScaleX(scaleT);
- mScreenshotView.setScaleY(scaleT);
- mScreenshotView.setTranslationX(t * finalPos.x);
- mScreenshotView.setTranslationY(t * finalPos.y);
- }
- });
- }
- return anim;
- }
- static void notifyScreenshotError(Context context, NotificationManager nManager) {
- Resources r = context.getResources();
- // Clear all existing notification, compose the new notification and show it
- Notification.Builder b = new Notification.Builder(context)
- .setTicker(r.getString(R.string.screenshot_failed_title))
- .setContentTitle(r.getString(R.string.screenshot_failed_title))
- .setContentText(r.getString(R.string.screenshot_failed_text))
- .setSmallIcon(R.drawable.stat_notify_image_error)
- .setWhen(System.currentTimeMillis())
- .setAutoCancel(true);
- Notification n =
- new Notification.BigTextStyle(b)
- .bigText(r.getString(R.string.screenshot_failed_text))
- .build();
- nManager.notify(SCREENSHOT_NOTIFICATION_ID, n);
- }
- }