|
18 | 18 |
|
19 | 19 | import android.app.Activity; |
20 | 20 | import android.content.BroadcastReceiver; |
| 21 | +import android.content.Context; |
| 22 | +import android.content.Intent; |
21 | 23 | import android.content.IntentFilter; |
| 24 | +import android.media.projection.MediaProjectionManager; |
22 | 25 | import android.os.Build; |
23 | 26 | import android.os.Bundle; |
24 | 27 | import android.os.Handler; |
25 | 28 | import android.util.Log; |
26 | 29 |
|
| 30 | +import java.io.File; |
| 31 | +import java.nio.file.Paths; |
27 | 32 | import java.util.Arrays; |
28 | 33 | import java.util.List; |
29 | 34 |
|
|
39 | 44 | import io.appium.settings.receivers.SmsReader; |
40 | 45 | import io.appium.settings.receivers.UnpairBluetoothDevicesReceiver; |
41 | 46 | import io.appium.settings.receivers.WiFiConnectionSettingReceiver; |
| 47 | +import io.appium.settings.recorder.RecorderService; |
| 48 | +import io.appium.settings.recorder.RecorderUtil; |
| 49 | + |
| 50 | +import static io.appium.settings.recorder.RecorderConstant.ACTION_RECORDING_BASE; |
| 51 | +import static io.appium.settings.recorder.RecorderConstant.ACTION_RECORDING_FILENAME; |
| 52 | +import static io.appium.settings.recorder.RecorderConstant.ACTION_RECORDING_MAX_DURATION; |
| 53 | +import static io.appium.settings.recorder.RecorderConstant.ACTION_RECORDING_PRIORITY; |
| 54 | +import static io.appium.settings.recorder.RecorderConstant.ACTION_RECORDING_RESOLUTION; |
| 55 | +import static io.appium.settings.recorder.RecorderConstant.ACTION_RECORDING_RESULT_CODE; |
| 56 | +import static io.appium.settings.recorder.RecorderConstant.ACTION_RECORDING_ROTATION; |
| 57 | +import static io.appium.settings.recorder.RecorderConstant.ACTION_RECORDING_START; |
| 58 | +import static io.appium.settings.recorder.RecorderConstant.ACTION_RECORDING_STOP; |
| 59 | +import static io.appium.settings.recorder.RecorderConstant.NO_PATH_SET; |
| 60 | +import static io.appium.settings.recorder.RecorderConstant.NO_RESOLUTION_MODE_SET; |
| 61 | +import static io.appium.settings.recorder.RecorderConstant.RECORDING_MAX_DURATION_DEFAULT_MS; |
| 62 | +import static io.appium.settings.recorder.RecorderConstant.RECORDING_PRIORITY_DEFAULT; |
| 63 | +import static io.appium.settings.recorder.RecorderConstant.RECORDING_ROTATION_DEFAULT_DEGREE; |
| 64 | +import static io.appium.settings.recorder.RecorderConstant.REQUEST_CODE_SCREEN_CAPTURE; |
42 | 65 |
|
43 | 66 | public class Settings extends Activity { |
44 | 67 | private static final String TAG = "APPIUM SETTINGS"; |
45 | 68 |
|
| 69 | + private String recordingOutputPath = NO_PATH_SET; |
| 70 | + private int recordingRotation = RECORDING_ROTATION_DEFAULT_DEGREE; |
| 71 | + private int recordingPriority = RECORDING_PRIORITY_DEFAULT; |
| 72 | + private int recordingMaxDuration = RECORDING_MAX_DURATION_DEFAULT_MS; |
| 73 | + private String recordingResolutionMode = NO_RESOLUTION_MODE_SET; |
| 74 | + |
46 | 75 | @Override |
47 | 76 | public void onCreate(Bundle savedInstanceState) { |
48 | 77 | super.onCreate(savedInstanceState); |
@@ -70,12 +99,152 @@ public void onCreate(Bundle savedInstanceState) { |
70 | 99 | LocationTracker.getInstance().start(this); |
71 | 100 | } |
72 | 101 |
|
| 102 | + handleRecording(getIntent()); |
| 103 | + } |
| 104 | + |
| 105 | + private void handleRecording(Intent intent) { |
| 106 | + if (intent == null) { |
| 107 | + Log.e(TAG, "handleRecording: Unable to retrieve intent instance"); |
| 108 | + finishActivity(); |
| 109 | + return; |
| 110 | + } |
| 111 | + |
| 112 | + String recordingAction = intent.getAction(); |
| 113 | + if (recordingAction == null) { |
| 114 | + Log.e(TAG, "handleRecording: Unable to retrieve intent.action instance"); |
| 115 | + finishActivity(); |
| 116 | + return; |
| 117 | + } |
| 118 | + |
| 119 | + if (!recordingAction.startsWith(ACTION_RECORDING_BASE)) { |
| 120 | + Log.i(TAG, "handleRecording: Received different intent with action: " |
| 121 | + + recordingAction); |
| 122 | + finishActivity(); |
| 123 | + return; |
| 124 | + } |
| 125 | + |
| 126 | + if (RecorderUtil.isLowerThanQ()) { |
| 127 | + Log.e(TAG, "handleRecording: Current Android OS Version is lower than Q"); |
| 128 | + finishActivity(); |
| 129 | + return; |
| 130 | + } |
| 131 | + |
| 132 | + if (!RecorderUtil.areRecordingPermissionsGranted(getApplicationContext())) { |
| 133 | + Log.e(TAG, "handleRecording: Required permissions are not granted"); |
| 134 | + finishActivity(); |
| 135 | + return; |
| 136 | + } |
| 137 | + |
| 138 | + if (recordingAction.equals(ACTION_RECORDING_START)) { |
| 139 | + String recordingFilename = intent.getStringExtra(ACTION_RECORDING_FILENAME); |
| 140 | + if (!RecorderUtil.isValidFileName(recordingFilename)) { |
| 141 | + Log.e(TAG, "handleRecording: Invalid filename passed by user: " |
| 142 | + + recordingFilename); |
| 143 | + finishActivity(); |
| 144 | + return; |
| 145 | + } |
| 146 | + |
| 147 | + /* |
| 148 | + External Storage File Directory for app |
| 149 | + (i.e /storage/emulated/0/Android/data/io.appium.settings/files) may not be created |
| 150 | + so we need to call getExternalFilesDir() method twice |
| 151 | + source:https://www.androidbugfix.com/2021/10/getexternalfilesdirnull-returns-null-in.html |
| 152 | + */ |
| 153 | + File externalStorageFile = getExternalFilesDir(null); |
| 154 | + if (externalStorageFile == null) { |
| 155 | + externalStorageFile = getExternalFilesDir(null); |
| 156 | + } |
| 157 | + // if path is still null despite calling method twice, early exit |
| 158 | + if (externalStorageFile == null) { |
| 159 | + Log.e(TAG, "handleRecording: Unable to retrieve external storage file path"); |
| 160 | + finishActivity(); |
| 161 | + return; |
| 162 | + } |
| 163 | + |
| 164 | + recordingOutputPath = Paths |
| 165 | + .get(externalStorageFile.getAbsolutePath(), recordingFilename) |
| 166 | + .toAbsolutePath() |
| 167 | + .toString(); |
| 168 | + |
| 169 | + recordingRotation = RecorderUtil.getDeviceRotationInDegree(getApplicationContext()); |
| 170 | + |
| 171 | + recordingPriority = RecorderUtil.getRecordingPriority(intent); |
| 172 | + |
| 173 | + recordingMaxDuration = RecorderUtil.getRecordingMaxDuration(intent); |
| 174 | + |
| 175 | + recordingResolutionMode = RecorderUtil.getRecordingResolutionMode(intent); |
| 176 | + |
| 177 | + // start record |
| 178 | + final MediaProjectionManager manager |
| 179 | + = (MediaProjectionManager) getSystemService( |
| 180 | + Context.MEDIA_PROJECTION_SERVICE); |
| 181 | + |
| 182 | + if (manager == null) { |
| 183 | + Log.e(TAG, "handleRecording: " + |
| 184 | + "Unable to retrieve MediaProjectionManager instance"); |
| 185 | + finishActivity(); |
| 186 | + return; |
| 187 | + } |
| 188 | + |
| 189 | + final Intent permissionIntent = manager.createScreenCaptureIntent(); |
| 190 | + |
| 191 | + startActivityForResult(permissionIntent, REQUEST_CODE_SCREEN_CAPTURE); |
| 192 | + } else if (recordingAction.equals(ACTION_RECORDING_STOP)) { |
| 193 | + // stop record |
| 194 | + final Intent recorderIntent = new Intent(this, RecorderService.class); |
| 195 | + recorderIntent.setAction(ACTION_RECORDING_STOP); |
| 196 | + startService(recorderIntent); |
| 197 | + |
| 198 | + finishActivity(); |
| 199 | + } else { |
| 200 | + Log.e(TAG, "handleRecording: Unknown recording intent with action:" |
| 201 | + + recordingAction); |
| 202 | + finishActivity(); |
| 203 | + } |
| 204 | + } |
| 205 | + |
| 206 | + private void finishActivity() { |
73 | 207 | Log.d(TAG, "Closing the app"); |
74 | 208 | Handler handler = new Handler(); |
75 | 209 | handler.postDelayed(Settings.this::finish, 1000); |
76 | 210 | } |
77 | 211 |
|
78 | | - private void registerSettingsReceivers(List<Class<? extends BroadcastReceiver>> receiverClasses) { |
| 212 | + @Override |
| 213 | + protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) |
| 214 | + { |
| 215 | + super.onActivityResult(requestCode, resultCode, data); |
| 216 | + if (REQUEST_CODE_SCREEN_CAPTURE != requestCode) { |
| 217 | + Log.e(TAG, "handleRecording: onActivityResult: " + |
| 218 | + "Received unknown request with code: " + requestCode); |
| 219 | + finishActivity(); |
| 220 | + return; |
| 221 | + } |
| 222 | + |
| 223 | + if (resultCode != Activity.RESULT_OK) { |
| 224 | + Log.e(TAG, "handleRecording: onActivityResult: " + |
| 225 | + "MediaProjection permission is not granted, " + |
| 226 | + "Did you apply appops adb command?"); |
| 227 | + finishActivity(); |
| 228 | + return; |
| 229 | + } |
| 230 | + |
| 231 | + final Intent intent = new Intent(this, RecorderService.class); |
| 232 | + intent.setAction(ACTION_RECORDING_START); |
| 233 | + intent.putExtra(ACTION_RECORDING_RESULT_CODE, resultCode); |
| 234 | + intent.putExtra(ACTION_RECORDING_FILENAME, recordingOutputPath); |
| 235 | + intent.putExtra(ACTION_RECORDING_ROTATION, recordingRotation); |
| 236 | + intent.putExtra(ACTION_RECORDING_PRIORITY, recordingPriority); |
| 237 | + intent.putExtra(ACTION_RECORDING_MAX_DURATION, recordingMaxDuration); |
| 238 | + intent.putExtra(ACTION_RECORDING_RESOLUTION, recordingResolutionMode); |
| 239 | + intent.putExtras(data); |
| 240 | + |
| 241 | + startService(intent); |
| 242 | + |
| 243 | + finishActivity(); |
| 244 | + } |
| 245 | + |
| 246 | + private void registerSettingsReceivers(List<Class<? extends BroadcastReceiver>> receiverClasses) |
| 247 | + { |
79 | 248 | for (Class<? extends BroadcastReceiver> receiverClass: receiverClasses) { |
80 | 249 | try { |
81 | 250 | final BroadcastReceiver receiver = receiverClass.newInstance(); |
|
0 commit comments