blob: b253635e9bfa728c38be1ebc18ae040bbcb4fd4c [file] [log] [blame]
Beth Thibodeau77c25452020-01-09 14:33:47 -05001/*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.screenrecord;
18
19import android.app.PendingIntent;
20import android.content.ComponentName;
21import android.content.Context;
22import android.content.Intent;
23import android.os.CountDownTimer;
24import android.util.Log;
25
Beth Thibodeau8bfbf1f2020-02-13 13:55:37 -050026import com.android.systemui.statusbar.policy.CallbackController;
27
28import java.util.ArrayList;
Beth Thibodeau77c25452020-01-09 14:33:47 -050029
30import javax.inject.Inject;
31import javax.inject.Singleton;
32
33/**
34 * Helper class to initiate a screen recording
35 */
36@Singleton
Beth Thibodeau8bfbf1f2020-02-13 13:55:37 -050037public class RecordingController
38 implements CallbackController<RecordingController.RecordingStateChangeCallback> {
Beth Thibodeau77c25452020-01-09 14:33:47 -050039 private static final String TAG = "RecordingController";
40 private static final String SYSUI_PACKAGE = "com.android.systemui";
41 private static final String SYSUI_SCREENRECORD_LAUNCHER =
42 "com.android.systemui.screenrecord.ScreenRecordDialog";
43
44 private final Context mContext;
45 private boolean mIsStarting;
46 private boolean mIsRecording;
Beth Thibodeau77c25452020-01-09 14:33:47 -050047 private PendingIntent mStopIntent;
48 private CountDownTimer mCountDownTimer = null;
49
Beth Thibodeau8bfbf1f2020-02-13 13:55:37 -050050 private ArrayList<RecordingStateChangeCallback> mListeners = new ArrayList<>();
51
Beth Thibodeau77c25452020-01-09 14:33:47 -050052 /**
53 * Create a new RecordingController
54 * @param context Context for the controller
55 */
56 @Inject
57 public RecordingController(Context context) {
58 mContext = context;
59 }
60
61 /**
Beth Thibodeauf54cace2020-04-23 12:46:55 -040062 * Get an intent to show screen recording options to the user.
Beth Thibodeau77c25452020-01-09 14:33:47 -050063 */
Beth Thibodeauf54cace2020-04-23 12:46:55 -040064 public Intent getPromptIntent() {
Beth Thibodeau77c25452020-01-09 14:33:47 -050065 final ComponentName launcherComponent = new ComponentName(SYSUI_PACKAGE,
66 SYSUI_SCREENRECORD_LAUNCHER);
67 final Intent intent = new Intent();
68 intent.setComponent(launcherComponent);
69 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Beth Thibodeauf54cace2020-04-23 12:46:55 -040070 return intent;
Beth Thibodeau77c25452020-01-09 14:33:47 -050071 }
72
73 /**
74 * Start counting down in preparation to start a recording
Beth Thibodeau58772782020-02-18 20:55:38 -050075 * @param ms Total time in ms to wait before starting
76 * @param interval Time in ms per countdown step
Beth Thibodeau77c25452020-01-09 14:33:47 -050077 * @param startIntent Intent to start a recording
78 * @param stopIntent Intent to stop a recording
79 */
Beth Thibodeau58772782020-02-18 20:55:38 -050080 public void startCountdown(long ms, long interval, PendingIntent startIntent,
81 PendingIntent stopIntent) {
Beth Thibodeau77c25452020-01-09 14:33:47 -050082 mIsStarting = true;
83 mStopIntent = stopIntent;
84
Beth Thibodeau58772782020-02-18 20:55:38 -050085 mCountDownTimer = new CountDownTimer(ms, interval) {
Beth Thibodeau77c25452020-01-09 14:33:47 -050086 @Override
87 public void onTick(long millisUntilFinished) {
Beth Thibodeau8bfbf1f2020-02-13 13:55:37 -050088 for (RecordingStateChangeCallback cb : mListeners) {
89 cb.onCountdown(millisUntilFinished);
90 }
Beth Thibodeau77c25452020-01-09 14:33:47 -050091 }
92
93 @Override
94 public void onFinish() {
95 mIsStarting = false;
96 mIsRecording = true;
Beth Thibodeau8bfbf1f2020-02-13 13:55:37 -050097 for (RecordingStateChangeCallback cb : mListeners) {
Beth Thibodeau58772782020-02-18 20:55:38 -050098 cb.onCountdownEnd();
Beth Thibodeau8bfbf1f2020-02-13 13:55:37 -050099 }
Beth Thibodeau77c25452020-01-09 14:33:47 -0500100 try {
101 startIntent.send();
Beth Thibodeau8bfbf1f2020-02-13 13:55:37 -0500102 Log.d(TAG, "sent start intent");
Beth Thibodeau77c25452020-01-09 14:33:47 -0500103 } catch (PendingIntent.CanceledException e) {
104 Log.e(TAG, "Pending intent was cancelled: " + e.getMessage());
105 }
106 }
107 };
108
109 mCountDownTimer.start();
110 }
111
Beth Thibodeau77c25452020-01-09 14:33:47 -0500112 /**
113 * Cancel a countdown in progress. This will not stop the recording if it already started.
114 */
115 public void cancelCountdown() {
116 if (mCountDownTimer != null) {
117 mCountDownTimer.cancel();
118 } else {
119 Log.e(TAG, "Timer was null");
120 }
121 mIsStarting = false;
Beth Thibodeau8bfbf1f2020-02-13 13:55:37 -0500122
123 for (RecordingStateChangeCallback cb : mListeners) {
Beth Thibodeau58772782020-02-18 20:55:38 -0500124 cb.onCountdownEnd();
Beth Thibodeau8bfbf1f2020-02-13 13:55:37 -0500125 }
Beth Thibodeau77c25452020-01-09 14:33:47 -0500126 }
127
128 /**
129 * Check if the recording is currently counting down to begin
130 * @return
131 */
132 public boolean isStarting() {
133 return mIsStarting;
134 }
135
136 /**
137 * Check if the recording is ongoing
138 * @return
139 */
Jay Aliomer0bd491a2020-03-16 14:34:10 -0400140 public synchronized boolean isRecording() {
Beth Thibodeau77c25452020-01-09 14:33:47 -0500141 return mIsRecording;
142 }
143
144 /**
145 * Stop the recording
146 */
147 public void stopRecording() {
Beth Thibodeau77c25452020-01-09 14:33:47 -0500148 try {
149 mStopIntent.send();
Beth Thibodeau58772782020-02-18 20:55:38 -0500150 updateState(false);
Beth Thibodeau77c25452020-01-09 14:33:47 -0500151 } catch (PendingIntent.CanceledException e) {
152 Log.e(TAG, "Error stopping: " + e.getMessage());
153 }
Beth Thibodeau77c25452020-01-09 14:33:47 -0500154 }
155
156 /**
157 * Update the current status
158 * @param isRecording
159 */
Jay Aliomer0bd491a2020-03-16 14:34:10 -0400160 public synchronized void updateState(boolean isRecording) {
Beth Thibodeau77c25452020-01-09 14:33:47 -0500161 mIsRecording = isRecording;
Beth Thibodeau8bfbf1f2020-02-13 13:55:37 -0500162 for (RecordingStateChangeCallback cb : mListeners) {
163 if (isRecording) {
164 cb.onRecordingStart();
165 } else {
166 cb.onRecordingEnd();
167 }
168 }
169 }
170
171 @Override
172 public void addCallback(RecordingStateChangeCallback listener) {
173 mListeners.add(listener);
174 }
175
176 @Override
177 public void removeCallback(RecordingStateChangeCallback listener) {
178 mListeners.remove(listener);
179 }
180
181 /**
182 * A callback for changes in the screen recording state
183 */
184 public interface RecordingStateChangeCallback {
185 /**
186 * Called when a countdown to recording has updated
187 *
188 * @param millisUntilFinished Time in ms remaining in the countdown
189 */
190 default void onCountdown(long millisUntilFinished) {}
191
192 /**
Beth Thibodeau58772782020-02-18 20:55:38 -0500193 * Called when a countdown to recording has ended. This is a separate method so that if
194 * needed, listeners can handle cases where recording fails to start
195 */
196 default void onCountdownEnd() {}
197
198 /**
Beth Thibodeau8bfbf1f2020-02-13 13:55:37 -0500199 * Called when a screen recording has started
200 */
201 default void onRecordingStart() {}
202
203 /**
204 * Called when a screen recording has ended
205 */
206 default void onRecordingEnd() {}
Beth Thibodeau77c25452020-01-09 14:33:47 -0500207 }
208}