Loading packages/SystemUI/AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -329,6 +329,7 @@ <activity android:name=".screenrecord.ScreenRecordDialog" android:theme="@style/ScreenRecord" android:showForAllUsers="true" android:excludeFromRecents="true" /> <service android:name=".screenrecord.RecordingService" /> Loading packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java +12 −10 Original line number Diff line number Diff line Loading @@ -22,13 +22,13 @@ import android.text.TextUtils; import android.util.Log; import android.widget.Switch; import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.screenrecord.RecordingController; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import javax.inject.Inject; Loading @@ -39,19 +39,17 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> implements RecordingController.RecordingStateChangeCallback { private static final String TAG = "ScreenRecordTile"; private RecordingController mController; private ActivityStarter mActivityStarter; private KeyguardDismissUtil mKeyguardDismissUtil; private long mMillisUntilFinished = 0; private Callback mCallback = new Callback(); private UiEventLogger mUiEventLogger; @Inject public ScreenRecordTile(QSHost host, RecordingController controller, ActivityStarter activityStarter, UiEventLogger uiEventLogger) { KeyguardDismissUtil keyguardDismissUtil) { super(host); mController = controller; mController.observe(this, mCallback); mActivityStarter = activityStarter; mUiEventLogger = uiEventLogger; mKeyguardDismissUtil = keyguardDismissUtil; } @Override Loading @@ -69,7 +67,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> } else if (mController.isRecording()) { stopRecording(); } else { startCountdown(); mUiHandler.post(() -> showPrompt()); } refreshState(); } Loading Loading @@ -114,11 +112,15 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> return mContext.getString(R.string.quick_settings_screen_record_label); } private void startCountdown() { // Close QS, otherwise the permission dialog appears beneath it private void showPrompt() { // Close QS, otherwise the dialog appears beneath it getHost().collapsePanels(); Intent intent = mController.getPromptIntent(); mActivityStarter.postStartActivityDismissingKeyguard(intent, 0); ActivityStarter.OnDismissAction dismissAction = () -> { mContext.startActivity(intent); return false; }; mKeyguardDismissUtil.executeWhenUnlocked(dismissAction, false); } private void cancelCountdown() { Loading packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java +27 −5 Original line number Diff line number Diff line Loading @@ -17,12 +17,17 @@ package com.android.systemui.screenrecord; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.CountDownTimer; import android.os.UserHandle; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.statusbar.policy.CallbackController; import java.util.ArrayList; Loading @@ -41,21 +46,30 @@ public class RecordingController private static final String SYSUI_SCREENRECORD_LAUNCHER = "com.android.systemui.screenrecord.ScreenRecordDialog"; private final Context mContext; private boolean mIsStarting; private boolean mIsRecording; private PendingIntent mStopIntent; private CountDownTimer mCountDownTimer = null; private BroadcastDispatcher mBroadcastDispatcher; private ArrayList<RecordingStateChangeCallback> mListeners = new ArrayList<>(); @VisibleForTesting protected final BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (mStopIntent != null) { stopRecording(); } } }; /** * Create a new RecordingController * @param context Context for the controller */ @Inject public RecordingController(Context context) { mContext = context; public RecordingController(BroadcastDispatcher broadcastDispatcher) { mBroadcastDispatcher = broadcastDispatcher; } /** Loading Loading @@ -99,6 +113,9 @@ public class RecordingController } try { startIntent.send(); IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_SWITCHED); mBroadcastDispatcher.registerReceiver(mUserChangeReceiver, userFilter, null, UserHandle.ALL); Log.d(TAG, "sent start intent"); } catch (PendingIntent.CanceledException e) { Log.e(TAG, "Pending intent was cancelled: " + e.getMessage()); Loading Loading @@ -146,11 +163,16 @@ public class RecordingController */ public void stopRecording() { try { if (mStopIntent != null) { mStopIntent.send(); } else { Log.e(TAG, "Stop intent was null"); } updateState(false); } catch (PendingIntent.CanceledException e) { Log.e(TAG, "Error stopping: " + e.getMessage()); } mBroadcastDispatcher.unregisterReceiver(mUserChangeReceiver); } /** Loading packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +44 −23 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import android.util.Log; import android.widget.Toast; Loading @@ -40,6 +41,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.LongRunning; import com.android.systemui.settings.CurrentUserContextTracker; import java.io.IOException; import java.util.concurrent.Executor; Loading @@ -58,7 +60,6 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis private static final String TAG = "RecordingService"; private static final String CHANNEL_ID = "screen_record"; private static final String EXTRA_RESULT_CODE = "extra_resultCode"; private static final String EXTRA_DATA = "extra_data"; private static final String EXTRA_PATH = "extra_path"; private static final String EXTRA_AUDIO_SOURCE = "extra_useAudio"; private static final String EXTRA_SHOW_TAPS = "extra_showTaps"; Loading @@ -79,14 +80,17 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis private final Executor mLongExecutor; private final UiEventLogger mUiEventLogger; private final NotificationManager mNotificationManager; private final CurrentUserContextTracker mUserContextTracker; @Inject public RecordingService(RecordingController controller, @LongRunning Executor executor, UiEventLogger uiEventLogger, NotificationManager notificationManager) { UiEventLogger uiEventLogger, NotificationManager notificationManager, CurrentUserContextTracker userContextTracker) { mController = controller; mLongExecutor = executor; mUiEventLogger = uiEventLogger; mNotificationManager = notificationManager; mUserContextTracker = userContextTracker; } /** Loading @@ -95,8 +99,6 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis * @param context Context from the requesting activity * @param resultCode The result code from {@link android.app.Activity#onActivityResult(int, int, * android.content.Intent)} * @param data The data from {@link android.app.Activity#onActivityResult(int, int, * android.content.Intent)} * @param audioSource The ordinal value of the audio source * {@link com.android.systemui.screenrecord.ScreenRecordingAudioSource} * @param showTaps True to make touches visible while recording Loading @@ -118,6 +120,8 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis String action = intent.getAction(); Log.d(TAG, "onStartCommand " + action); int mCurrentUserId = mUserContextTracker.getCurrentUserContext().getUserId(); UserHandle currentUser = new UserHandle(mCurrentUserId); switch (action) { case ACTION_START: mAudioSource = ScreenRecordingAudioSource Loading @@ -132,8 +136,8 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis setTapsVisible(mShowTaps); mRecorder = new ScreenMediaRecorder( getApplicationContext(), getUserId(), mUserContextTracker.getCurrentUserContext(), mCurrentUserId, mAudioSource, this ); Loading @@ -148,7 +152,14 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis } else { mUiEventLogger.log(Events.ScreenRecordEvent.SCREEN_RECORD_END_QS_TILE); } stopRecording(); // Check user ID - we may be getting a stop intent after user switch, in which case // we want to post the notifications for that user, which is NOT current user int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); if (userId == -1) { userId = mUserContextTracker.getCurrentUserContext().getUserId(); } Log.d(TAG, "notifying for user " + userId); stopRecording(userId); mNotificationManager.cancel(NOTIFICATION_RECORDING_ID); stopSelf(); break; Loading @@ -165,7 +176,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); // Remove notification mNotificationManager.cancel(NOTIFICATION_VIEW_ID); mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser); startActivity(Intent.createChooser(shareIntent, shareLabel) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); Loading @@ -184,7 +195,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis Toast.LENGTH_LONG).show(); // Remove notification mNotificationManager.cancel(NOTIFICATION_VIEW_ID); mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser); Log.d(TAG, "Deleted recording " + uri); break; } Loading Loading @@ -215,11 +226,12 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis mController.updateState(true); createRecordingNotification(); mUiEventLogger.log(Events.ScreenRecordEvent.SCREEN_RECORD_START); } catch (IOException | RemoteException e) { } catch (IOException | RemoteException | IllegalStateException e) { Toast.makeText(this, R.string.screenrecord_start_error, Toast.LENGTH_LONG) .show(); e.printStackTrace(); mController.updateState(false); } } Loading @@ -242,7 +254,6 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis ? res.getString(R.string.screenrecord_ongoing_screen_only) : res.getString(R.string.screenrecord_ongoing_screen_and_audio); Intent stopIntent = getNotificationIntent(this); Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_screenrecord) Loading @@ -254,7 +265,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis .setOngoing(true) .setContentIntent( PendingIntent.getService(this, REQUEST_CODE, stopIntent, PendingIntent.FLAG_UPDATE_CURRENT)) PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)) .addExtras(extras); startForeground(NOTIFICATION_RECORDING_ID, builder.build()); } Loading @@ -265,11 +276,17 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis String notificationTitle = mAudioSource == ScreenRecordingAudioSource.NONE ? res.getString(R.string.screenrecord_ongoing_screen_only) : res.getString(R.string.screenrecord_ongoing_screen_and_audio); Bundle extras = new Bundle(); extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, res.getString(R.string.screenrecord_name)); Notification.Builder builder = new Notification.Builder(getApplicationContext(), CHANNEL_ID) .setContentTitle(notificationTitle) .setContentText( getResources().getString(R.string.screenrecord_background_processing_label)) .setSmallIcon(R.drawable.ic_screenrecord); .setSmallIcon(R.drawable.ic_screenrecord) .addExtras(extras); return builder.build(); } Loading @@ -287,7 +304,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis this, REQUEST_CODE, getShareIntent(this, uri.toString()), PendingIntent.FLAG_UPDATE_CURRENT)) PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)) .build(); Notification.Action deleteAction = new Notification.Action.Builder( Loading @@ -297,7 +314,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis this, REQUEST_CODE, getDeleteIntent(this, uri.toString()), PendingIntent.FLAG_UPDATE_CURRENT)) PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)) .build(); Bundle extras = new Bundle(); Loading Loading @@ -328,34 +345,36 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis return builder.build(); } private void stopRecording() { private void stopRecording(int userId) { setTapsVisible(mOriginalShowTaps); if (getRecorder() != null) { getRecorder().end(); saveRecording(); saveRecording(userId); } else { Log.e(TAG, "stopRecording called, but recorder was null"); } mController.updateState(false); } private void saveRecording() { mNotificationManager.notify(NOTIFICATION_PROCESSING_ID, createProcessingNotification()); private void saveRecording(int userId) { UserHandle currentUser = new UserHandle(userId); mNotificationManager.notifyAsUser(null, NOTIFICATION_PROCESSING_ID, createProcessingNotification(), currentUser); mLongExecutor.execute(() -> { try { Log.d(TAG, "saving recording"); Notification notification = createSaveNotification(getRecorder().save()); if (!mController.isRecording()) { Log.d(TAG, "showing saved notification"); mNotificationManager.notify(NOTIFICATION_VIEW_ID, notification); mNotificationManager.notifyAsUser(null, NOTIFICATION_VIEW_ID, notification, currentUser); } } catch (IOException e) { Log.e(TAG, "Error saving screen recording: " + e.getMessage()); Toast.makeText(this, R.string.screenrecord_delete_error, Toast.LENGTH_LONG) .show(); } finally { mNotificationManager.cancel(NOTIFICATION_PROCESSING_ID); mNotificationManager.cancelAsUser(null, NOTIFICATION_PROCESSING_ID, currentUser); } }); } Loading @@ -371,7 +390,9 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis * @return */ public static Intent getStopIntent(Context context) { return new Intent(context, RecordingService.class).setAction(ACTION_STOP); return new Intent(context, RecordingService.class) .setAction(ACTION_STOP) .putExtra(Intent.EXTRA_USER_HANDLE, context.getUserId()); } /** Loading packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java +5 −10 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.systemui.screenrecord; import android.content.Context; import android.media.AudioAttributes; import android.media.AudioFormat; import android.media.AudioPlaybackCaptureConfiguration; Loading @@ -39,7 +38,6 @@ public class ScreenInternalAudioRecorder { private static String TAG = "ScreenAudioRecorder"; private static final int TIMEOUT = 500; private static final float MIC_VOLUME_SCALE = 1.4f; private final Context mContext; private AudioRecord mAudioRecord; private AudioRecord mAudioRecordMic; private Config mConfig = new Config(); Loading @@ -49,17 +47,14 @@ public class ScreenInternalAudioRecorder { private long mPresentationTime; private long mTotalBytes; private MediaMuxer mMuxer; private String mOutFile; private boolean mMic; private int mTrackId = -1; public ScreenInternalAudioRecorder(String outFile, Context context, MediaProjection mp, boolean includeMicInput) throws IOException { public ScreenInternalAudioRecorder(String outFile, MediaProjection mp, boolean includeMicInput) throws IOException { mMic = includeMicInput; mOutFile = outFile; mMuxer = new MediaMuxer(outFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); mContext = context; mMediaProjection = mp; Log.d(TAG, "creating audio file " + outFile); setupSimple(); Loading Loading @@ -266,8 +261,9 @@ public class ScreenInternalAudioRecorder { /** * start recording * @throws IllegalStateException if recording fails to initialize */ public void start() { public void start() throws IllegalStateException { if (mThread != null) { Log.e(TAG, "a recording is being done in parallel or stop is not called"); } Loading @@ -276,8 +272,7 @@ public class ScreenInternalAudioRecorder { Log.d(TAG, "channel count " + mAudioRecord.getChannelCount()); mCodec.start(); if (mAudioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) { Log.e(TAG, "Error starting audio recording"); return; throw new IllegalStateException("Audio recording failed to start"); } mThread.start(); } Loading Loading
packages/SystemUI/AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -329,6 +329,7 @@ <activity android:name=".screenrecord.ScreenRecordDialog" android:theme="@style/ScreenRecord" android:showForAllUsers="true" android:excludeFromRecents="true" /> <service android:name=".screenrecord.RecordingService" /> Loading
packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java +12 −10 Original line number Diff line number Diff line Loading @@ -22,13 +22,13 @@ import android.text.TextUtils; import android.util.Log; import android.widget.Switch; import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.screenrecord.RecordingController; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import javax.inject.Inject; Loading @@ -39,19 +39,17 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> implements RecordingController.RecordingStateChangeCallback { private static final String TAG = "ScreenRecordTile"; private RecordingController mController; private ActivityStarter mActivityStarter; private KeyguardDismissUtil mKeyguardDismissUtil; private long mMillisUntilFinished = 0; private Callback mCallback = new Callback(); private UiEventLogger mUiEventLogger; @Inject public ScreenRecordTile(QSHost host, RecordingController controller, ActivityStarter activityStarter, UiEventLogger uiEventLogger) { KeyguardDismissUtil keyguardDismissUtil) { super(host); mController = controller; mController.observe(this, mCallback); mActivityStarter = activityStarter; mUiEventLogger = uiEventLogger; mKeyguardDismissUtil = keyguardDismissUtil; } @Override Loading @@ -69,7 +67,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> } else if (mController.isRecording()) { stopRecording(); } else { startCountdown(); mUiHandler.post(() -> showPrompt()); } refreshState(); } Loading Loading @@ -114,11 +112,15 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> return mContext.getString(R.string.quick_settings_screen_record_label); } private void startCountdown() { // Close QS, otherwise the permission dialog appears beneath it private void showPrompt() { // Close QS, otherwise the dialog appears beneath it getHost().collapsePanels(); Intent intent = mController.getPromptIntent(); mActivityStarter.postStartActivityDismissingKeyguard(intent, 0); ActivityStarter.OnDismissAction dismissAction = () -> { mContext.startActivity(intent); return false; }; mKeyguardDismissUtil.executeWhenUnlocked(dismissAction, false); } private void cancelCountdown() { Loading
packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java +27 −5 Original line number Diff line number Diff line Loading @@ -17,12 +17,17 @@ package com.android.systemui.screenrecord; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.CountDownTimer; import android.os.UserHandle; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.statusbar.policy.CallbackController; import java.util.ArrayList; Loading @@ -41,21 +46,30 @@ public class RecordingController private static final String SYSUI_SCREENRECORD_LAUNCHER = "com.android.systemui.screenrecord.ScreenRecordDialog"; private final Context mContext; private boolean mIsStarting; private boolean mIsRecording; private PendingIntent mStopIntent; private CountDownTimer mCountDownTimer = null; private BroadcastDispatcher mBroadcastDispatcher; private ArrayList<RecordingStateChangeCallback> mListeners = new ArrayList<>(); @VisibleForTesting protected final BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (mStopIntent != null) { stopRecording(); } } }; /** * Create a new RecordingController * @param context Context for the controller */ @Inject public RecordingController(Context context) { mContext = context; public RecordingController(BroadcastDispatcher broadcastDispatcher) { mBroadcastDispatcher = broadcastDispatcher; } /** Loading Loading @@ -99,6 +113,9 @@ public class RecordingController } try { startIntent.send(); IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_SWITCHED); mBroadcastDispatcher.registerReceiver(mUserChangeReceiver, userFilter, null, UserHandle.ALL); Log.d(TAG, "sent start intent"); } catch (PendingIntent.CanceledException e) { Log.e(TAG, "Pending intent was cancelled: " + e.getMessage()); Loading Loading @@ -146,11 +163,16 @@ public class RecordingController */ public void stopRecording() { try { if (mStopIntent != null) { mStopIntent.send(); } else { Log.e(TAG, "Stop intent was null"); } updateState(false); } catch (PendingIntent.CanceledException e) { Log.e(TAG, "Error stopping: " + e.getMessage()); } mBroadcastDispatcher.unregisterReceiver(mUserChangeReceiver); } /** Loading
packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +44 −23 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import android.util.Log; import android.widget.Toast; Loading @@ -40,6 +41,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.LongRunning; import com.android.systemui.settings.CurrentUserContextTracker; import java.io.IOException; import java.util.concurrent.Executor; Loading @@ -58,7 +60,6 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis private static final String TAG = "RecordingService"; private static final String CHANNEL_ID = "screen_record"; private static final String EXTRA_RESULT_CODE = "extra_resultCode"; private static final String EXTRA_DATA = "extra_data"; private static final String EXTRA_PATH = "extra_path"; private static final String EXTRA_AUDIO_SOURCE = "extra_useAudio"; private static final String EXTRA_SHOW_TAPS = "extra_showTaps"; Loading @@ -79,14 +80,17 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis private final Executor mLongExecutor; private final UiEventLogger mUiEventLogger; private final NotificationManager mNotificationManager; private final CurrentUserContextTracker mUserContextTracker; @Inject public RecordingService(RecordingController controller, @LongRunning Executor executor, UiEventLogger uiEventLogger, NotificationManager notificationManager) { UiEventLogger uiEventLogger, NotificationManager notificationManager, CurrentUserContextTracker userContextTracker) { mController = controller; mLongExecutor = executor; mUiEventLogger = uiEventLogger; mNotificationManager = notificationManager; mUserContextTracker = userContextTracker; } /** Loading @@ -95,8 +99,6 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis * @param context Context from the requesting activity * @param resultCode The result code from {@link android.app.Activity#onActivityResult(int, int, * android.content.Intent)} * @param data The data from {@link android.app.Activity#onActivityResult(int, int, * android.content.Intent)} * @param audioSource The ordinal value of the audio source * {@link com.android.systemui.screenrecord.ScreenRecordingAudioSource} * @param showTaps True to make touches visible while recording Loading @@ -118,6 +120,8 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis String action = intent.getAction(); Log.d(TAG, "onStartCommand " + action); int mCurrentUserId = mUserContextTracker.getCurrentUserContext().getUserId(); UserHandle currentUser = new UserHandle(mCurrentUserId); switch (action) { case ACTION_START: mAudioSource = ScreenRecordingAudioSource Loading @@ -132,8 +136,8 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis setTapsVisible(mShowTaps); mRecorder = new ScreenMediaRecorder( getApplicationContext(), getUserId(), mUserContextTracker.getCurrentUserContext(), mCurrentUserId, mAudioSource, this ); Loading @@ -148,7 +152,14 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis } else { mUiEventLogger.log(Events.ScreenRecordEvent.SCREEN_RECORD_END_QS_TILE); } stopRecording(); // Check user ID - we may be getting a stop intent after user switch, in which case // we want to post the notifications for that user, which is NOT current user int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); if (userId == -1) { userId = mUserContextTracker.getCurrentUserContext().getUserId(); } Log.d(TAG, "notifying for user " + userId); stopRecording(userId); mNotificationManager.cancel(NOTIFICATION_RECORDING_ID); stopSelf(); break; Loading @@ -165,7 +176,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); // Remove notification mNotificationManager.cancel(NOTIFICATION_VIEW_ID); mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser); startActivity(Intent.createChooser(shareIntent, shareLabel) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); Loading @@ -184,7 +195,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis Toast.LENGTH_LONG).show(); // Remove notification mNotificationManager.cancel(NOTIFICATION_VIEW_ID); mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser); Log.d(TAG, "Deleted recording " + uri); break; } Loading Loading @@ -215,11 +226,12 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis mController.updateState(true); createRecordingNotification(); mUiEventLogger.log(Events.ScreenRecordEvent.SCREEN_RECORD_START); } catch (IOException | RemoteException e) { } catch (IOException | RemoteException | IllegalStateException e) { Toast.makeText(this, R.string.screenrecord_start_error, Toast.LENGTH_LONG) .show(); e.printStackTrace(); mController.updateState(false); } } Loading @@ -242,7 +254,6 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis ? res.getString(R.string.screenrecord_ongoing_screen_only) : res.getString(R.string.screenrecord_ongoing_screen_and_audio); Intent stopIntent = getNotificationIntent(this); Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_screenrecord) Loading @@ -254,7 +265,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis .setOngoing(true) .setContentIntent( PendingIntent.getService(this, REQUEST_CODE, stopIntent, PendingIntent.FLAG_UPDATE_CURRENT)) PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)) .addExtras(extras); startForeground(NOTIFICATION_RECORDING_ID, builder.build()); } Loading @@ -265,11 +276,17 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis String notificationTitle = mAudioSource == ScreenRecordingAudioSource.NONE ? res.getString(R.string.screenrecord_ongoing_screen_only) : res.getString(R.string.screenrecord_ongoing_screen_and_audio); Bundle extras = new Bundle(); extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, res.getString(R.string.screenrecord_name)); Notification.Builder builder = new Notification.Builder(getApplicationContext(), CHANNEL_ID) .setContentTitle(notificationTitle) .setContentText( getResources().getString(R.string.screenrecord_background_processing_label)) .setSmallIcon(R.drawable.ic_screenrecord); .setSmallIcon(R.drawable.ic_screenrecord) .addExtras(extras); return builder.build(); } Loading @@ -287,7 +304,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis this, REQUEST_CODE, getShareIntent(this, uri.toString()), PendingIntent.FLAG_UPDATE_CURRENT)) PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)) .build(); Notification.Action deleteAction = new Notification.Action.Builder( Loading @@ -297,7 +314,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis this, REQUEST_CODE, getDeleteIntent(this, uri.toString()), PendingIntent.FLAG_UPDATE_CURRENT)) PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)) .build(); Bundle extras = new Bundle(); Loading Loading @@ -328,34 +345,36 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis return builder.build(); } private void stopRecording() { private void stopRecording(int userId) { setTapsVisible(mOriginalShowTaps); if (getRecorder() != null) { getRecorder().end(); saveRecording(); saveRecording(userId); } else { Log.e(TAG, "stopRecording called, but recorder was null"); } mController.updateState(false); } private void saveRecording() { mNotificationManager.notify(NOTIFICATION_PROCESSING_ID, createProcessingNotification()); private void saveRecording(int userId) { UserHandle currentUser = new UserHandle(userId); mNotificationManager.notifyAsUser(null, NOTIFICATION_PROCESSING_ID, createProcessingNotification(), currentUser); mLongExecutor.execute(() -> { try { Log.d(TAG, "saving recording"); Notification notification = createSaveNotification(getRecorder().save()); if (!mController.isRecording()) { Log.d(TAG, "showing saved notification"); mNotificationManager.notify(NOTIFICATION_VIEW_ID, notification); mNotificationManager.notifyAsUser(null, NOTIFICATION_VIEW_ID, notification, currentUser); } } catch (IOException e) { Log.e(TAG, "Error saving screen recording: " + e.getMessage()); Toast.makeText(this, R.string.screenrecord_delete_error, Toast.LENGTH_LONG) .show(); } finally { mNotificationManager.cancel(NOTIFICATION_PROCESSING_ID); mNotificationManager.cancelAsUser(null, NOTIFICATION_PROCESSING_ID, currentUser); } }); } Loading @@ -371,7 +390,9 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis * @return */ public static Intent getStopIntent(Context context) { return new Intent(context, RecordingService.class).setAction(ACTION_STOP); return new Intent(context, RecordingService.class) .setAction(ACTION_STOP) .putExtra(Intent.EXTRA_USER_HANDLE, context.getUserId()); } /** Loading
packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java +5 −10 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.systemui.screenrecord; import android.content.Context; import android.media.AudioAttributes; import android.media.AudioFormat; import android.media.AudioPlaybackCaptureConfiguration; Loading @@ -39,7 +38,6 @@ public class ScreenInternalAudioRecorder { private static String TAG = "ScreenAudioRecorder"; private static final int TIMEOUT = 500; private static final float MIC_VOLUME_SCALE = 1.4f; private final Context mContext; private AudioRecord mAudioRecord; private AudioRecord mAudioRecordMic; private Config mConfig = new Config(); Loading @@ -49,17 +47,14 @@ public class ScreenInternalAudioRecorder { private long mPresentationTime; private long mTotalBytes; private MediaMuxer mMuxer; private String mOutFile; private boolean mMic; private int mTrackId = -1; public ScreenInternalAudioRecorder(String outFile, Context context, MediaProjection mp, boolean includeMicInput) throws IOException { public ScreenInternalAudioRecorder(String outFile, MediaProjection mp, boolean includeMicInput) throws IOException { mMic = includeMicInput; mOutFile = outFile; mMuxer = new MediaMuxer(outFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); mContext = context; mMediaProjection = mp; Log.d(TAG, "creating audio file " + outFile); setupSimple(); Loading Loading @@ -266,8 +261,9 @@ public class ScreenInternalAudioRecorder { /** * start recording * @throws IllegalStateException if recording fails to initialize */ public void start() { public void start() throws IllegalStateException { if (mThread != null) { Log.e(TAG, "a recording is being done in parallel or stop is not called"); } Loading @@ -276,8 +272,7 @@ public class ScreenInternalAudioRecorder { Log.d(TAG, "channel count " + mAudioRecord.getChannelCount()); mCodec.start(); if (mAudioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) { Log.e(TAG, "Error starting audio recording"); return; throw new IllegalStateException("Audio recording failed to start"); } mThread.start(); } Loading