1 /* 2 * Copyright (C) 2018 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 17 package com.android.systemui.screenrecord; 18 19 import static android.app.Activity.RESULT_OK; 20 21 import static com.android.systemui.media.MediaProjectionAppSelectorActivity.EXTRA_CAPTURE_REGION_RESULT_RECEIVER; 22 import static com.android.systemui.media.MediaProjectionAppSelectorActivity.KEY_CAPTURE_TARGET; 23 import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.INTERNAL; 24 import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC; 25 import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC_AND_INTERNAL; 26 import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.NONE; 27 28 import android.app.Activity; 29 import android.app.PendingIntent; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.os.Bundle; 33 import android.os.Handler; 34 import android.os.Looper; 35 import android.os.ResultReceiver; 36 import android.view.Gravity; 37 import android.view.View; 38 import android.view.Window; 39 import android.view.WindowManager; 40 import android.widget.ArrayAdapter; 41 import android.widget.Spinner; 42 import android.widget.Switch; 43 import android.widget.TextView; 44 45 import androidx.annotation.Nullable; 46 47 import com.android.systemui.R; 48 import com.android.systemui.animation.ActivityLaunchAnimator; 49 import com.android.systemui.animation.DialogLaunchAnimator; 50 import com.android.systemui.flags.FeatureFlags; 51 import com.android.systemui.flags.Flags; 52 import com.android.systemui.media.MediaProjectionAppSelectorActivity; 53 import com.android.systemui.media.MediaProjectionCaptureTarget; 54 import com.android.systemui.plugins.ActivityStarter; 55 import com.android.systemui.settings.UserContextProvider; 56 import com.android.systemui.statusbar.phone.SystemUIDialog; 57 58 import java.util.Arrays; 59 import java.util.List; 60 61 /** 62 * Dialog to select screen recording options 63 */ 64 public class ScreenRecordDialog extends SystemUIDialog { 65 private static final List<ScreenRecordingAudioSource> MODES = Arrays.asList(INTERNAL, MIC, 66 MIC_AND_INTERNAL); 67 private static final long DELAY_MS = 3000; 68 private static final long INTERVAL_MS = 1000; 69 70 private final RecordingController mController; 71 private final UserContextProvider mUserContextProvider; 72 @Nullable 73 private final Runnable mOnStartRecordingClicked; 74 private final ActivityStarter mActivityStarter; 75 private final FeatureFlags mFlags; 76 private final DialogLaunchAnimator mDialogLaunchAnimator; 77 private Switch mTapsSwitch; 78 private Switch mAudioSwitch; 79 private Spinner mOptions; 80 ScreenRecordDialog(Context context, RecordingController controller, ActivityStarter activityStarter, UserContextProvider userContextProvider, FeatureFlags flags, DialogLaunchAnimator dialogLaunchAnimator, @Nullable Runnable onStartRecordingClicked)81 public ScreenRecordDialog(Context context, RecordingController controller, 82 ActivityStarter activityStarter, UserContextProvider userContextProvider, 83 FeatureFlags flags, DialogLaunchAnimator dialogLaunchAnimator, 84 @Nullable Runnable onStartRecordingClicked) { 85 super(context); 86 mController = controller; 87 mUserContextProvider = userContextProvider; 88 mActivityStarter = activityStarter; 89 mDialogLaunchAnimator = dialogLaunchAnimator; 90 mFlags = flags; 91 mOnStartRecordingClicked = onStartRecordingClicked; 92 } 93 94 @Override onCreate(Bundle savedInstanceState)95 public void onCreate(Bundle savedInstanceState) { 96 super.onCreate(savedInstanceState); 97 98 Window window = getWindow(); 99 100 window.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS); 101 102 window.setGravity(Gravity.CENTER); 103 setTitle(R.string.screenrecord_title); 104 105 setContentView(R.layout.screen_record_dialog); 106 107 TextView cancelBtn = findViewById(R.id.button_cancel); 108 cancelBtn.setOnClickListener(v -> dismiss()); 109 110 TextView startBtn = findViewById(R.id.button_start); 111 startBtn.setOnClickListener(v -> { 112 if (mOnStartRecordingClicked != null) { 113 // Note that it is important to run this callback before dismissing, so that the 114 // callback can disable the dialog exit animation if it wants to. 115 mOnStartRecordingClicked.run(); 116 } 117 118 // Start full-screen recording 119 requestScreenCapture(/* captureTarget= */ null); 120 dismiss(); 121 }); 122 123 if (mFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)) { 124 TextView appBtn = findViewById(R.id.button_app); 125 126 appBtn.setVisibility(View.VISIBLE); 127 appBtn.setOnClickListener(v -> { 128 Intent intent = new Intent(getContext(), MediaProjectionAppSelectorActivity.class); 129 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 130 131 // We can't start activity for result here so we use result receiver to get 132 // the selected target to capture 133 intent.putExtra(EXTRA_CAPTURE_REGION_RESULT_RECEIVER, 134 new CaptureTargetResultReceiver()); 135 136 ActivityLaunchAnimator.Controller animationController = 137 mDialogLaunchAnimator.createActivityLaunchController(appBtn); 138 139 if (animationController == null) { 140 dismiss(); 141 } 142 143 mActivityStarter.startActivity(intent, /* dismissShade= */ true, 144 animationController); 145 }); 146 } 147 148 mAudioSwitch = findViewById(R.id.screenrecord_audio_switch); 149 mTapsSwitch = findViewById(R.id.screenrecord_taps_switch); 150 mOptions = findViewById(R.id.screen_recording_options); 151 ArrayAdapter a = new ScreenRecordingAdapter(getContext().getApplicationContext(), 152 android.R.layout.simple_spinner_dropdown_item, 153 MODES); 154 a.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 155 mOptions.setAdapter(a); 156 mOptions.setOnItemClickListenerInt((parent, view, position, id) -> { 157 mAudioSwitch.setChecked(true); 158 }); 159 } 160 161 /** 162 * Starts screen capture after some countdown 163 * @param captureTarget target to capture (could be e.g. a task) or 164 * null to record the whole screen 165 */ requestScreenCapture(@ullable MediaProjectionCaptureTarget captureTarget)166 private void requestScreenCapture(@Nullable MediaProjectionCaptureTarget captureTarget) { 167 Context userContext = mUserContextProvider.getUserContext(); 168 boolean showTaps = mTapsSwitch.isChecked(); 169 ScreenRecordingAudioSource audioMode = mAudioSwitch.isChecked() 170 ? (ScreenRecordingAudioSource) mOptions.getSelectedItem() 171 : NONE; 172 PendingIntent startIntent = PendingIntent.getForegroundService(userContext, 173 RecordingService.REQUEST_CODE, 174 RecordingService.getStartIntent( 175 userContext, Activity.RESULT_OK, 176 audioMode.ordinal(), showTaps, captureTarget), 177 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); 178 PendingIntent stopIntent = PendingIntent.getService(userContext, 179 RecordingService.REQUEST_CODE, 180 RecordingService.getStopIntent(userContext), 181 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); 182 mController.startCountdown(DELAY_MS, INTERVAL_MS, startIntent, stopIntent); 183 } 184 185 private class CaptureTargetResultReceiver extends ResultReceiver { 186 CaptureTargetResultReceiver()187 CaptureTargetResultReceiver() { 188 super(new Handler(Looper.getMainLooper())); 189 } 190 191 @Override onReceiveResult(int resultCode, Bundle resultData)192 protected void onReceiveResult(int resultCode, Bundle resultData) { 193 if (resultCode == RESULT_OK) { 194 MediaProjectionCaptureTarget captureTarget = resultData 195 .getParcelable(KEY_CAPTURE_TARGET, MediaProjectionCaptureTarget.class); 196 197 // Start recording of the selected target 198 requestScreenCapture(captureTarget); 199 } 200 } 201 } 202 } 203