io.appium.settings 4.2.0 → 4.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/apks/settings_apk-debug.apk +0 -0
  2. package/package.json +2 -8
  3. package/app/build.gradle +0 -38
  4. package/app/src/main/AndroidManifest.xml +0 -149
  5. package/app/src/main/java/io/appium/settings/AppiumIME.java +0 -153
  6. package/app/src/main/java/io/appium/settings/ForegroundService.java +0 -70
  7. package/app/src/main/java/io/appium/settings/LocationService.java +0 -215
  8. package/app/src/main/java/io/appium/settings/LocationTracker.java +0 -313
  9. package/app/src/main/java/io/appium/settings/NLService.java +0 -134
  10. package/app/src/main/java/io/appium/settings/Settings.java +0 -260
  11. package/app/src/main/java/io/appium/settings/UnicodeIME.java +0 -187
  12. package/app/src/main/java/io/appium/settings/Unlock.java +0 -75
  13. package/app/src/main/java/io/appium/settings/handlers/AbstractSettingHandler.java +0 -64
  14. package/app/src/main/java/io/appium/settings/handlers/AnimationSettingHandler.java +0 -66
  15. package/app/src/main/java/io/appium/settings/handlers/BluetoothConnectionSettingHandler.java +0 -42
  16. package/app/src/main/java/io/appium/settings/handlers/DataConnectionSettingHandler.java +0 -134
  17. package/app/src/main/java/io/appium/settings/handlers/LocaleSettingHandler.java +0 -86
  18. package/app/src/main/java/io/appium/settings/handlers/WiFiConnectionSettingHandler.java +0 -40
  19. package/app/src/main/java/io/appium/settings/helpers/NotificationHelpers.java +0 -67
  20. package/app/src/main/java/io/appium/settings/helpers/PlayServicesHelpers.java +0 -30
  21. package/app/src/main/java/io/appium/settings/helpers/Utils.java +0 -29
  22. package/app/src/main/java/io/appium/settings/location/FusedLocationProvider.java +0 -99
  23. package/app/src/main/java/io/appium/settings/location/LocationBuilder.java +0 -80
  24. package/app/src/main/java/io/appium/settings/location/LocationManagerProvider.java +0 -97
  25. package/app/src/main/java/io/appium/settings/location/MockLocationProvider.java +0 -31
  26. package/app/src/main/java/io/appium/settings/notifications/StoredNotification.java +0 -103
  27. package/app/src/main/java/io/appium/settings/notifications/StoredNotifications.java +0 -50
  28. package/app/src/main/java/io/appium/settings/receivers/AbstractSettingReceiver.java +0 -62
  29. package/app/src/main/java/io/appium/settings/receivers/AnimationSettingReceiver.java +0 -40
  30. package/app/src/main/java/io/appium/settings/receivers/BluetoothConnectionSettingReceiver.java +0 -49
  31. package/app/src/main/java/io/appium/settings/receivers/ClipboardReceiver.java +0 -91
  32. package/app/src/main/java/io/appium/settings/receivers/DataConnectionSettingReceiver.java +0 -40
  33. package/app/src/main/java/io/appium/settings/receivers/HasAction.java +0 -21
  34. package/app/src/main/java/io/appium/settings/receivers/LocaleSettingReceiver.java +0 -127
  35. package/app/src/main/java/io/appium/settings/receivers/LocationInfoReceiver.java +0 -67
  36. package/app/src/main/java/io/appium/settings/receivers/MediaScannerReceiver.java +0 -91
  37. package/app/src/main/java/io/appium/settings/receivers/NotificationsReceiver.java +0 -100
  38. package/app/src/main/java/io/appium/settings/receivers/SmsReader.java +0 -113
  39. package/app/src/main/java/io/appium/settings/receivers/UnpairBluetoothDevicesReceiver.java +0 -84
  40. package/app/src/main/java/io/appium/settings/receivers/WiFiConnectionSettingReceiver.java +0 -39
  41. package/app/src/main/java/io/appium/settings/recorder/RecorderConstant.java +0 -94
  42. package/app/src/main/java/io/appium/settings/recorder/RecorderService.java +0 -202
  43. package/app/src/main/java/io/appium/settings/recorder/RecorderThread.java +0 -459
  44. package/app/src/main/java/io/appium/settings/recorder/RecorderUtil.java +0 -298
  45. package/app/src/main/res/drawable-hdpi/ic_launcher.png +0 -0
  46. package/app/src/main/res/drawable-ldpi/ic_launcher.png +0 -0
  47. package/app/src/main/res/drawable-mdpi/ic_launcher.png +0 -0
  48. package/app/src/main/res/drawable-xhdpi/ic_launcher.png +0 -0
  49. package/app/src/main/res/drawable-xxhdpi/ic_launcher.png +0 -0
  50. package/app/src/main/res/drawable-xxxhdpi/ic_launcher.png +0 -0
  51. package/app/src/main/res/layout/main.xml +0 -29
  52. package/app/src/main/res/values/dimens.xml +0 -7
  53. package/app/src/main/res/values/strings.xml +0 -9
  54. package/app/src/main/res/xml/method.xml +0 -3
  55. package/build.gradle +0 -17
  56. package/gradle/wrapper/gradle-wrapper.jar +0 -0
  57. package/gradle/wrapper/gradle-wrapper.properties +0 -6
  58. package/gradle.properties +0 -15
  59. package/gradlew +0 -160
  60. package/gradlew.bat +0 -90
  61. package/settings.gradle +0 -1
@@ -1,202 +0,0 @@
1
- /*
2
- Copyright 2012-present Appium Committers
3
- <p>
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
- <p>
8
- http://www.apache.org/licenses/LICENSE-2.0
9
- <p>
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 io.appium.settings.recorder;
18
-
19
- import android.app.Service;
20
- import android.content.Context;
21
- import android.content.Intent;
22
- import android.media.projection.MediaProjection;
23
- import android.media.projection.MediaProjectionManager;
24
- import android.os.Build;
25
- import android.os.IBinder;
26
- import android.util.DisplayMetrics;
27
- import android.util.Log;
28
- import android.util.Size;
29
-
30
- import androidx.annotation.Nullable;
31
- import androidx.annotation.RequiresApi;
32
- import io.appium.settings.helpers.NotificationHelpers;
33
-
34
- import static io.appium.settings.recorder.RecorderConstant.ACTION_RECORDING_FILENAME;
35
- import static io.appium.settings.recorder.RecorderConstant.ACTION_RECORDING_MAX_DURATION;
36
- import static io.appium.settings.recorder.RecorderConstant.ACTION_RECORDING_PRIORITY;
37
- import static io.appium.settings.recorder.RecorderConstant.ACTION_RECORDING_RESOLUTION;
38
- import static io.appium.settings.recorder.RecorderConstant.ACTION_RECORDING_RESULT_CODE;
39
- import static io.appium.settings.recorder.RecorderConstant.ACTION_RECORDING_ROTATION;
40
- import static io.appium.settings.recorder.RecorderConstant.ACTION_RECORDING_START;
41
- import static io.appium.settings.recorder.RecorderConstant.ACTION_RECORDING_STOP;
42
- import static io.appium.settings.recorder.RecorderConstant.RECORDING_MAX_DURATION_DEFAULT_MS;
43
- import static io.appium.settings.recorder.RecorderConstant.RECORDING_PRIORITY_DEFAULT;
44
- import static io.appium.settings.recorder.RecorderConstant.RECORDING_ROTATION_DEFAULT_DEGREE;
45
-
46
- public class RecorderService extends Service {
47
- private static final String TAG = "RecorderService";
48
-
49
- private static RecorderThread recorderThread;
50
-
51
- public RecorderService() {
52
- super();
53
- }
54
-
55
- @Override
56
- public void onDestroy() {
57
- Log.v(TAG, "onDestroy called: Stopping recorder");
58
- if (recorderThread != null && recorderThread.isRecordingRunning()) {
59
- recorderThread.stopRecording();
60
- }
61
- super.onDestroy();
62
- }
63
-
64
- @Nullable
65
- @Override
66
- public IBinder onBind(final Intent intent) {
67
- return null;
68
- }
69
-
70
- @RequiresApi(api = Build.VERSION_CODES.Q)
71
- @Override
72
- public int onStartCommand(final Intent intent, final int flags, final int startId) {
73
- if (intent == null) {
74
- Log.e(TAG, "onStartCommand: Unable to retrieve recording intent");
75
- return START_NOT_STICKY;
76
- }
77
- final String action = intent.getAction();
78
- if (action == null) {
79
- Log.e(TAG, "onStartCommand: Unable to retrieve recording intent:action");
80
- return START_NOT_STICKY;
81
- }
82
-
83
- int result = START_STICKY;
84
- if (ACTION_RECORDING_START.equals(action)) {
85
- showNotification(); // TODO is this really necessary
86
-
87
- MediaProjectionManager mMediaProjectionManager =
88
- (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
89
-
90
- if (mMediaProjectionManager != null) {
91
- startRecord(mMediaProjectionManager, intent);
92
- } else {
93
- Log.e(TAG, "onStartCommand: " +
94
- "Unable to retrieve MediaProjectionManager instance");
95
- result = START_NOT_STICKY;
96
- }
97
- } else if (ACTION_RECORDING_STOP.equals(action)) {
98
- Log.v(TAG, "onStartCommand: Received recording stop intent, stopping recording");
99
- stopRecord();
100
- result = START_NOT_STICKY;
101
- } else {
102
- Log.v(TAG, "onStartCommand: Received unknown recording intent with action: "
103
- + action);
104
- result = START_NOT_STICKY;
105
- }
106
-
107
- return result;
108
- }
109
-
110
- /**
111
- * start recording
112
- */
113
- @RequiresApi(api = Build.VERSION_CODES.Q)
114
- private void startRecord(MediaProjectionManager mediaProjectionManager,
115
- final Intent intent) {
116
- if (recorderThread != null) {
117
- if (recorderThread.isRecordingRunning()) {
118
- Log.v(TAG, "Recording is already continuing, exiting");
119
- return;
120
- } else {
121
- Log.w(TAG, "Recording is stopped, " +
122
- "but recording instance is still alive, starting recording");
123
- recorderThread = null;
124
- }
125
- }
126
-
127
- int resultCode = intent.getIntExtra(ACTION_RECORDING_RESULT_CODE, 0);
128
- // get MediaProjection
129
- final MediaProjection projection = mediaProjectionManager.getMediaProjection(resultCode,
130
- intent);
131
- if (projection == null) {
132
- Log.e(TAG, "Recording is stopped, Unable to retrieve MediaProjection instance");
133
- return;
134
- }
135
-
136
- String outputFilePath = intent.getStringExtra(ACTION_RECORDING_FILENAME);
137
- if (outputFilePath == null) {
138
- Log.e(TAG, "Recording is stopped, Unable to retrieve outputFilePath instance");
139
- return;
140
- }
141
-
142
- /* TODO we need to rotate frames that comes from virtual screen before writing to file via muxer,
143
- * for handling landscape mode properly, we need to find a way to rotate images somehow fast and reliable
144
- */
145
- int recordingRotationDegree = intent.getIntExtra(ACTION_RECORDING_ROTATION,
146
- RECORDING_ROTATION_DEFAULT_DEGREE);
147
-
148
- DisplayMetrics metrics = getResources().getDisplayMetrics();
149
- int rawWidth = metrics.widthPixels;
150
- int rawHeight = metrics.heightPixels;
151
- int rawDpi = metrics.densityDpi;
152
-
153
- String recordingResolutionMode = intent.getStringExtra(ACTION_RECORDING_RESOLUTION);
154
-
155
- Size recordingResolution = RecorderUtil.
156
- getRecordingResolution(recordingResolutionMode);
157
-
158
- int resolutionWidth = recordingResolution.getWidth();
159
- int resolutionHeight = recordingResolution.getHeight();
160
-
161
- /*
162
- MediaCodec's tested supported resolutions (as per CTS tests) are for landscape mode as default (1920x1080, 1280x720 etc.)
163
- but if phone or tablet is in portrait mode (usually it is),
164
- we need to flip width/height to match it
165
- */
166
- if (rawWidth < rawHeight) {
167
- resolutionWidth = recordingResolution.getHeight();
168
- resolutionHeight = recordingResolution.getWidth();
169
- }
170
-
171
- Log.v(TAG, String.format("Starting recording with resolution(widthxheight): (%dx%d)",
172
- resolutionWidth, resolutionHeight));
173
-
174
- int recordingPriority = intent.getIntExtra(ACTION_RECORDING_PRIORITY,
175
- RECORDING_PRIORITY_DEFAULT);
176
-
177
- int recordingMaxDuration = intent.getIntExtra(ACTION_RECORDING_MAX_DURATION,
178
- RECORDING_MAX_DURATION_DEFAULT_MS);
179
-
180
- recorderThread = new RecorderThread(projection, outputFilePath,
181
- resolutionWidth, resolutionHeight, rawDpi,
182
- recordingRotationDegree, recordingPriority, recordingMaxDuration);
183
- recorderThread.startRecording();
184
- }
185
-
186
- /**
187
- * stop recording
188
- */
189
- private void stopRecord() {
190
- if (recorderThread != null) {
191
- recorderThread.stopRecording();
192
- recorderThread = null;
193
- }
194
- stopSelf();
195
- }
196
-
197
- private void showNotification() {
198
- // Set the info for the views that show in the notification panel.
199
- startForeground(NotificationHelpers.APPIUM_NOTIFICATION_IDENTIFIER,
200
- NotificationHelpers.getNotification(this));
201
- }
202
- }
@@ -1,459 +0,0 @@
1
- /*
2
- Copyright 2012-present Appium Committers
3
- <p>
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
- <p>
8
- http://www.apache.org/licenses/LICENSE-2.0
9
- <p>
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 io.appium.settings.recorder;
18
-
19
- import android.hardware.display.DisplayManager;
20
- import android.hardware.display.VirtualDisplay;
21
- import android.media.AudioAttributes;
22
- import android.media.AudioFormat;
23
- import android.media.AudioPlaybackCaptureConfiguration;
24
- import android.media.AudioRecord;
25
- import android.media.MediaCodec;
26
- import android.media.MediaCodecInfo;
27
- import android.media.MediaFormat;
28
- import android.media.MediaMuxer;
29
- import android.media.projection.MediaProjection;
30
- import android.os.Build;
31
- import android.os.Handler;
32
- import android.os.Looper;
33
- import android.util.Log;
34
- import android.view.Surface;
35
-
36
- import java.io.IOException;
37
- import java.nio.ByteBuffer;
38
-
39
- import androidx.annotation.RequiresApi;
40
-
41
- import static io.appium.settings.recorder.RecorderConstant.BPS_IN_MBPS;
42
- import static io.appium.settings.recorder.RecorderConstant.NANOSECONDS_IN_MICROSECOND;
43
- import static io.appium.settings.recorder.RecorderConstant.NO_TIMESTAMP_SET;
44
- import static io.appium.settings.recorder.RecorderConstant.NO_TRACK_INDEX_SET;
45
- import static io.appium.settings.recorder.RecorderConstant.RECORDING_DEFAULT_VIDEO_MIME_TYPE;
46
- import static io.appium.settings.recorder.RecorderConstant.VIDEO_CODEC_DEFAULT_FRAME_RATE;
47
-
48
- public class RecorderThread implements Runnable {
49
-
50
- private static final String TAG = "RecorderThread";
51
-
52
- private final MediaProjection mediaProjection;
53
- private final String outputFilePath;
54
- private final int videoWidth;
55
- private final int videoHeight;
56
- private final int videoDpi;
57
- private final int recordingRotation;
58
- private final int recordingPriority;
59
- private final int recordingMaxDuration;
60
-
61
- private boolean muxerStarted = false;
62
- private boolean isStartTimestampInitialized = false;
63
- private long startTimestampUs = System.nanoTime() / NANOSECONDS_IN_MICROSECOND;
64
- private long lastAudioTimestampUs = NO_TIMESTAMP_SET;
65
-
66
- private int videoTrackIndex = NO_TRACK_INDEX_SET;
67
- private int audioTrackIndex = NO_TRACK_INDEX_SET;
68
-
69
- private volatile boolean stopped = false;
70
- private volatile boolean audioStopped = false;
71
- private volatile boolean hasAsyncError = false;
72
-
73
- @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
74
- private final VirtualDisplay.Callback displayCallback = new VirtualDisplay.Callback() {
75
- @Override
76
- public void onPaused() {
77
- super.onPaused();
78
- Log.v(TAG, "VirtualDisplay callback: Display streaming paused");
79
- }
80
-
81
- @Override
82
- public void onStopped() {
83
- super.onStopped();
84
- if (!stopped) {
85
- hasAsyncError = true;
86
- }
87
- }
88
- };
89
-
90
- public RecorderThread(MediaProjection mediaProjection, String outputFilePath,
91
- int videoWidth, int videoHeight, int videoDpi, int recordingRotation,
92
- int recordingPriority, int recordingMaxDuration) {
93
- this.mediaProjection = mediaProjection;
94
- this.outputFilePath = outputFilePath;
95
- this.videoWidth = videoWidth;
96
- this.videoHeight = videoHeight;
97
- this.videoDpi = videoDpi;
98
- this.recordingRotation = recordingRotation;
99
- this.recordingPriority = recordingPriority;
100
- this.recordingMaxDuration = recordingMaxDuration;
101
- }
102
-
103
- public void startRecording() {
104
- stopped = false;
105
- Thread recordingThread = new Thread(this);
106
- recordingThread.start();
107
- }
108
-
109
- public void stopRecording() {
110
- stopped = true;
111
- }
112
-
113
- public boolean isRecordingRunning() {
114
- return !stopped;
115
- }
116
-
117
- @RequiresApi(api = Build.VERSION_CODES.KITKAT)
118
- private MediaFormat initVideoEncoderFormat(String videoMime, int videoWidth,
119
- int videoHeight, int videoBitrate,
120
- int videoFrameRate) {
121
- MediaFormat encoderFormat = MediaFormat.createVideoFormat(videoMime, videoWidth,
122
- videoHeight);
123
- encoderFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
124
- MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
125
- encoderFormat.setInteger(MediaFormat.KEY_BIT_RATE, videoBitrate);
126
- encoderFormat.setInteger(MediaFormat.KEY_FRAME_RATE, videoFrameRate);
127
- encoderFormat.setInteger(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER,
128
- RecorderConstant.AUDIO_CODEC_REPEAT_PREV_FRAME_AFTER_MS);
129
- encoderFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,
130
- RecorderConstant.AUDIO_CODEC_I_FRAME_INTERVAL_MS);
131
- return encoderFormat;
132
- }
133
-
134
- @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
135
- private VirtualDisplay initVirtualDisplay(MediaProjection mediaProjection,
136
- Surface surface, Handler handler,
137
- int videoWidth, int videoHeight, int videoDpi) {
138
- return mediaProjection.createVirtualDisplay("Appium Screen Recorder",
139
- videoWidth, videoHeight, videoDpi,
140
- DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
141
- surface, displayCallback, handler);
142
- }
143
-
144
- @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
145
- private MediaCodec initAudioCodec(int sampleRate) throws IOException {
146
- // TODO set channelCount 2 try stereo quality
147
- MediaFormat encoderFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC,
148
- sampleRate, RecorderConstant.AUDIO_CODEC_CHANNEL_COUNT);
149
- encoderFormat.setInteger(MediaFormat.KEY_BIT_RATE,
150
- RecorderConstant.AUDIO_CODEC_DEFAULT_BITRATE);
151
-
152
- MediaCodec audioEncoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
153
- audioEncoder.configure(encoderFormat, null, null,
154
- MediaCodec.CONFIGURE_FLAG_ENCODE);
155
- return audioEncoder;
156
- }
157
-
158
- @RequiresApi(api = Build.VERSION_CODES.Q)
159
- private AudioRecord initAudioRecord(MediaProjection mediaProjection, int sampleRate) {
160
- int channelConfig = AudioFormat.CHANNEL_IN_MONO;
161
- int minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig,
162
- AudioFormat.ENCODING_PCM_16BIT);
163
-
164
- AudioFormat audioFormat = new AudioFormat.Builder()
165
- .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
166
- .setSampleRate(sampleRate)
167
- .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
168
- .build();
169
-
170
- AudioRecord.Builder audioRecordBuilder = new AudioRecord.Builder();
171
- AudioPlaybackCaptureConfiguration apcc =
172
- new AudioPlaybackCaptureConfiguration.Builder(mediaProjection)
173
- .addMatchingUsage(AudioAttributes.USAGE_MEDIA)
174
- .build();
175
- return audioRecordBuilder.setAudioFormat(audioFormat)
176
- .setBufferSizeInBytes(4 * minBufferSize)
177
- .setAudioPlaybackCaptureConfig(apcc)
178
- .build();
179
- }
180
-
181
- @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
182
- private Thread initAudioRecordThread(MediaCodec audioEncoder, final AudioRecord audioRecord,
183
- int priority) {
184
- return new Thread(new Runnable() {
185
- @Override
186
- public void run() {
187
- Thread.currentThread().setPriority(priority);
188
- try {
189
- audioRecord.startRecording();
190
- } catch (Exception e) {
191
- hasAsyncError = true;
192
- e.printStackTrace();
193
- return;
194
- }
195
- try {
196
- while (!audioStopped) {
197
- int index = audioEncoder.dequeueInputBuffer(
198
- RecorderConstant.MEDIA_QUEUE_BUFFERING_DEFAULT_TIMEOUT_MS);
199
- if (index < 0) {
200
- continue;
201
- }
202
- ByteBuffer inputBuffer = audioEncoder.getInputBuffer(index);
203
- if (inputBuffer == null) {
204
- if (!stopped) {
205
- hasAsyncError = true;
206
- }
207
- return;
208
- }
209
- inputBuffer.clear();
210
- int read = audioRecord.read(inputBuffer, inputBuffer.capacity());
211
- if (read <= 0) {
212
- if (!stopped) {
213
- hasAsyncError = true;
214
- }
215
- break;
216
- }
217
- audioEncoder.queueInputBuffer(index, 0, read,
218
- getPresentationTimeUs(), 0);
219
- }
220
- } catch (Exception e) {
221
- if (!stopped) {
222
- Log.e(TAG, "Recording stopped, Audio Thread error", e);
223
- hasAsyncError = true;
224
- e.printStackTrace();
225
- }
226
- } finally {
227
- audioRecord.stop();
228
- audioRecord.release();
229
- }
230
- }
231
- });
232
- }
233
-
234
- private long getPresentationTimeUs() {
235
- if (!isStartTimestampInitialized) {
236
- startTimestampUs =
237
- System.nanoTime() / RecorderConstant.NANOSECONDS_IN_MICROSECOND;
238
- isStartTimestampInitialized = true;
239
- }
240
- return (System.nanoTime() / RecorderConstant.NANOSECONDS_IN_MICROSECOND
241
- - startTimestampUs);
242
- }
243
-
244
- private int calculateBitRate(int width, int height, int frameRate) {
245
- return (int) (RecorderConstant.BITRATE_MULTIPLIER *
246
- frameRate * width * height);
247
- }
248
-
249
- private void startMuxerIfSetUp(MediaMuxer muxer) {
250
- if (audioTrackIndex >= 0 && videoTrackIndex >= 0) {
251
- muxer.start();
252
- muxerStarted = true;
253
- }
254
- }
255
-
256
- @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
257
- private boolean writeAudioBufferToFile(MediaCodec audioEncoder, MediaMuxer muxer,
258
- MediaCodec.BufferInfo bufferInfo) {
259
- int encoderStatus;
260
-
261
- encoderStatus = audioEncoder.dequeueOutputBuffer(bufferInfo, 0);
262
- if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
263
- if (audioTrackIndex > 0) {
264
- Log.e(TAG, "Recording stopped, audioTrackIndex greater than zero");
265
- return false;
266
- }
267
- audioTrackIndex = muxer.addTrack(audioEncoder.getOutputFormat());
268
- startMuxerIfSetUp(muxer);
269
- } else if (encoderStatus < 0 && encoderStatus != MediaCodec.INFO_TRY_AGAIN_LATER) {
270
- Log.w(TAG, "Unexpected result from audio encoder.dequeueOutputBuffer: "
271
- + encoderStatus + ", however continuing recording");
272
- } else if (encoderStatus >= 0) {
273
- ByteBuffer encodedData = audioEncoder.getOutputBuffer(encoderStatus);
274
- if (encodedData == null) {
275
- Log.e(TAG, "Recording stopped, " +
276
- "Unable to retrieve output buffer of audio encoder");
277
- return false;
278
- }
279
-
280
- if (bufferInfo.presentationTimeUs > this.lastAudioTimestampUs
281
- && muxerStarted && bufferInfo.size != 0 &&
282
- (bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
283
- this.lastAudioTimestampUs = bufferInfo.presentationTimeUs;
284
- muxer.writeSampleData(audioTrackIndex, encodedData, bufferInfo);
285
- }
286
-
287
- audioEncoder.releaseOutputBuffer(encoderStatus, false);
288
-
289
- if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
290
- Log.v(TAG, "Recording stopped, audio encoder buffer reached end of stream");
291
- return false;
292
- }
293
- }
294
- return true;
295
- }
296
-
297
- @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
298
- private boolean writeVideoBufferToFile(MediaCodec videoEncoder, MediaMuxer muxer,
299
- MediaCodec.BufferInfo bufferInfo) {
300
- int encoderStatus;
301
-
302
- encoderStatus = videoEncoder.dequeueOutputBuffer(bufferInfo,
303
- RecorderConstant.MEDIA_QUEUE_BUFFERING_DEFAULT_TIMEOUT_MS);
304
- if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
305
- if (videoTrackIndex > 0) {
306
- Log.e(TAG, "Recording stopped, videoTrackIndex greater than zero");
307
- return false;
308
- }
309
- videoTrackIndex = muxer.addTrack(videoEncoder.getOutputFormat());
310
- startMuxerIfSetUp(muxer);
311
- } else if (encoderStatus < 0 && encoderStatus != MediaCodec.INFO_TRY_AGAIN_LATER) {
312
- Log.w(TAG, "Unexpected result from encoder.dequeueOutputBuffer: "
313
- + encoderStatus + ", however continuing recording");
314
- } else if (encoderStatus >= 0) {
315
- ByteBuffer encodedData = videoEncoder.getOutputBuffer(encoderStatus);
316
- if (encodedData == null) {
317
- Log.w(TAG, "Recording stopped, " +
318
- "Unable to retrieve output buffer of videoEncoder");
319
- return false;
320
- }
321
-
322
- if (bufferInfo.size != 0 &&
323
- (bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
324
- bufferInfo.presentationTimeUs = getPresentationTimeUs();
325
- muxer.writeSampleData(videoTrackIndex, encodedData, bufferInfo);
326
- }
327
-
328
- videoEncoder.releaseOutputBuffer(encoderStatus, false);
329
-
330
- if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
331
- Log.v(TAG, "Recording stopped, video encoder buffer reached end of stream");
332
- return false;
333
- }
334
- }
335
- return true;
336
- }
337
-
338
- @RequiresApi(api = Build.VERSION_CODES.Q)
339
- @Override
340
- public void run() {
341
- VirtualDisplay virtualDisplay = null;
342
- MediaCodec videoEncoder = null;
343
- MediaCodec audioEncoder = null;
344
- Surface surface = null;
345
- Thread audioRecordThread = null;
346
- MediaMuxer muxer = null;
347
- try {
348
- videoEncoder = MediaCodec.createEncoderByType(RECORDING_DEFAULT_VIDEO_MIME_TYPE);
349
-
350
- MediaCodecInfo.VideoCapabilities videoEncoderCapabilities = videoEncoder
351
- .getCodecInfo().getCapabilitiesForType(RECORDING_DEFAULT_VIDEO_MIME_TYPE)
352
- .getVideoCapabilities();
353
-
354
- int videoFrameRate = Math.min(VIDEO_CODEC_DEFAULT_FRAME_RATE,
355
- videoEncoderCapabilities.getSupportedFrameRates().getUpper());
356
-
357
- int videoBitrate = videoEncoderCapabilities.getBitrateRange()
358
- .clamp(calculateBitRate(this.videoWidth, this.videoHeight, videoFrameRate));
359
-
360
- Log.i(TAG, String.format("Recording starting with frame rate = %d FPS " +
361
- "and bitrate = %5.2f Mbps",
362
- videoFrameRate, videoBitrate / BPS_IN_MBPS));
363
-
364
- MediaFormat videoEncoderFormat =
365
- initVideoEncoderFormat(RECORDING_DEFAULT_VIDEO_MIME_TYPE,
366
- this.videoWidth, this.videoHeight, videoBitrate, videoFrameRate);
367
-
368
- videoEncoder.configure(videoEncoderFormat, null, null,
369
- MediaCodec.CONFIGURE_FLAG_ENCODE);
370
- surface = videoEncoder.createInputSurface();
371
- videoEncoder.start();
372
-
373
- Handler handler = new Handler(Looper.getMainLooper());
374
- virtualDisplay = initVirtualDisplay(this.mediaProjection, surface, handler,
375
- this.videoWidth, this.videoHeight, this.videoDpi);
376
-
377
- int sampleRate = RecorderConstant.AUDIO_CODEC_SAMPLE_RATE_HZ;
378
- audioEncoder = initAudioCodec(sampleRate);
379
- audioEncoder.start();
380
-
381
- AudioRecord audioRecord = initAudioRecord(this.mediaProjection, sampleRate);
382
-
383
- muxer = new MediaMuxer(this.outputFilePath,
384
- MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
385
-
386
- // set output file orientation info
387
- // note: this method must be run before muxer.start()
388
- muxer.setOrientationHint(recordingRotation);
389
-
390
- audioRecordThread = initAudioRecordThread(audioEncoder, audioRecord,
391
- this.recordingPriority);
392
- audioRecordThread.start();
393
-
394
- MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
395
- lastAudioTimestampUs = NO_TIMESTAMP_SET;
396
-
397
- long recordingStartTime = System.currentTimeMillis();
398
-
399
- while (!stopped && !hasAsyncError) {
400
- if (!writeAudioBufferToFile(audioEncoder, muxer, bufferInfo)) {
401
- break;
402
- }
403
-
404
- if (videoTrackIndex >= 0 && audioTrackIndex < 0) {
405
- continue; // wait for audio config before processing any video data frames
406
- }
407
-
408
- if (!writeVideoBufferToFile(videoEncoder, muxer, bufferInfo)) {
409
- break;
410
- }
411
-
412
- if ((System.currentTimeMillis() - recordingStartTime) >= this.recordingMaxDuration) {
413
- Log.v(TAG, "Recording stopped, reached maximum duration");
414
- stopped = true;
415
- }
416
- }
417
- } catch (Exception mainException) {
418
- Log.e(TAG, "run: Exception occurred during recording", mainException);
419
- } finally {
420
- if (muxer != null) {
421
- muxer.stop();
422
- muxer.release();
423
- muxer = null;
424
- }
425
-
426
- if (virtualDisplay != null) {
427
- virtualDisplay.release();
428
- virtualDisplay = null;
429
- }
430
-
431
- if (surface != null) {
432
- surface.release();
433
- surface = null;
434
- }
435
-
436
- if (videoEncoder != null) {
437
- videoEncoder.stop();
438
- videoEncoder.release();
439
- videoEncoder = null;
440
- }
441
-
442
- if (audioRecordThread != null) {
443
- audioStopped = true;
444
- try {
445
- audioRecordThread.join();
446
- audioRecordThread = null;
447
- } catch (InterruptedException e) {
448
- Log.w(TAG, "Error releasing resources, audioRecordThread: ", e);
449
- }
450
- }
451
-
452
- if (audioEncoder != null) {
453
- audioEncoder.stop();
454
- audioEncoder.release();
455
- audioEncoder = null;
456
- }
457
- }
458
- }
459
- }