Screenshot global actions item.
Refactor TakeScreenshot code out of PhoneWindowManager;
add power menu item for Screenshot.
Test: runtest systemui
Change-Id: Ie8602386cd59f8fd991369d2117f7c4d76f65e41
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
new file mode 100644
index 0000000..f7ea787
--- /dev/null
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -0,0 +1,128 @@
+package com.android.internal.util;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+public class ScreenshotHelper {
+ private static final String TAG = "ScreenshotHelper";
+
+ private static final String SYSUI_PACKAGE = "com.android.systemui";
+ private static final String SYSUI_SCREENSHOT_SERVICE =
+ "com.android.systemui.screenshot.TakeScreenshotService";
+ private static final String SYSUI_SCREENSHOT_ERROR_RECEIVER =
+ "com.android.systemui.screenshot.ScreenshotServiceErrorReceiver";
+
+ // Time until we give up on the screenshot & show an error instead.
+ private final int SCREENSHOT_TIMEOUT_MS = 10000;
+
+ private final Object mScreenshotLock = new Object();
+ private ServiceConnection mScreenshotConnection = null;
+ private final Context mContext;
+
+ public ScreenshotHelper(Context context) {
+ mContext = context;
+ }
+
+ public void takeScreenshot(final int screenshotType, final boolean hasStatus,
+ final boolean hasNav, Handler handler) {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != null) {
+ return;
+ }
+ final ComponentName serviceComponent = new ComponentName(SYSUI_PACKAGE,
+ SYSUI_SCREENSHOT_SERVICE);
+ final Intent serviceIntent = new Intent();
+
+ final Runnable mScreenshotTimeout = new Runnable() {
+ @Override public void run() {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != null) {
+ mContext.unbindService(mScreenshotConnection);
+ mScreenshotConnection = null;
+ notifyScreenshotError();
+ }
+ }
+ }
+ };
+
+ serviceIntent.setComponent(serviceComponent);
+ ServiceConnection conn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != this) {
+ return;
+ }
+ Messenger messenger = new Messenger(service);
+ Message msg = Message.obtain(null, screenshotType);
+ final ServiceConnection myConn = this;
+ Handler h = new Handler(handler.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection == myConn) {
+ mContext.unbindService(mScreenshotConnection);
+ mScreenshotConnection = null;
+ handler.removeCallbacks(mScreenshotTimeout);
+ }
+ }
+ }
+ };
+ msg.replyTo = new Messenger(h);
+ msg.arg1 = hasStatus ? 1: 0;
+ msg.arg2 = hasNav ? 1: 0;
+ try {
+ messenger.send(msg);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't take screenshot: " + e);
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != null) {
+ mContext.unbindService(mScreenshotConnection);
+ mScreenshotConnection = null;
+ handler.removeCallbacks(mScreenshotTimeout);
+ notifyScreenshotError();
+ }
+ }
+ }
+ };
+ if (mContext.bindServiceAsUser(serviceIntent, conn,
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
+ UserHandle.CURRENT)) {
+ mScreenshotConnection = conn;
+ handler.postDelayed(mScreenshotTimeout, SCREENSHOT_TIMEOUT_MS);
+ }
+ }
+ }
+
+ /**
+ * Notifies the screenshot service to show an error.
+ */
+ private void notifyScreenshotError() {
+ // If the service process is killed, then ask it to clean up after itself
+ final ComponentName errorComponent = new ComponentName(SYSUI_PACKAGE,
+ SYSUI_SCREENSHOT_ERROR_RECEIVER);
+ // Broadcast needs to have a valid action. We'll just pick
+ // a generic one, since the receiver here doesn't care.
+ Intent errorIntent = new Intent(Intent.ACTION_USER_PRESENT);
+ errorIntent.setComponent(errorComponent);
+ errorIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
+ Intent.FLAG_RECEIVER_FOREGROUND);
+ mContext.sendBroadcastAsUser(errorIntent, UserHandle.CURRENT);
+ }
+
+}
diff --git a/core/res/res/drawable/ic_screenshot.xml b/core/res/res/drawable/ic_screenshot.xml
new file mode 100644
index 0000000..3074b28
--- /dev/null
+++ b/core/res/res/drawable/ic_screenshot.xml
@@ -0,0 +1,33 @@
+<!--
+Copyright (C) 2018 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M0,0h24v24H0V0z"
+ android:fillColor="#00000000"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17.0,1.0L7.0,1.0C5.9,1.0 5.0,1.9 5.0,3.0l0.0,18.0c0.0,1.1 0.9,2.0 2.0,2.0l10.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L19.0,3.0C19.0,1.9 18.1,1.0 17.0,1.0zM17.0,20.0L7.0,20.0L7.0,4.0l10.0,0.0L17.0,20.0z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13.0,6.0l-4.0,0.0 0.0,4.0 1.5,0.0 0.0,-2.5 2.5,0.0z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.0,18.0l4.0,0.0 0.0,-4.0 -1.5,0.0 0.0,2.5 -2.5,0.0z"/>
+</vector>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f1e9662..ed94f84 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2481,6 +2481,7 @@
<string-array translatable="false" name="config_globalActionsList">
<item>power</item>
<item>restart</item>
+ <item>screenshot</item>
<item>logout</item>
<item>bugreport</item>
<item>users</item>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f671b6b..b2fa294 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -484,6 +484,9 @@
<!-- label for item that logouts the current user -->
<string name="global_action_logout">End session</string>
+ <!-- label for screenshot item in power menu -->
+ <string name="global_action_screenshot">Screenshot</string>
+
<!-- Take bug report menu title [CHAR LIMIT=NONE] -->
<string name="bugreport_title">Take bug report</string>
<!-- Message in bugreport dialog describing what it does [CHAR LIMIT=NONE] -->
@@ -4800,13 +4803,10 @@
<!--Battery saver warning. STOPSHIP: Remove it eventually. -->
<string name="battery_saver_warning_title" translatable="false">Extreme battery saver</string>
-
<!-- Label for the uninstall button on the harmful app warning dialog. -->
<string name="harmful_app_warning_uninstall">Uninstall</string>
<!-- Label for the launch anyway button on the harmful app warning dialog. -->
<string name="harmful_app_warning_launch_anyway">Launch anyway</string>
<!-- Title for the harmful app warning dialog. -->
<string name="harmful_app_warning_title">Uninstall harmful app?</string>
-
-
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0dad20b..1251c11 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1722,6 +1722,7 @@
<java-symbol type="string" name="global_action_lockdown" />
<java-symbol type="string" name="global_action_voice_assist" />
<java-symbol type="string" name="global_action_assist" />
+ <java-symbol type="string" name="global_action_screenshot" />
<java-symbol type="string" name="invalidPuk" />
<java-symbol type="string" name="lockscreen_carrier_default" />
<java-symbol type="style" name="Animation.LockScreen" />
@@ -2889,8 +2890,9 @@
<java-symbol type="bool" name="config_permissionReviewRequired" />
-
+ <!-- Global actions icons -->
<java-symbol type="drawable" name="ic_restart" />
+ <java-symbol type="drawable" name="ic_screenshot" />
<java-symbol type="drawable" name="emergency_icon" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a19917d..6ff239e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -173,22 +173,25 @@
[CHAR LIMIT=25] -->
<string name="compat_mode_off">Stretch to fill screen</string>
+ <!-- Power menu item for taking a screenshot [CHAR LIMIT=20]-->
+ <string name="global_action_screenshot">Screenshot</string>
+
<!-- Notification ticker displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=30] -->
<string name="screenshot_saving_ticker">Saving screenshot\u2026</string>
<!-- Notification title displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=50] -->
<string name="screenshot_saving_title">Saving screenshot\u2026</string>
<!-- Notification text displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=100] -->
- <string name="screenshot_saving_text">Screenshot is being saved.</string>
+ <string name="screenshot_saving_text">Screenshot is being saved</string>
<!-- Notification title displayed when a screenshot is saved to the Gallery. [CHAR LIMIT=50] -->
- <string name="screenshot_saved_title">Screenshot captured.</string>
+ <string name="screenshot_saved_title">Screenshot saved</string>
<!-- Notification text displayed when a screenshot is saved to the Gallery. [CHAR LIMIT=100] -->
- <string name="screenshot_saved_text">Tap to view your screenshot.</string>
+ <string name="screenshot_saved_text">Tap to view your screenshot</string>
<!-- Notification title displayed when we fail to take a screenshot. [CHAR LIMIT=50] -->
- <string name="screenshot_failed_title">Couldn\'t capture screenshot.</string>
+ <string name="screenshot_failed_title">Couldn\'t capture screenshot</string>
<!-- Notification text displayed when we fail to save a screenshot for unknown reasons. [CHAR LIMIT=100] -->
- <string name="screenshot_failed_to_save_unknown_text">Problem encountered while saving screenshot.</string>
+ <string name="screenshot_failed_to_save_unknown_text">Problem encountered while saving screenshot</string>
<!-- Notification text displayed when we fail to save a screenshot. [CHAR LIMIT=100] -->
- <string name="screenshot_failed_to_save_text">Can\'t save screenshot due to limited storage space.</string>
+ <string name="screenshot_failed_to_save_text">Can\'t save screenshot due to limited storage space</string>
<!-- Notification text displayed when we fail to take a screenshot. [CHAR LIMIT=100] -->
<string name="screenshot_failed_to_capture_text">Taking screenshots isn\'t allowed by the app or
your organization</string>
@@ -2038,4 +2041,5 @@
<string name="touch_filtered_warning">Because an app is obscuring a permission request, Settings
can’t verify your response.</string>
+
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index e008148..0f34513 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -24,10 +24,12 @@
import android.app.WallpaperManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.ServiceConnection;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.graphics.Point;
@@ -36,7 +38,9 @@
import android.net.ConnectivityManager;
import android.os.Build;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Message;
+import android.os.Messenger;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
@@ -77,6 +81,7 @@
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.util.EmergencyAffordanceManager;
+import com.android.internal.util.ScreenshotHelper;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.Dependency;
import com.android.systemui.HardwareUiLayout;
@@ -117,6 +122,7 @@
private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
private static final String GLOBAL_ACTION_KEY_RESTART = "restart";
private static final String GLOBAL_ACTION_KEY_LOGOUT = "logout";
+ private static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot";
private final Context mContext;
private final GlobalActionsManager mWindowManagerFuncs;
@@ -143,6 +149,7 @@
private boolean mHasLogoutButton;
private final boolean mShowSilentToggle;
private final EmergencyAffordanceManager mEmergencyAffordanceManager;
+ private final ScreenshotHelper mScreenshotHelper;
/**
* @param context everything needs a context :(
@@ -183,6 +190,7 @@
R.bool.config_useFixedVolume);
mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
+ mScreenshotHelper = new ScreenshotHelper(context);
}
/**
@@ -340,6 +348,8 @@
mItems.add(getAssistAction());
} else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
mItems.add(new RestartAction());
+ } else if (GLOBAL_ACTION_KEY_SCREENSHOT.equals(actionKey)) {
+ mItems.add(new ScreenshotAction());
} else if (GLOBAL_ACTION_KEY_LOGOUT.equals(actionKey)) {
if (mDevicePolicyManager.isLogoutEnabled()
&& getCurrentUser().id != UserHandle.USER_SYSTEM) {
@@ -458,6 +468,38 @@
}
+ private class ScreenshotAction extends SinglePressAction {
+ public ScreenshotAction() {
+ super(R.drawable.ic_screenshot, R.string.global_action_screenshot);
+ }
+
+ @Override
+ public void onPress() {
+ // Add a little delay before executing, to give the
+ // dialog a chance to go away before it takes a
+ // screenshot.
+ // TODO: instead, omit global action dialog layer
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ mScreenshotHelper.takeScreenshot(1, true, true, mHandler);
+ MetricsLogger.action(mContext,
+ MetricsEvent.ACTION_SCREENSHOT_POWER_MENU);
+ }
+ }, 500);
+ }
+
+ @Override
+ public boolean showDuringKeyguard() {
+ return true;
+ }
+
+ @Override
+ public boolean showBeforeProvisioning() {
+ return false;
+ }
+ }
+
private class BugReportAction extends SinglePressAction implements LongPressAction {
public BugReportAction() {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index f3bae20..34b8bfe 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -67,6 +67,8 @@
case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
mScreenshot.takeScreenshotPartial(finisher, msg.arg1 > 0, msg.arg2 > 0);
break;
+ default:
+ Log.d(TAG, "Invalid screenshot option: " + msg.what);
}
}
};
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 4f04d36..10a809a 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5129,6 +5129,12 @@
// OS: P
FUELGAUGE_SMART_BATTERY = 1281;
+ // ACTION: User tapped Screenshot in the power menu.
+ // CATEGORY: GLOBAL_SYSTEM_UI
+ // OS: P
+ ACTION_SCREENSHOT_POWER_MENU = 1282;
+
+
// ---- End P Constants, all P constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a1852fe..31dd673 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -269,6 +269,7 @@
import com.android.internal.policy.PhoneWindow;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.ScreenshotHelper;
import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.widget.PointerLocationView;
import com.android.server.GestureLauncherService;
@@ -460,6 +461,7 @@
AccessibilityManager mAccessibilityManager;
BurnInProtectionHelper mBurnInProtectionHelper;
AppOpsManager mAppOpsManager;
+ private ScreenshotHelper mScreenshotHelper;
private boolean mHasFeatureWatch;
private boolean mHasFeatureLeanback;
@@ -1713,7 +1715,9 @@
@Override
public void run() {
- takeScreenshot(mScreenshotType);
+ mScreenshotHelper.takeScreenshot(mScreenshotType,
+ mStatusBar != null && mStatusBar.isVisibleLw(),
+ mNavigationBar != null && mNavigationBar.isVisibleLw(), mHandler);
}
}
@@ -2191,6 +2195,7 @@
mWindowManagerFuncs.notifyKeyguardTrustedChanged();
}
});
+ mScreenshotHelper = new ScreenshotHelper(mContext);
}
/**
@@ -5766,100 +5771,6 @@
setHdmiPlugged(!mHdmiPlugged);
}
- final Object mScreenshotLock = new Object();
- ServiceConnection mScreenshotConnection = null;
-
- final Runnable mScreenshotTimeout = new Runnable() {
- @Override public void run() {
- synchronized (mScreenshotLock) {
- if (mScreenshotConnection != null) {
- mContext.unbindService(mScreenshotConnection);
- mScreenshotConnection = null;
- notifyScreenshotError();
- }
- }
- }
- };
-
- // Assume this is called from the Handler thread.
- private void takeScreenshot(final int screenshotType) {
- synchronized (mScreenshotLock) {
- if (mScreenshotConnection != null) {
- return;
- }
- final ComponentName serviceComponent = new ComponentName(SYSUI_PACKAGE,
- SYSUI_SCREENSHOT_SERVICE);
- final Intent serviceIntent = new Intent();
- serviceIntent.setComponent(serviceComponent);
- ServiceConnection conn = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- synchronized (mScreenshotLock) {
- if (mScreenshotConnection != this) {
- return;
- }
- Messenger messenger = new Messenger(service);
- Message msg = Message.obtain(null, screenshotType);
- final ServiceConnection myConn = this;
- Handler h = new Handler(mHandler.getLooper()) {
- @Override
- public void handleMessage(Message msg) {
- synchronized (mScreenshotLock) {
- if (mScreenshotConnection == myConn) {
- mContext.unbindService(mScreenshotConnection);
- mScreenshotConnection = null;
- mHandler.removeCallbacks(mScreenshotTimeout);
- }
- }
- }
- };
- msg.replyTo = new Messenger(h);
- msg.arg1 = msg.arg2 = 0;
- if (mStatusBar != null && mStatusBar.isVisibleLw())
- msg.arg1 = 1;
- if (mNavigationBar != null && mNavigationBar.isVisibleLw())
- msg.arg2 = 1;
- try {
- messenger.send(msg);
- } catch (RemoteException e) {
- }
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- synchronized (mScreenshotLock) {
- if (mScreenshotConnection != null) {
- mContext.unbindService(mScreenshotConnection);
- mScreenshotConnection = null;
- mHandler.removeCallbacks(mScreenshotTimeout);
- notifyScreenshotError();
- }
- }
- }
- };
- if (mContext.bindServiceAsUser(serviceIntent, conn,
- Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
- UserHandle.CURRENT)) {
- mScreenshotConnection = conn;
- mHandler.postDelayed(mScreenshotTimeout, 10000);
- }
- }
- }
-
- /**
- * Notifies the screenshot service to show an error.
- */
- private void notifyScreenshotError() {
- // If the service process is killed, then ask it to clean up after itself
- final ComponentName errorComponent = new ComponentName(SYSUI_PACKAGE,
- SYSUI_SCREENSHOT_ERROR_RECEIVER);
- Intent errorIntent = new Intent(Intent.ACTION_USER_PRESENT);
- errorIntent.setComponent(errorComponent);
- errorIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
- Intent.FLAG_RECEIVER_FOREGROUND);
- mContext.sendBroadcastAsUser(errorIntent, UserHandle.CURRENT);
- }
/** {@inheritDoc} */
@Override