summaryrefslogtreecommitdiffstats
path: root/packages/SystemUI
diff options
context:
space:
mode:
authorWinson Chung <winsonc@google.com>2011-06-27 13:15:32 -0700
committerWinson Chung <winsonc@google.com>2011-07-01 10:47:07 -0700
commit9112ec3039dda4186c6f957981237c0691db2269 (patch)
tree684de37f923204e28db85f85b336b68d59dc58e9 /packages/SystemUI
parente2474bc711c5534ab20949206c54eb364d9a8ac5 (diff)
downloadframeworks_base-9112ec3039dda4186c6f957981237c0691db2269.zip
frameworks_base-9112ec3039dda4186c6f957981237c0691db2269.tar.gz
frameworks_base-9112ec3039dda4186c6f957981237c0691db2269.tar.bz2
Binding Pwr+VolDown to take screenshot.
Change-Id: I77b018538f6bc870f59df0b5336ba95f4582beec
Diffstat (limited to 'packages/SystemUI')
-rw-r--r--packages/SystemUI/AndroidManifest.xml5
-rw-r--r--packages/SystemUI/res/drawable-hdpi/global_screenshot_background.9.pngbin0 -> 358 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/global_screenshot_background.9.pngbin0 -> 358 bytes
-rw-r--r--packages/SystemUI/res/layout/global_screenshot.xml40
-rw-r--r--packages/SystemUI/res/values/strings.xml5
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java384
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java50
7 files changed, 484 insertions, 0 deletions
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6d8eab6..f42cbbf 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -25,6 +25,11 @@
android:exported="true"
/>
+ <!-- started from PhoneWindowManager
+ TODO: Should have an android:permission attribute -->
+ <service android:name=".screenshot.TakeScreenshotService"
+ android:exported="false" />
+
<activity android:name=".usb.UsbPreferenceActivity"
android:theme="@*android:style/Theme.Holo.Dialog.Alert"
android:excludeFromRecents="true">
diff --git a/packages/SystemUI/res/drawable-hdpi/global_screenshot_background.9.png b/packages/SystemUI/res/drawable-hdpi/global_screenshot_background.9.png
new file mode 100644
index 0000000..e14111d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/global_screenshot_background.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/global_screenshot_background.9.png b/packages/SystemUI/res/drawable-mdpi/global_screenshot_background.9.png
new file mode 100644
index 0000000..e14111d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/global_screenshot_background.9.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
new file mode 100644
index 0000000..6cb8799
--- /dev/null
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <ImageView android:id="@+id/global_screenshot_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#FF000000"
+ android:visibility="gone" />
+ <FrameLayout
+ android:id="@+id/global_screenshot_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@drawable/global_screenshot_background"
+ android:visibility="gone">
+ <ImageView android:id="@+id/global_screenshot"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:adjustViewBounds="true" />
+ </FrameLayout>
+ <ImageView android:id="@+id/global_screenshot_flash"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#FFFFFFFF"
+ android:visibility="gone" />
+</FrameLayout>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 86e0cd0..70f9b75 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -165,4 +165,9 @@
<string name="use_ptp_button_title">Mount as a camera (PTP)</string>
<!-- Label for the installer CD image option in UsbPreferenceActivity. [CHAR LIMIT=50] -->
<string name="installer_cd_button_title">Install Android File Transfer application for Mac</string>
+
+ <!-- toast message displayed when a screenshot is saved to the Gallery. -->
+ <string name="screenshot_saving_toast">Screenshot saved to Gallery</string>
+ <!-- toast message displayed when we fail to take a screenshot. -->
+ <string name="screenshot_failed_toast">Could not save screenshot</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
new file mode 100644
index 0000000..83a5578
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -0,0 +1,384 @@
+/*
+ * 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.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.app.Activity;
+import android.content.ContentValues;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.PixelFormat;
+import android.media.MediaScannerConnection;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.ServiceManager;
+import android.provider.MediaStore;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.systemui.R;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.Thread;
+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;
+ int result;
+}
+
+/**
+ * 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_PATH_TEMPLATE = "%s/%s/Screenshot_%s-%d.png";
+
+ @Override
+ protected SaveImageInBackgroundData doInBackground(SaveImageInBackgroundData... params) {
+ if (params.length != 1) return null;
+
+ Context context = params[0].context;
+ Bitmap image = params[0].image;
+
+ try{
+ long currentTime = System.currentTimeMillis();
+ String date = new SimpleDateFormat("MM-dd-yy-kk-mm-ss").format(new Date(currentTime));
+ String imageDir = Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_PICTURES).getAbsolutePath();
+ String imageFilePath = String.format(SCREENSHOT_FILE_PATH_TEMPLATE,
+ imageDir, SCREENSHOTS_DIR_NAME,
+ date, currentTime % 1000);
+
+ // Save the screenshot to the MediaStore
+ ContentValues values = new ContentValues();
+ values.put(MediaStore.Images.ImageColumns.DATA, imageFilePath);
+ values.put(MediaStore.Images.ImageColumns.TITLE, "Screenshot");
+ values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, "Screenshot");
+ values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, currentTime);
+ values.put(MediaStore.Images.ImageColumns.DATE_ADDED, currentTime);
+ values.put(MediaStore.Images.ImageColumns.DATE_MODIFIED, currentTime);
+ values.put(MediaStore.Images.ImageColumns.MIME_TYPE, "image/png");
+ Uri uri = context.getContentResolver().insert(
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
+
+ OutputStream out = context.getContentResolver().openOutputStream(uri);
+ image.compress(Bitmap.CompressFormat.PNG, 100, out);
+ out.flush();
+ out.close();
+
+ params[0].result = 0;
+ }catch(IOException e){
+ params[0].result = 1;
+ }
+
+ return params[0];
+ };
+
+ @Override
+ protected void onPostExecute(SaveImageInBackgroundData params) {
+ if (params.result > 0) {
+ // Show a message that we've failed to save the image to disk
+ Toast.makeText(params.context, R.string.screenshot_failed_toast,
+ Toast.LENGTH_SHORT).show();
+ } else {
+ // Show a message that we've saved the screenshot to disk
+ Toast.makeText(params.context, R.string.screenshot_saving_toast,
+ Toast.LENGTH_SHORT).show();
+ }
+ };
+}
+
+/**
+ * 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_FADE_IN_DURATION = 900;
+ private static final int SCREENSHOT_FADE_OUT_DELAY = 1000;
+ private static final int SCREENSHOT_FADE_OUT_DURATION = 450;
+ private static final int TOAST_FADE_IN_DURATION = 500;
+ private static final int TOAST_FADE_OUT_DELAY = 1000;
+ private static final int TOAST_FADE_OUT_DURATION = 500;
+ private static final float BACKGROUND_ALPHA = 0.65f;
+ private static final float SCREENSHOT_SCALE = 0.85f;
+ private static final float SCREENSHOT_MIN_SCALE = 0.7f;
+ private static final float SCREENSHOT_ROTATION = -6.75f; // -12.5f;
+
+ private Context mContext;
+ private LayoutInflater mLayoutInflater;
+ private IWindowManager mIWindowManager;
+ private WindowManager mWindowManager;
+ private WindowManager.LayoutParams mWindowLayoutParams;
+ private Display mDisplay;
+ private DisplayMetrics mDisplayMetrics;
+ private Matrix mDisplayMatrix;
+
+ private Bitmap mScreenBitmap;
+ private View mScreenshotLayout;
+ private ImageView mBackgroundView;
+ private FrameLayout mScreenshotContainerView;
+ private ImageView mScreenshotView;
+
+ private AnimatorSet mScreenshotAnimation;
+
+ // General use cubic interpolator
+ final TimeInterpolator mCubicInterpolator = new TimeInterpolator() {
+ public float getInterpolation(float t) {
+ return t*t*t;
+ }
+ };
+ // The interpolator used to control the background alpha at the start of the animation
+ final TimeInterpolator mBackgroundViewAlphaInterpolator = new TimeInterpolator() {
+ public float getInterpolation(float t) {
+ float tStep = 0.35f;
+ if (t < tStep) {
+ return t * (1f / tStep);
+ } else {
+ return 1f;
+ }
+ }
+ };
+
+ /**
+ * @param context everything needs a context :(
+ */
+ public GlobalScreenshot(Context context) {
+ mContext = context;
+ mLayoutInflater = (LayoutInflater)
+ context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ // Inflate the screenshot layout
+ mDisplayMetrics = new DisplayMetrics();
+ mDisplayMatrix = new Matrix();
+ mScreenshotLayout = mLayoutInflater.inflate(R.layout.global_screenshot, null);
+ mBackgroundView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_background);
+ mScreenshotContainerView = (FrameLayout) mScreenshotLayout.findViewById(R.id.global_screenshot_container);
+ mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot);
+ 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
+ mIWindowManager = IWindowManager.Stub.asInterface(
+ ServiceManager.getService(Context.WINDOW_SERVICE));
+ 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_HARDWARE_ACCELERATED_SYSTEM
+ | WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
+ PixelFormat.TRANSLUCENT);
+ mWindowLayoutParams.token = new Binder();
+ mWindowLayoutParams.setTitle("ScreenshotAnimation");
+ mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ mDisplay = mWindowManager.getDefaultDisplay();
+ }
+
+ /**
+ * Creates a new worker thread and saves the screenshot to the media store.
+ */
+ private void saveScreenshotInWorkerThread() {
+ SaveImageInBackgroundData data = new SaveImageInBackgroundData();
+ data.context = mContext;
+ data.image = mScreenBitmap;
+ new SaveImageInBackgroundTask().execute(data);
+ }
+
+ /**
+ * @return the current display rotation in degrees
+ */
+ private float getDegreesForRotation(int value) {
+ switch (value) {
+ case Surface.ROTATION_90:
+ return 90f;
+ case Surface.ROTATION_180:
+ return 180f;
+ case Surface.ROTATION_270:
+ return 270f;
+ }
+ return 0f;
+ }
+
+ /**
+ * Takes a screenshot of the current display and shows an animation.
+ */
+ void takeScreenshot() {
+ // 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]);
+ }
+ mScreenBitmap = Surface.screenshot((int) dims[0], (int) dims[1]);
+ 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(360f - degrees);
+ c.translate(-dims[0] / 2, -dims[1] / 2);
+ c.drawBitmap(mScreenBitmap, 0, 0, null);
+ mScreenBitmap = ss;
+ }
+
+ // If we couldn't take the screenshot, notify the user
+ if (mScreenBitmap == null) {
+ Toast.makeText(mContext, R.string.screenshot_failed_toast,
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ // Start the post-screenshot animation
+ startAnimation();
+ }
+
+
+ /**
+ * Starts the animation after taking the screenshot
+ */
+ private void startAnimation() {
+ // Add the view for the animation
+ mScreenshotView.setImageBitmap(mScreenBitmap);
+ mScreenshotLayout.requestFocus();
+
+ // Setup the animation with the screenshot just taken
+ if (mScreenshotAnimation != null) {
+ mScreenshotAnimation.end();
+ }
+
+ mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
+ ValueAnimator screenshotFadeInAnim = createScreenshotFadeInAnimation();
+ ValueAnimator screenshotFadeOutAnim = createScreenshotFadeOutAnimation();
+ mScreenshotAnimation = new AnimatorSet();
+ mScreenshotAnimation.play(screenshotFadeInAnim).before(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);
+ }
+ });
+ mScreenshotAnimation.start();
+ }
+ private ValueAnimator createScreenshotFadeInAnimation() {
+ ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+ anim.setInterpolator(mCubicInterpolator);
+ anim.setDuration(SCREENSHOT_FADE_IN_DURATION);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mBackgroundView.setVisibility(View.VISIBLE);
+ mScreenshotContainerView.setVisibility(View.VISIBLE);
+ }
+ });
+ anim.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float t = ((Float) animation.getAnimatedValue()).floatValue();
+ mBackgroundView.setAlpha(mBackgroundViewAlphaInterpolator.getInterpolation(t) *
+ BACKGROUND_ALPHA);
+ float scaleT = SCREENSHOT_SCALE + (1f - t) * SCREENSHOT_SCALE;
+ mScreenshotContainerView.setAlpha(t*t*t*t);
+ mScreenshotContainerView.setScaleX(scaleT);
+ mScreenshotContainerView.setScaleY(scaleT);
+ mScreenshotContainerView.setRotation(t * SCREENSHOT_ROTATION);
+ }
+ });
+ return anim;
+ }
+ private ValueAnimator createScreenshotFadeOutAnimation() {
+ ValueAnimator anim = ValueAnimator.ofFloat(1f, 0f);
+ anim.setInterpolator(mCubicInterpolator);
+ anim.setStartDelay(SCREENSHOT_FADE_OUT_DELAY);
+ anim.setDuration(SCREENSHOT_FADE_OUT_DURATION);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mBackgroundView.setVisibility(View.GONE);
+ mScreenshotContainerView.setVisibility(View.GONE);
+ }
+ });
+ anim.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float t = ((Float) animation.getAnimatedValue()).floatValue();
+ float scaleT = SCREENSHOT_MIN_SCALE +
+ t*(SCREENSHOT_SCALE - SCREENSHOT_MIN_SCALE);
+ mScreenshotContainerView.setAlpha(t);
+ mScreenshotContainerView.setScaleX(scaleT);
+ mScreenshotContainerView.setScaleY(scaleT);
+ mBackgroundView.setAlpha(t * t * BACKGROUND_ALPHA);
+ }
+ });
+ return anim;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
new file mode 100644
index 0000000..35eaedf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -0,0 +1,50 @@
+/*
+ * 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.app.Service;
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+
+import com.android.systemui.R;
+
+public class TakeScreenshotService extends Service {
+ private static final String TAG = "TakeScreenshotService";
+
+ private static GlobalScreenshot mScreenshot;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (mScreenshot == null) {
+ mScreenshot = new GlobalScreenshot(this);
+ }
+ mScreenshot.takeScreenshot();
+ return null;
+ }
+}