package com.android.internal.util; import android.annotation.NonNull; 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; } /** * Request a screenshot be taken. * * @param screenshotType The type of screenshot, for example either * {@link android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN} * or {@link android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION} * @param hasStatus {@code true} if the status bar is currently showing. {@code false} if not. * @param hasNav {@code true} if the navigation bar is currently showing. {@code false} if not. * @param handler A handler used in case the screenshot times out */ public void takeScreenshot(final int screenshotType, final boolean hasStatus, final boolean hasNav, @NonNull 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); } }