|
|
@@ -0,0 +1,362 @@
|
|
|
+package com.sheep.gamegroup.util;
|
|
|
+
|
|
|
+/*
|
|
|
+ * 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.
|
|
|
+ */
|
|
|
+
|
|
|
+import android.animation.Animator;
|
|
|
+import android.animation.AnimatorListenerAdapter;
|
|
|
+import android.animation.AnimatorSet;
|
|
|
+import android.animation.ValueAnimator;
|
|
|
+import android.animation.ValueAnimator.AnimatorUpdateListener;
|
|
|
+import android.content.Context;
|
|
|
+import android.content.res.Resources;
|
|
|
+import android.graphics.Bitmap;
|
|
|
+import android.graphics.PixelFormat;
|
|
|
+import android.graphics.PointF;
|
|
|
+import android.media.MediaActionSound;
|
|
|
+import android.util.DisplayMetrics;
|
|
|
+import android.view.Display;
|
|
|
+import android.view.LayoutInflater;
|
|
|
+import android.view.MotionEvent;
|
|
|
+import android.view.View;
|
|
|
+import android.view.ViewGroup;
|
|
|
+import android.view.WindowManager;
|
|
|
+import android.view.animation.Interpolator;
|
|
|
+import android.widget.ImageView;
|
|
|
+
|
|
|
+import com.sheep.jiuyan.samllsheep.R;
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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?
|
|
|
+ */
|
|
|
+public class GlobalScreenShot {
|
|
|
+ private static final String TAG = "GlobalScreenshot";
|
|
|
+
|
|
|
+ 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 Display mDisplay;
|
|
|
+ private DisplayMetrics mDisplayMetrics;
|
|
|
+
|
|
|
+ private Bitmap mScreenBitmap;
|
|
|
+ private View mScreenshotLayout;
|
|
|
+ private ImageView mBackgroundView;
|
|
|
+ private ImageView mScreenshotView;
|
|
|
+ private ImageView mScreenshotFlash;
|
|
|
+
|
|
|
+ private AnimatorSet mScreenshotAnimation;
|
|
|
+
|
|
|
+ private float mBgPadding;
|
|
|
+ private float mBgPaddingScale;
|
|
|
+
|
|
|
+ private MediaActionSound mCameraSound;
|
|
|
+
|
|
|
+
|
|
|
+ private onScreenShotListener mOnScreenShotListener;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @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
|
|
|
+ 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_SYSTEM_ALERT,
|
|
|
+ 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);
|
|
|
+
|
|
|
+ mDisplay = mWindowManager.getDefaultDisplay();
|
|
|
+ mDisplayMetrics = new DisplayMetrics();
|
|
|
+ mDisplay.getRealMetrics(mDisplayMetrics);
|
|
|
+
|
|
|
+ // Scale has to account for both sides of the bg
|
|
|
+ mBgPadding = (float) r.getDimensionPixelSize(R.dimen.content_padding_1);
|
|
|
+ mBgPaddingScale = mBgPadding / mDisplayMetrics.widthPixels;
|
|
|
+
|
|
|
+ // Setup the Camera shutter sound
|
|
|
+ mCameraSound = new MediaActionSound();
|
|
|
+ mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Takes a screenshot of the current display and shows an animation.
|
|
|
+ */
|
|
|
+ public void takeScreenshot(Bitmap bitmap, onScreenShotListener onScreenShotListener, boolean statusBarVisible, boolean navBarVisible) {
|
|
|
+ // Take the screenshot
|
|
|
+ mScreenBitmap = bitmap;
|
|
|
+ this.mOnScreenShotListener = onScreenShotListener;
|
|
|
+
|
|
|
+ if (mOnScreenShotListener != null) {
|
|
|
+ mOnScreenShotListener.onStartShot();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (mScreenBitmap == null) {
|
|
|
+ notifyScreenshotError(mContext);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Optimizations
|
|
|
+ mScreenBitmap.setHasAlpha(false);
|
|
|
+ mScreenBitmap.prepareToDraw();
|
|
|
+
|
|
|
+ // Start the post-screenshot animation
|
|
|
+ startAnimation(mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
|
|
|
+ statusBarVisible, navBarVisible);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Starts the animation after taking the screenshot
|
|
|
+ */
|
|
|
+ private void startAnimation(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();
|
|
|
+ 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(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;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void notifyScreenshotError(Context context) {
|
|
|
+ if (mOnScreenShotListener != null) {
|
|
|
+ mOnScreenShotListener.onFinishShot(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void saveScreenshotInWorkerThread() {
|
|
|
+ if (mOnScreenShotListener != null) {
|
|
|
+ mOnScreenShotListener.onFinishShot(true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public interface onScreenShotListener {
|
|
|
+
|
|
|
+ void onStartShot();
|
|
|
+
|
|
|
+ void onFinishShot(boolean success);
|
|
|
+ }
|
|
|
+}
|