blob: 7fd94c6859fb27d8153f9b1b0548085a22c43eff [file] [log] [blame]
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -05001package com.android.internal.util;
2
Phil Weaverd0429742018-01-16 15:32:30 -08003import android.annotation.NonNull;
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -05004import android.content.ComponentName;
5import android.content.Context;
6import android.content.Intent;
7import android.content.ServiceConnection;
8import android.os.Handler;
9import android.os.IBinder;
10import android.os.Message;
11import android.os.Messenger;
12import android.os.RemoteException;
13import android.os.UserHandle;
14import android.util.Log;
15
16public class ScreenshotHelper {
17 private static final String TAG = "ScreenshotHelper";
18
19 private static final String SYSUI_PACKAGE = "com.android.systemui";
20 private static final String SYSUI_SCREENSHOT_SERVICE =
21 "com.android.systemui.screenshot.TakeScreenshotService";
22 private static final String SYSUI_SCREENSHOT_ERROR_RECEIVER =
23 "com.android.systemui.screenshot.ScreenshotServiceErrorReceiver";
24
25 // Time until we give up on the screenshot & show an error instead.
26 private final int SCREENSHOT_TIMEOUT_MS = 10000;
27
28 private final Object mScreenshotLock = new Object();
29 private ServiceConnection mScreenshotConnection = null;
30 private final Context mContext;
31
32 public ScreenshotHelper(Context context) {
33 mContext = context;
34 }
35
Phil Weaverd0429742018-01-16 15:32:30 -080036 /**
37 * Request a screenshot be taken.
38 *
39 * @param screenshotType The type of screenshot, for example either
40 * {@link android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN}
41 * or {@link android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION}
42 * @param hasStatus {@code true} if the status bar is currently showing. {@code false} if not.
43 * @param hasNav {@code true} if the navigation bar is currently showing. {@code false} if not.
44 * @param handler A handler used in case the screenshot times out
45 */
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -050046 public void takeScreenshot(final int screenshotType, final boolean hasStatus,
Phil Weaverd0429742018-01-16 15:32:30 -080047 final boolean hasNav, @NonNull Handler handler) {
Alison Cichowlasa2cd19e2017-12-06 10:51:21 -050048 synchronized (mScreenshotLock) {
49 if (mScreenshotConnection != null) {
50 return;
51 }
52 final ComponentName serviceComponent = new ComponentName(SYSUI_PACKAGE,
53 SYSUI_SCREENSHOT_SERVICE);
54 final Intent serviceIntent = new Intent();
55
56 final Runnable mScreenshotTimeout = new Runnable() {
57 @Override public void run() {
58 synchronized (mScreenshotLock) {
59 if (mScreenshotConnection != null) {
60 mContext.unbindService(mScreenshotConnection);
61 mScreenshotConnection = null;
62 notifyScreenshotError();
63 }
64 }
65 }
66 };
67
68 serviceIntent.setComponent(serviceComponent);
69 ServiceConnection conn = new ServiceConnection() {
70 @Override
71 public void onServiceConnected(ComponentName name, IBinder service) {
72 synchronized (mScreenshotLock) {
73 if (mScreenshotConnection != this) {
74 return;
75 }
76 Messenger messenger = new Messenger(service);
77 Message msg = Message.obtain(null, screenshotType);
78 final ServiceConnection myConn = this;
79 Handler h = new Handler(handler.getLooper()) {
80 @Override
81 public void handleMessage(Message msg) {
82 synchronized (mScreenshotLock) {
83 if (mScreenshotConnection == myConn) {
84 mContext.unbindService(mScreenshotConnection);
85 mScreenshotConnection = null;
86 handler.removeCallbacks(mScreenshotTimeout);
87 }
88 }
89 }
90 };
91 msg.replyTo = new Messenger(h);
92 msg.arg1 = hasStatus ? 1: 0;
93 msg.arg2 = hasNav ? 1: 0;
94 try {
95 messenger.send(msg);
96 } catch (RemoteException e) {
97 Log.e(TAG, "Couldn't take screenshot: " + e);
98 }
99 }
100 }
101
102 @Override
103 public void onServiceDisconnected(ComponentName name) {
104 synchronized (mScreenshotLock) {
105 if (mScreenshotConnection != null) {
106 mContext.unbindService(mScreenshotConnection);
107 mScreenshotConnection = null;
108 handler.removeCallbacks(mScreenshotTimeout);
109 notifyScreenshotError();
110 }
111 }
112 }
113 };
114 if (mContext.bindServiceAsUser(serviceIntent, conn,
115 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
116 UserHandle.CURRENT)) {
117 mScreenshotConnection = conn;
118 handler.postDelayed(mScreenshotTimeout, SCREENSHOT_TIMEOUT_MS);
119 }
120 }
121 }
122
123 /**
124 * Notifies the screenshot service to show an error.
125 */
126 private void notifyScreenshotError() {
127 // If the service process is killed, then ask it to clean up after itself
128 final ComponentName errorComponent = new ComponentName(SYSUI_PACKAGE,
129 SYSUI_SCREENSHOT_ERROR_RECEIVER);
130 // Broadcast needs to have a valid action. We'll just pick
131 // a generic one, since the receiver here doesn't care.
132 Intent errorIntent = new Intent(Intent.ACTION_USER_PRESENT);
133 errorIntent.setComponent(errorComponent);
134 errorIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
135 Intent.FLAG_RECEIVER_FOREGROUND);
136 mContext.sendBroadcastAsUser(errorIntent, UserHandle.CURRENT);
137 }
138
139}