diff --git a/server/src/main/java/com/genymobile/scrcpy/AndroidVersions.java b/server/src/main/java/com/genymobile/scrcpy/AndroidVersions.java
new file mode 100644
index 00000000..8acad7ee
--- /dev/null
+++ b/server/src/main/java/com/genymobile/scrcpy/AndroidVersions.java
@@ -0,0 +1,32 @@
+package com.genymobile.scrcpy;
+
+import android.os.Build;
+
+/**
+ * Android version code constants, done right.
+ *
+ * API levels
+ */
+public final class AndroidVersions {
+
+ private AndroidVersions() {
+ // not instantiable
+ }
+
+ public static final int API_20_ANDROID_4_4 = Build.VERSION_CODES.KITKAT_WATCH;
+ public static final int API_21_ANDROID_5_0 = Build.VERSION_CODES.LOLLIPOP;
+ public static final int API_22_ANDROID_5_1 = Build.VERSION_CODES.LOLLIPOP_MR1;
+ public static final int API_23_ANDROID_6_0 = Build.VERSION_CODES.M;
+ public static final int API_24_ANDROID_7_0 = Build.VERSION_CODES.N;
+ public static final int API_25_ANDROID_7_1 = Build.VERSION_CODES.N_MR1;
+ public static final int API_26_ANDROID_8_0 = Build.VERSION_CODES.O;
+ public static final int API_27_ANDROID_8_1 = Build.VERSION_CODES.O_MR1;
+ public static final int API_28_ANDROID_9 = Build.VERSION_CODES.P;
+ public static final int API_29_ANDROID_10 = Build.VERSION_CODES.Q;
+ public static final int API_30_ANDROID_11 = Build.VERSION_CODES.R;
+ public static final int API_31_ANDROID_12 = Build.VERSION_CODES.S;
+ public static final int API_32_ANDROID_12L = Build.VERSION_CODES.S_V2;
+ public static final int API_33_ANDROID_13 = Build.VERSION_CODES.TIRAMISU;
+ public static final int API_34_ANDROID_14 = Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+
+}
diff --git a/server/src/main/java/com/genymobile/scrcpy/FakeContext.java b/server/src/main/java/com/genymobile/scrcpy/FakeContext.java
index 2ea7bf4a..0b086cc5 100644
--- a/server/src/main/java/com/genymobile/scrcpy/FakeContext.java
+++ b/server/src/main/java/com/genymobile/scrcpy/FakeContext.java
@@ -4,7 +4,6 @@ import android.annotation.TargetApi;
import android.content.AttributionSource;
import android.content.Context;
import android.content.ContextWrapper;
-import android.os.Build;
import android.os.Process;
public final class FakeContext extends ContextWrapper {
@@ -32,7 +31,7 @@ public final class FakeContext extends ContextWrapper {
return PACKAGE_NAME;
}
- @TargetApi(Build.VERSION_CODES.S)
+ @TargetApi(AndroidVersions.API_31_ANDROID_12)
@Override
public AttributionSource getAttributionSource() {
AttributionSource.Builder builder = new AttributionSource.Builder(Process.SHELL_UID);
diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java
index 7817fdf5..555cf97a 100644
--- a/server/src/main/java/com/genymobile/scrcpy/Server.java
+++ b/server/src/main/java/com/genymobile/scrcpy/Server.java
@@ -121,7 +121,7 @@ public final class Server {
}
private static void scrcpy(Options options) throws IOException, ConfigurationException {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S && options.getVideoSource() == VideoSource.CAMERA) {
+ if (Build.VERSION.SDK_INT < AndroidVersions.API_31_ANDROID_12 && options.getVideoSource() == VideoSource.CAMERA) {
Ln.e("Camera mirroring is not supported before Android 12");
throw new ConfigurationException("Camera mirroring is not supported");
}
diff --git a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java
index 7de98b72..eec00a04 100644
--- a/server/src/main/java/com/genymobile/scrcpy/Workarounds.java
+++ b/server/src/main/java/com/genymobile/scrcpy/Workarounds.java
@@ -52,7 +52,7 @@ public final class Workarounds {
}
public static void apply() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ if (Build.VERSION.SDK_INT >= AndroidVersions.API_31_ANDROID_12) {
// On some Samsung devices, DisplayManagerGlobal.getDisplayInfoLocked() calls ActivityThread.currentActivityThread().getConfiguration(),
// which requires a non-null ConfigurationController.
// ConfigurationController was introduced in Android 12, so do not attempt to set it on lower versions.
@@ -155,7 +155,7 @@ public final class Workarounds {
}
}
- @TargetApi(Build.VERSION_CODES.R)
+ @TargetApi(AndroidVersions.API_30_ANDROID_11)
@SuppressLint("WrongConstant,MissingPermission")
public static AudioRecord createAudioRecord(int source, int sampleRate, int channelConfig, int channels, int channelMask, int encoding) throws
AudioCaptureException {
@@ -226,7 +226,7 @@ public final class Workarounds {
int[] session = new int[]{AudioManager.AUDIO_SESSION_ID_GENERATE};
int initResult;
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
+ if (Build.VERSION.SDK_INT < AndroidVersions.API_31_ANDROID_12) {
// private native final int native_setup(Object audiorecord_this,
// Object /*AudioAttributes*/ attributes,
// int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
@@ -252,7 +252,7 @@ public final class Workarounds {
Method getParcelMethod = attributionSourceState.getClass().getDeclaredMethod("getParcel");
Parcel attributionSourceParcel = (Parcel) getParcelMethod.invoke(attributionSourceState);
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ if (Build.VERSION.SDK_INT < AndroidVersions.API_34_ANDROID_14) {
// private native int native_setup(Object audiorecordThis,
// Object /*AudioAttributes*/ attributes,
// int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
diff --git a/server/src/main/java/com/genymobile/scrcpy/audio/AudioDirectCapture.java b/server/src/main/java/com/genymobile/scrcpy/audio/AudioDirectCapture.java
index 8d4a4c2d..5c859738 100644
--- a/server/src/main/java/com/genymobile/scrcpy/audio/AudioDirectCapture.java
+++ b/server/src/main/java/com/genymobile/scrcpy/audio/AudioDirectCapture.java
@@ -1,5 +1,6 @@
package com.genymobile.scrcpy.audio;
+import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.FakeContext;
import com.genymobile.scrcpy.Workarounds;
import com.genymobile.scrcpy.util.Ln;
@@ -45,11 +46,11 @@ public class AudioDirectCapture implements AudioCapture {
}
}
- @TargetApi(Build.VERSION_CODES.M)
+ @TargetApi(AndroidVersions.API_23_ANDROID_6_0)
@SuppressLint({"WrongConstant", "MissingPermission"})
private static AudioRecord createAudioRecord(int audioSource) {
AudioRecord.Builder builder = new AudioRecord.Builder();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ if (Build.VERSION.SDK_INT >= AndroidVersions.API_31_ANDROID_12) {
// On older APIs, Workarounds.fillAppInfo() must be called beforehand
builder.setContext(FakeContext.get());
}
@@ -117,7 +118,7 @@ public class AudioDirectCapture implements AudioCapture {
@Override
public void checkCompatibility() throws AudioCaptureException {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ if (Build.VERSION.SDK_INT < AndroidVersions.API_30_ANDROID_11) {
Ln.w("Audio disabled: it is not supported before Android 11");
throw new AudioCaptureException();
}
@@ -125,7 +126,7 @@ public class AudioDirectCapture implements AudioCapture {
@Override
public void start() throws AudioCaptureException {
- if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {
+ if (Build.VERSION.SDK_INT == AndroidVersions.API_30_ANDROID_11) {
startWorkaroundAndroid11();
try {
tryStartRecording(5, 100);
@@ -146,7 +147,7 @@ public class AudioDirectCapture implements AudioCapture {
}
@Override
- @TargetApi(Build.VERSION_CODES.N)
+ @TargetApi(AndroidVersions.API_24_ANDROID_7_0)
public int read(ByteBuffer outDirectBuffer, MediaCodec.BufferInfo outBufferInfo) {
return reader.read(outDirectBuffer, outBufferInfo);
}
diff --git a/server/src/main/java/com/genymobile/scrcpy/audio/AudioEncoder.java b/server/src/main/java/com/genymobile/scrcpy/audio/AudioEncoder.java
index f462431a..fcc0c52f 100644
--- a/server/src/main/java/com/genymobile/scrcpy/audio/AudioEncoder.java
+++ b/server/src/main/java/com/genymobile/scrcpy/audio/AudioEncoder.java
@@ -1,5 +1,6 @@
package com.genymobile.scrcpy.audio;
+import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.AsyncProcessor;
import com.genymobile.scrcpy.device.ConfigurationException;
import com.genymobile.scrcpy.device.Streamer;
@@ -93,7 +94,7 @@ public final class AudioEncoder implements AsyncProcessor {
return format;
}
- @TargetApi(Build.VERSION_CODES.N)
+ @TargetApi(AndroidVersions.API_24_ANDROID_7_0)
private void inputThread(MediaCodec mediaCodec, AudioCapture capture) throws IOException, InterruptedException {
final MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
@@ -175,9 +176,9 @@ public final class AudioEncoder implements AsyncProcessor {
}
}
- @TargetApi(Build.VERSION_CODES.M)
+ @TargetApi(AndroidVersions.API_23_ANDROID_6_0)
private void encode() throws IOException, ConfigurationException, AudioCaptureException {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ if (Build.VERSION.SDK_INT < AndroidVersions.API_30_ANDROID_11) {
Ln.w("Audio disabled: it is not supported before Android 11");
streamer.writeDisableStream(false);
return;
@@ -314,7 +315,7 @@ public final class AudioEncoder implements AsyncProcessor {
}
private final class EncoderCallback extends MediaCodec.Callback {
- @TargetApi(Build.VERSION_CODES.N)
+ @TargetApi(AndroidVersions.API_24_ANDROID_7_0)
@Override
public void onInputBufferAvailable(MediaCodec codec, int index) {
try {
diff --git a/server/src/main/java/com/genymobile/scrcpy/audio/AudioPlaybackCapture.java b/server/src/main/java/com/genymobile/scrcpy/audio/AudioPlaybackCapture.java
index e38493f2..009a239a 100644
--- a/server/src/main/java/com/genymobile/scrcpy/audio/AudioPlaybackCapture.java
+++ b/server/src/main/java/com/genymobile/scrcpy/audio/AudioPlaybackCapture.java
@@ -1,5 +1,6 @@
package com.genymobile.scrcpy.audio;
+import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.FakeContext;
import com.genymobile.scrcpy.util.Ln;
@@ -108,7 +109,7 @@ public final class AudioPlaybackCapture implements AudioCapture {
@Override
public void checkCompatibility() throws AudioCaptureException {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+ if (Build.VERSION.SDK_INT < AndroidVersions.API_33_ANDROID_13) {
Ln.w("Audio disabled: audio playback capture source not supported before Android 13");
throw new AudioCaptureException();
}
@@ -130,7 +131,7 @@ public final class AudioPlaybackCapture implements AudioCapture {
}
@Override
- @TargetApi(Build.VERSION_CODES.N)
+ @TargetApi(AndroidVersions.API_24_ANDROID_7_0)
public int read(ByteBuffer outDirectBuffer, MediaCodec.BufferInfo outBufferInfo) {
return reader.read(outDirectBuffer, outBufferInfo);
}
diff --git a/server/src/main/java/com/genymobile/scrcpy/audio/AudioRawRecorder.java b/server/src/main/java/com/genymobile/scrcpy/audio/AudioRawRecorder.java
index 3924c205..9645bbbd 100644
--- a/server/src/main/java/com/genymobile/scrcpy/audio/AudioRawRecorder.java
+++ b/server/src/main/java/com/genymobile/scrcpy/audio/AudioRawRecorder.java
@@ -1,5 +1,6 @@
package com.genymobile.scrcpy.audio;
+import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.AsyncProcessor;
import com.genymobile.scrcpy.device.Streamer;
import com.genymobile.scrcpy.util.IO;
@@ -24,7 +25,7 @@ public final class AudioRawRecorder implements AsyncProcessor {
}
private void record() throws IOException, AudioCaptureException {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ if (Build.VERSION.SDK_INT < AndroidVersions.API_30_ANDROID_11) {
Ln.w("Audio disabled: it is not supported before Android 11");
streamer.writeDisableStream(false);
return;
diff --git a/server/src/main/java/com/genymobile/scrcpy/audio/AudioRecordReader.java b/server/src/main/java/com/genymobile/scrcpy/audio/AudioRecordReader.java
index 80286831..32b42257 100644
--- a/server/src/main/java/com/genymobile/scrcpy/audio/AudioRecordReader.java
+++ b/server/src/main/java/com/genymobile/scrcpy/audio/AudioRecordReader.java
@@ -1,12 +1,12 @@
package com.genymobile.scrcpy.audio;
+import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.util.Ln;
import android.annotation.TargetApi;
import android.media.AudioRecord;
import android.media.AudioTimestamp;
import android.media.MediaCodec;
-import android.os.Build;
import java.nio.ByteBuffer;
@@ -26,7 +26,7 @@ public class AudioRecordReader {
this.recorder = recorder;
}
- @TargetApi(Build.VERSION_CODES.N)
+ @TargetApi(AndroidVersions.API_24_ANDROID_7_0)
public int read(ByteBuffer outDirectBuffer, MediaCodec.BufferInfo outBufferInfo) {
int r = recorder.read(outDirectBuffer, AudioConfig.MAX_READ_SIZE);
if (r <= 0) {
diff --git a/server/src/main/java/com/genymobile/scrcpy/control/Controller.java b/server/src/main/java/com/genymobile/scrcpy/control/Controller.java
index b445427d..8fa27e81 100644
--- a/server/src/main/java/com/genymobile/scrcpy/control/Controller.java
+++ b/server/src/main/java/com/genymobile/scrcpy/control/Controller.java
@@ -1,5 +1,6 @@
package com.genymobile.scrcpy.control;
+import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.AsyncProcessor;
import com.genymobile.scrcpy.CleanUp;
import com.genymobile.scrcpy.device.Device;
@@ -318,7 +319,7 @@ public class Controller implements AsyncProcessor {
*
* Otherwise, Chrome does not work properly:
*/
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && source == InputDevice.SOURCE_MOUSE) {
+ if (Build.VERSION.SDK_INT >= AndroidVersions.API_23_ANDROID_6_0 && source == InputDevice.SOURCE_MOUSE) {
if (action == MotionEvent.ACTION_DOWN) {
if (actionButton == buttons) {
// First button pressed: ACTION_DOWN
@@ -423,7 +424,7 @@ public class Controller implements AsyncProcessor {
private void getClipboard(int copyKey) {
// On Android >= 7, press the COPY or CUT key if requested
- if (copyKey != ControlMessage.COPY_KEY_NONE && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && device.supportsInputEvents()) {
+ if (copyKey != ControlMessage.COPY_KEY_NONE && Build.VERSION.SDK_INT >= AndroidVersions.API_24_ANDROID_7_0 && device.supportsInputEvents()) {
int key = copyKey == ControlMessage.COPY_KEY_COPY ? KeyEvent.KEYCODE_COPY : KeyEvent.KEYCODE_CUT;
// Wait until the event is finished, to ensure that the clipboard text we read just after is the correct one
device.pressReleaseKeycode(key, Device.INJECT_MODE_WAIT_FOR_FINISH);
@@ -448,7 +449,7 @@ public class Controller implements AsyncProcessor {
}
// On Android >= 7, also press the PASTE key if requested
- if (paste && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && device.supportsInputEvents()) {
+ if (paste && Build.VERSION.SDK_INT >= AndroidVersions.API_24_ANDROID_7_0 && device.supportsInputEvents()) {
device.pressReleaseKeycode(KeyEvent.KEYCODE_PASTE, Device.INJECT_MODE_ASYNC);
}
diff --git a/server/src/main/java/com/genymobile/scrcpy/control/UhidManager.java b/server/src/main/java/com/genymobile/scrcpy/control/UhidManager.java
index d8cfd81f..8121adfc 100644
--- a/server/src/main/java/com/genymobile/scrcpy/control/UhidManager.java
+++ b/server/src/main/java/com/genymobile/scrcpy/control/UhidManager.java
@@ -1,5 +1,6 @@
package com.genymobile.scrcpy.control;
+import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.util.Ln;
import com.genymobile.scrcpy.util.StringUtils;
@@ -38,7 +39,7 @@ public final class UhidManager {
public UhidManager(DeviceMessageSender sender) {
this.sender = sender;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ if (Build.VERSION.SDK_INT >= AndroidVersions.API_23_ANDROID_6_0) {
HandlerThread thread = new HandlerThread("UHidManager");
thread.start();
queue = thread.getLooper().getQueue();
@@ -71,7 +72,7 @@ public final class UhidManager {
}
private void registerUhidListener(int id, FileDescriptor fd) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ if (Build.VERSION.SDK_INT >= AndroidVersions.API_23_ANDROID_6_0) {
queue.addOnFileDescriptorEventListener(fd, MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT, (fd2, events) -> {
try {
buffer.clear();
@@ -97,7 +98,7 @@ public final class UhidManager {
}
private void unregisterUhidListener(FileDescriptor fd) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ if (Build.VERSION.SDK_INT >= AndroidVersions.API_23_ANDROID_6_0) {
queue.removeOnFileDescriptorEventListener(fd);
}
}
diff --git a/server/src/main/java/com/genymobile/scrcpy/device/Device.java b/server/src/main/java/com/genymobile/scrcpy/device/Device.java
index 5a1083fd..1f375942 100644
--- a/server/src/main/java/com/genymobile/scrcpy/device/Device.java
+++ b/server/src/main/java/com/genymobile/scrcpy/device/Device.java
@@ -1,5 +1,6 @@
package com.genymobile.scrcpy.device;
+import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.Options;
import com.genymobile.scrcpy.util.Ln;
import com.genymobile.scrcpy.util.LogUtils;
@@ -104,7 +105,7 @@ public final class Device {
}
}, displayId);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ if (Build.VERSION.SDK_INT >= AndroidVersions.API_29_ANDROID_10) {
ServiceManager.getWindowManager().registerDisplayFoldListener(new IDisplayFoldListener.Stub() {
@Override
public void onDisplayFoldChanged(int displayId, boolean folded) {
@@ -161,8 +162,8 @@ public final class Device {
Ln.w("Display doesn't have FLAG_SUPPORTS_PROTECTED_BUFFERS flag, mirroring can be restricted");
}
- // main display or any display on Android >= Q
- supportsInputEvents = displayId == 0 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
+ // main display or any display on Android >= 10
+ supportsInputEvents = displayId == 0 || Build.VERSION.SDK_INT >= AndroidVersions.API_29_ANDROID_10;
if (!supportsInputEvents) {
Ln.w("Input events are not supported for secondary displays before Android 10");
}
@@ -215,7 +216,7 @@ public final class Device {
}
public static boolean supportsInputEvents(int displayId) {
- return displayId == 0 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
+ return displayId == 0 || Build.VERSION.SDK_INT >= AndroidVersions.API_29_ANDROID_10;
}
public boolean supportsInputEvents() {
@@ -323,10 +324,10 @@ public final class Device {
* @param mode one of the {@code POWER_MODE_*} constants
*/
public static boolean setScreenPowerMode(int mode) {
- boolean applyToMultiPhysicalDisplays = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
+ boolean applyToMultiPhysicalDisplays = Build.VERSION.SDK_INT >= AndroidVersions.API_29_ANDROID_10;
if (applyToMultiPhysicalDisplays
- && Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+ && Build.VERSION.SDK_INT >= AndroidVersions.API_34_ANDROID_14
&& Build.BRAND.equalsIgnoreCase("honor")
&& SurfaceControl.hasGetBuildInDisplayMethod()) {
// Workaround for Honor devices with Android 14:
@@ -338,7 +339,7 @@ public final class Device {
if (applyToMultiPhysicalDisplays) {
// On Android 14, these internal methods have been moved to DisplayControl
boolean useDisplayControl =
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && !SurfaceControl.hasGetPhysicalDisplayIdsMethod();
+ Build.VERSION.SDK_INT >= AndroidVersions.API_34_ANDROID_14 && !SurfaceControl.hasGetPhysicalDisplayIdsMethod();
// Change the power mode for all physical displays
long[] physicalDisplayIds = useDisplayControl ? DisplayControl.getPhysicalDisplayIds() : SurfaceControl.getPhysicalDisplayIds();
diff --git a/server/src/main/java/com/genymobile/scrcpy/util/IO.java b/server/src/main/java/com/genymobile/scrcpy/util/IO.java
index d9247a98..b953f290 100644
--- a/server/src/main/java/com/genymobile/scrcpy/util/IO.java
+++ b/server/src/main/java/com/genymobile/scrcpy/util/IO.java
@@ -1,5 +1,6 @@
package com.genymobile.scrcpy.util;
+import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.BuildConfig;
import android.os.Build;
@@ -31,7 +32,7 @@ public final class IO {
}
public static void writeFully(FileDescriptor fd, ByteBuffer from) throws IOException {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ if (Build.VERSION.SDK_INT >= AndroidVersions.API_23_ANDROID_6_0) {
while (from.hasRemaining()) {
write(fd, from);
}
diff --git a/server/src/main/java/com/genymobile/scrcpy/util/Settings.java b/server/src/main/java/com/genymobile/scrcpy/util/Settings.java
index d9e82d62..e6465525 100644
--- a/server/src/main/java/com/genymobile/scrcpy/util/Settings.java
+++ b/server/src/main/java/com/genymobile/scrcpy/util/Settings.java
@@ -1,5 +1,6 @@
package com.genymobile.scrcpy.util;
+import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.wrappers.ContentProvider;
import com.genymobile.scrcpy.wrappers.ServiceManager;
@@ -34,7 +35,7 @@ public final class Settings {
}
public static String getValue(String table, String key) throws SettingsException {
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
+ if (Build.VERSION.SDK_INT <= AndroidVersions.API_30_ANDROID_11) {
// on Android >= 12, it always fails:
try (ContentProvider provider = ServiceManager.getActivityManager().createSettingsProvider()) {
return provider.getValue(table, key);
@@ -47,7 +48,7 @@ public final class Settings {
}
public static void putValue(String table, String key, String value) throws SettingsException {
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
+ if (Build.VERSION.SDK_INT <= AndroidVersions.API_30_ANDROID_11) {
// on Android >= 12, it always fails:
try (ContentProvider provider = ServiceManager.getActivityManager().createSettingsProvider()) {
provider.putValue(table, key, value);
@@ -60,7 +61,7 @@ public final class Settings {
}
public static String getAndPutValue(String table, String key, String value) throws SettingsException {
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
+ if (Build.VERSION.SDK_INT <= AndroidVersions.API_30_ANDROID_11) {
// on Android >= 12, it always fails:
try (ContentProvider provider = ServiceManager.getActivityManager().createSettingsProvider()) {
String oldValue = provider.getValue(table, key);
diff --git a/server/src/main/java/com/genymobile/scrcpy/video/CameraCapture.java b/server/src/main/java/com/genymobile/scrcpy/video/CameraCapture.java
index 3b8fc59b..a5fa4b06 100644
--- a/server/src/main/java/com/genymobile/scrcpy/video/CameraCapture.java
+++ b/server/src/main/java/com/genymobile/scrcpy/video/CameraCapture.java
@@ -1,5 +1,6 @@
package com.genymobile.scrcpy.video;
+import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.device.Size;
import com.genymobile.scrcpy.util.HandlerExecutor;
import com.genymobile.scrcpy.util.Ln;
@@ -20,7 +21,6 @@ import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.MediaCodec;
-import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Range;
@@ -118,7 +118,7 @@ public class CameraCapture extends SurfaceCapture {
return null;
}
- @TargetApi(Build.VERSION_CODES.N)
+ @TargetApi(AndroidVersions.API_24_ANDROID_7_0)
private static Size selectSize(String cameraId, Size explicitSize, int maxSize, CameraAspectRatio aspectRatio, boolean highSpeed)
throws CameraAccessException {
if (explicitSize != null) {
@@ -242,7 +242,7 @@ public class CameraCapture extends SurfaceCapture {
}
@SuppressLint("MissingPermission")
- @TargetApi(Build.VERSION_CODES.S)
+ @TargetApi(AndroidVersions.API_31_ANDROID_12)
private CameraDevice openCamera(String id) throws CameraAccessException, InterruptedException {
CompletableFuture future = new CompletableFuture<>();
ServiceManager.getCameraManager().openCamera(id, new CameraDevice.StateCallback() {
@@ -289,7 +289,7 @@ public class CameraCapture extends SurfaceCapture {
}
}
- @TargetApi(Build.VERSION_CODES.S)
+ @TargetApi(AndroidVersions.API_31_ANDROID_12)
private CameraCaptureSession createCaptureSession(CameraDevice camera, Surface surface) throws CameraAccessException, InterruptedException {
CompletableFuture future = new CompletableFuture<>();
OutputConfiguration outputConfig = new OutputConfiguration(surface);
@@ -328,7 +328,7 @@ public class CameraCapture extends SurfaceCapture {
return requestBuilder.build();
}
- @TargetApi(Build.VERSION_CODES.S)
+ @TargetApi(AndroidVersions.API_31_ANDROID_12)
private void setRepeatingRequest(CameraCaptureSession session, CaptureRequest request) throws CameraAccessException, InterruptedException {
CameraCaptureSession.CaptureCallback callback = new CameraCaptureSession.CaptureCallback() {
@Override
diff --git a/server/src/main/java/com/genymobile/scrcpy/video/ScreenCapture.java b/server/src/main/java/com/genymobile/scrcpy/video/ScreenCapture.java
index 62afb263..e6357410 100644
--- a/server/src/main/java/com/genymobile/scrcpy/video/ScreenCapture.java
+++ b/server/src/main/java/com/genymobile/scrcpy/video/ScreenCapture.java
@@ -1,5 +1,6 @@
package com.genymobile.scrcpy.video;
+import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.device.Device;
import com.genymobile.scrcpy.device.Size;
import com.genymobile.scrcpy.util.Ln;
@@ -103,8 +104,8 @@ public class ScreenCapture extends SurfaceCapture implements Device.RotationList
private static IBinder createDisplay() throws Exception {
// Since Android 12 (preview), secure displays could not be created with shell permissions anymore.
// On Android 12 preview, SDK_INT is still R (not S), but CODENAME is "S".
- boolean secure = Build.VERSION.SDK_INT < Build.VERSION_CODES.R || (Build.VERSION.SDK_INT == Build.VERSION_CODES.R && !"S".equals(
- Build.VERSION.CODENAME));
+ boolean secure = Build.VERSION.SDK_INT < AndroidVersions.API_30_ANDROID_11 || (Build.VERSION.SDK_INT == AndroidVersions.API_30_ANDROID_11
+ && !"S".equals(Build.VERSION.CODENAME));
return SurfaceControl.createDisplay("scrcpy", secure);
}
diff --git a/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java b/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java
index 41c38642..5a9417da 100644
--- a/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java
+++ b/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java
@@ -1,5 +1,6 @@
package com.genymobile.scrcpy.video;
+import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.AsyncProcessor;
import com.genymobile.scrcpy.device.ConfigurationException;
import com.genymobile.scrcpy.device.Size;
@@ -238,7 +239,7 @@ public class SurfaceEncoder implements AsyncProcessor {
// must be present to configure the encoder, but does not impact the actual frame rate, which is variable
format.setInteger(MediaFormat.KEY_FRAME_RATE, 60);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ if (Build.VERSION.SDK_INT >= AndroidVersions.API_24_ANDROID_7_0) {
format.setInteger(MediaFormat.KEY_COLOR_RANGE, MediaFormat.COLOR_RANGE_LIMITED);
}
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, DEFAULT_I_FRAME_INTERVAL);
diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/ActivityManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/ActivityManager.java
index bb1ca0d4..c907e12f 100644
--- a/server/src/main/java/com/genymobile/scrcpy/wrappers/ActivityManager.java
+++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/ActivityManager.java
@@ -1,5 +1,6 @@
package com.genymobile.scrcpy.wrappers;
+import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.FakeContext;
import com.genymobile.scrcpy.util.Ln;
@@ -7,7 +8,6 @@ import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Intent;
import android.os.Binder;
-import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IInterface;
@@ -63,7 +63,7 @@ public final class ActivityManager {
return removeContentProviderExternalMethod;
}
- @TargetApi(Build.VERSION_CODES.Q)
+ @TargetApi(AndroidVersions.API_29_ANDROID_10)
private ContentProvider getContentProviderExternal(String name, IBinder token) {
try {
Method method = getGetContentProviderExternalMethod();
diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java
index c5f007fe..791df0f8 100644
--- a/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java
+++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java
@@ -1,5 +1,6 @@
package com.genymobile.scrcpy.wrappers;
+import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.FakeContext;
import com.genymobile.scrcpy.util.Ln;
@@ -36,7 +37,7 @@ public final class ClipboardManager {
private Method getGetPrimaryClipMethod() throws NoSuchMethodException {
if (getPrimaryClipMethod == null) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ if (Build.VERSION.SDK_INT < AndroidVersions.API_29_ANDROID_10) {
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class);
return getPrimaryClipMethod;
}
@@ -99,7 +100,7 @@ public final class ClipboardManager {
private Method getSetPrimaryClipMethod() throws NoSuchMethodException {
if (setPrimaryClipMethod == null) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ if (Build.VERSION.SDK_INT < AndroidVersions.API_29_ANDROID_10) {
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class);
return setPrimaryClipMethod;
}
@@ -137,7 +138,7 @@ public final class ClipboardManager {
}
private static ClipData getPrimaryClip(Method method, int methodVersion, IInterface manager) throws ReflectiveOperationException {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ if (Build.VERSION.SDK_INT < AndroidVersions.API_29_ANDROID_10) {
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME);
}
@@ -161,7 +162,7 @@ public final class ClipboardManager {
}
private static void setPrimaryClip(Method method, int methodVersion, IInterface manager, ClipData clipData) throws ReflectiveOperationException {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ if (Build.VERSION.SDK_INT < AndroidVersions.API_29_ANDROID_10) {
method.invoke(manager, clipData, FakeContext.PACKAGE_NAME);
return;
}
@@ -210,7 +211,7 @@ public final class ClipboardManager {
private static void addPrimaryClipChangedListener(Method method, int methodVersion, IInterface manager, IOnPrimaryClipChangedListener listener)
throws ReflectiveOperationException {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ if (Build.VERSION.SDK_INT < AndroidVersions.API_29_ANDROID_10) {
method.invoke(manager, listener, FakeContext.PACKAGE_NAME);
return;
}
@@ -230,7 +231,7 @@ public final class ClipboardManager {
private Method getAddPrimaryClipChangedListener() throws NoSuchMethodException {
if (addPrimaryClipChangedListener == null) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ if (Build.VERSION.SDK_INT < AndroidVersions.API_29_ANDROID_10) {
addPrimaryClipChangedListener = manager.getClass()
.getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class);
} else {
diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/ContentProvider.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/ContentProvider.java
index 7e92ac50..f625b398 100644
--- a/server/src/main/java/com/genymobile/scrcpy/wrappers/ContentProvider.java
+++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/ContentProvider.java
@@ -1,5 +1,6 @@
package com.genymobile.scrcpy.wrappers;
+import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.FakeContext;
import com.genymobile.scrcpy.util.Ln;
import com.genymobile.scrcpy.util.SettingsException;
@@ -51,7 +52,7 @@ public final class ContentProvider implements Closeable {
@SuppressLint("PrivateApi")
private Method getCallMethod() throws NoSuchMethodException {
if (callMethod == null) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ if (Build.VERSION.SDK_INT >= AndroidVersions.API_31_ANDROID_12) {
callMethod = provider.getClass().getMethod("call", AttributionSource.class, String.class, String.class, String.class, Bundle.class);
callMethodVersion = 0;
} else {
@@ -79,7 +80,7 @@ public final class ContentProvider implements Closeable {
Method method = getCallMethod();
Object[] args;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && callMethodVersion == 0) {
+ if (Build.VERSION.SDK_INT >= AndroidVersions.API_31_ANDROID_12 && callMethodVersion == 0) {
args = new Object[]{FakeContext.get().getAttributionSource(), "settings", callMethod, arg, extras};
} else {
switch (callMethodVersion) {
diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayControl.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayControl.java
index cc9d5526..a57f7948 100644
--- a/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayControl.java
+++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayControl.java
@@ -1,16 +1,16 @@
package com.genymobile.scrcpy.wrappers;
+import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.util.Ln;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
-import android.os.Build;
import android.os.IBinder;
import java.lang.reflect.Method;
@SuppressLint({"PrivateApi", "SoonBlockedPrivateApi", "BlockedPrivateApi"})
-@TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+@TargetApi(AndroidVersions.API_34_ANDROID_14)
public final class DisplayControl {
private static final Class> CLASS;
diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/PowerManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/PowerManager.java
index 0a56f347..615ceb42 100644
--- a/server/src/main/java/com/genymobile/scrcpy/wrappers/PowerManager.java
+++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/PowerManager.java
@@ -1,5 +1,6 @@
package com.genymobile.scrcpy.wrappers;
+import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.util.Ln;
import android.annotation.SuppressLint;
@@ -24,7 +25,7 @@ public final class PowerManager {
private Method getIsScreenOnMethod() throws NoSuchMethodException {
if (isScreenOnMethod == null) {
@SuppressLint("ObsoleteSdkInt") // we may lower minSdkVersion in the future
- String methodName = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH ? "isInteractive" : "isScreenOn";
+ String methodName = Build.VERSION.SDK_INT >= AndroidVersions.API_20_ANDROID_4_4 ? "isInteractive" : "isScreenOn";
isScreenOnMethod = manager.getClass().getMethod(methodName);
}
return isScreenOnMethod;
diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/SurfaceControl.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/SurfaceControl.java
index 038e7ca0..3bae4a37 100644
--- a/server/src/main/java/com/genymobile/scrcpy/wrappers/SurfaceControl.java
+++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/SurfaceControl.java
@@ -1,5 +1,6 @@
package com.genymobile.scrcpy.wrappers;
+import com.genymobile.scrcpy.AndroidVersions;
import com.genymobile.scrcpy.util.Ln;
import android.annotation.SuppressLint;
@@ -83,9 +84,9 @@ public final class SurfaceControl {
private static Method getGetBuiltInDisplayMethod() throws NoSuchMethodException {
if (getBuiltInDisplayMethod == null) {
- // the method signature has changed in Android Q
+ // the method signature has changed in Android 10
//
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ if (Build.VERSION.SDK_INT < AndroidVersions.API_29_ANDROID_10) {
getBuiltInDisplayMethod = CLASS.getMethod("getBuiltInDisplay", int.class);
} else {
getBuiltInDisplayMethod = CLASS.getMethod("getInternalDisplayToken");
@@ -106,7 +107,7 @@ public final class SurfaceControl {
public static IBinder getBuiltInDisplay() {
try {
Method method = getGetBuiltInDisplayMethod();
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ if (Build.VERSION.SDK_INT < AndroidVersions.API_29_ANDROID_10) {
// call getBuiltInDisplay(0)
return (IBinder) method.invoke(null, 0);
}