Merge "Screenshot global actions item."

This commit is contained in:
TreeHugger Robot
2018-01-13 00:34:42 +00:00
committed by Android (Google) Code Review
10 changed files with 234 additions and 105 deletions

View File

@@ -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);
}
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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
cant verify your response.</string>
</resources>

View File

@@ -24,10 +24,12 @@ import android.app.KeyguardManager;
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.media.AudioManager;
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.logging.nano.MetricsProto.MetricsEvent;
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 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener,
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 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener,
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 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener,
R.bool.config_useFixedVolume);
mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
mScreenshotHelper = new ScreenshotHelper(context);
}
/**
@@ -340,6 +348,8 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener,
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 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
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() {

View File

@@ -67,6 +67,8 @@ public class TakeScreenshotService extends Service {
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);
}
}
};

View File

@@ -5129,6 +5129,12 @@ message MetricsEvent {
// 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

View File

@@ -269,6 +269,7 @@ import com.android.internal.policy.IShortcutService;
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 @@ public class PhoneWindowManager implements WindowManagerPolicy {
AccessibilityManager mAccessibilityManager;
BurnInProtectionHelper mBurnInProtectionHelper;
AppOpsManager mAppOpsManager;
private ScreenshotHelper mScreenshotHelper;
private boolean mHasFeatureWatch;
private boolean mHasFeatureLeanback;
@@ -1713,7 +1715,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public void run() {
takeScreenshot(mScreenshotType);
mScreenshotHelper.takeScreenshot(mScreenshotType,
mStatusBar != null && mStatusBar.isVisibleLw(),
mNavigationBar != null && mNavigationBar.isVisibleLw(), mHandler);
}
}
@@ -2191,6 +2195,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mWindowManagerFuncs.notifyKeyguardTrustedChanged();
}
});
mScreenshotHelper = new ScreenshotHelper(mContext);
}
/**
@@ -5766,100 +5771,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
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