Compare commits
7 Commits
reschange.
...
issue4492
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9f1478af6 | ||
|
|
3001f8a2d5 | ||
|
|
c6ff78f414 | ||
|
|
40f2560d98 | ||
|
|
26aa28c998 | ||
|
|
ef79fcbbd2 | ||
|
|
bd9292931e |
@@ -1,4 +1,4 @@
|
|||||||
# scrcpy (v2.3)
|
# scrcpy (v2.3.1)
|
||||||
|
|
||||||
<img src="app/data/icon.svg" width="128" height="128" alt="scrcpy" align="right" />
|
<img src="app/data/icon.svg" width="128" height="128" alt="scrcpy" align="right" />
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ BEGIN
|
|||||||
VALUE "LegalCopyright", "Romain Vimont, Genymobile"
|
VALUE "LegalCopyright", "Romain Vimont, Genymobile"
|
||||||
VALUE "OriginalFilename", "scrcpy.exe"
|
VALUE "OriginalFilename", "scrcpy.exe"
|
||||||
VALUE "ProductName", "scrcpy"
|
VALUE "ProductName", "scrcpy"
|
||||||
VALUE "ProductVersion", "2.3"
|
VALUE "ProductVersion", "2.3.1"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|||||||
@@ -227,8 +227,9 @@ run_demuxer(void *data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Config packets must be merged with the next non-config packet only for
|
// Config packets must be merged with the next non-config packet only for
|
||||||
// video streams
|
// H.26x
|
||||||
bool must_merge_config_packet = codec->type == AVMEDIA_TYPE_VIDEO;
|
bool must_merge_config_packet = raw_codec_id == SC_CODEC_ID_H264
|
||||||
|
|| raw_codec_id == SC_CODEC_ID_H265;
|
||||||
|
|
||||||
struct sc_packet_merger merger;
|
struct sc_packet_merger merger;
|
||||||
|
|
||||||
|
|||||||
@@ -233,10 +233,10 @@ install` must be run as root)._
|
|||||||
|
|
||||||
#### Option 2: Use prebuilt server
|
#### Option 2: Use prebuilt server
|
||||||
|
|
||||||
- [`scrcpy-server-v2.3`][direct-scrcpy-server]
|
- [`scrcpy-server-v2.3.1`][direct-scrcpy-server]
|
||||||
<sub>SHA-256: `8daed514d7796fca6987dc973e201bd15ba51d0f7258973dec92d9ded00dbd5f`</sub>
|
<sub>SHA-256: `f6814822fc308a7a532f253485c9038183c6296a6c5df470a9e383b4f8e7605b`</sub>
|
||||||
|
|
||||||
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v2.3/scrcpy-server-v2.3
|
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v2.3.1/scrcpy-server-v2.3.1
|
||||||
|
|
||||||
Download the prebuilt server somewhere, and specify its path during the Meson
|
Download the prebuilt server somewhere, and specify its path during the Meson
|
||||||
configuration:
|
configuration:
|
||||||
|
|||||||
@@ -21,6 +21,13 @@ This will create a new video device in `/dev/videoN`, where `N` is an integer
|
|||||||
(more [options](https://github.com/umlaeute/v4l2loopback#options) are available
|
(more [options](https://github.com/umlaeute/v4l2loopback#options) are available
|
||||||
to create several devices or devices with specific IDs).
|
to create several devices or devices with specific IDs).
|
||||||
|
|
||||||
|
If you encounter problems detecting your device with Chrome/WebRTC, you can try
|
||||||
|
`exclusive_caps` mode:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo modprobe v4l2loopback exclusive_caps=1
|
||||||
|
```
|
||||||
|
|
||||||
To list the enabled devices:
|
To list the enabled devices:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -4,14 +4,14 @@
|
|||||||
|
|
||||||
Download the [latest release]:
|
Download the [latest release]:
|
||||||
|
|
||||||
- [`scrcpy-win64-v2.3.zip`][direct-win64] (64-bit)
|
- [`scrcpy-win64-v2.3.1.zip`][direct-win64] (64-bit)
|
||||||
<sub>SHA-256: `a2fdd2733bd337261bb493e77d990078a23e7a40149dd0c0dc45725c929a715f`</sub>
|
<sub>SHA-256: `f1f78ac98214078425804e524a1bed515b9d4b8a05b78d210a4ced2b910b262d`</sub>
|
||||||
- [`scrcpy-win32-v2.3.zip`][direct-win32] (32-bit)
|
- [`scrcpy-win32-v2.3.1.zip`][direct-win32] (32-bit)
|
||||||
<sub>SHA-256: `dfdbb69a872d717aed5bcfe352e571564c357fdb7a9c172d69f450fdf5154a0a`</sub>
|
<sub>SHA-256: `5dffc2d432e9b8b5b0e16f12e71428c37c70d9124cfbe7620df0b41b7efe91ff`</sub>
|
||||||
|
|
||||||
[latest release]: https://github.com/Genymobile/scrcpy/releases/latest
|
[latest release]: https://github.com/Genymobile/scrcpy/releases/latest
|
||||||
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v2.3/scrcpy-win64-v2.3.zip
|
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v2.3.1/scrcpy-win64-v2.3.1.zip
|
||||||
[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v2.3/scrcpy-win32-v2.3.zip
|
[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v2.3.1/scrcpy-win32-v2.3.1.zip
|
||||||
|
|
||||||
and extract it.
|
and extract it.
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
BUILDDIR=build-auto
|
BUILDDIR=build-auto
|
||||||
PREBUILT_SERVER_URL=https://github.com/Genymobile/scrcpy/releases/download/v2.3/scrcpy-server-v2.3
|
PREBUILT_SERVER_URL=https://github.com/Genymobile/scrcpy/releases/download/v2.3.1/scrcpy-server-v2.3.1
|
||||||
PREBUILT_SERVER_SHA256=8daed514d7796fca6987dc973e201bd15ba51d0f7258973dec92d9ded00dbd5f
|
PREBUILT_SERVER_SHA256=f6814822fc308a7a532f253485c9038183c6296a6c5df470a9e383b4f8e7605b
|
||||||
|
|
||||||
echo "[scrcpy] Downloading prebuilt server..."
|
echo "[scrcpy] Downloading prebuilt server..."
|
||||||
wget "$PREBUILT_SERVER_URL" -O scrcpy-server
|
wget "$PREBUILT_SERVER_URL" -O scrcpy-server
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
project('scrcpy', 'c',
|
project('scrcpy', 'c',
|
||||||
version: '2.3',
|
version: '2.3.1',
|
||||||
meson_version: '>= 0.48',
|
meson_version: '>= 0.48',
|
||||||
default_options: [
|
default_options: [
|
||||||
'c_std=c11',
|
'c_std=c11',
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ android {
|
|||||||
applicationId "com.genymobile.scrcpy"
|
applicationId "com.genymobile.scrcpy"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 34
|
targetSdkVersion 34
|
||||||
versionCode 20300
|
versionCode 20301
|
||||||
versionName "2.3"
|
versionName "2.3.1"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
SCRCPY_DEBUG=false
|
SCRCPY_DEBUG=false
|
||||||
SCRCPY_VERSION_NAME=2.3
|
SCRCPY_VERSION_NAME=2.3.1
|
||||||
|
|
||||||
PLATFORM=${ANDROID_PLATFORM:-34}
|
PLATFORM=${ANDROID_PLATFORM:-34}
|
||||||
BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-34.0.0}
|
BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-34.0.0}
|
||||||
|
|||||||
26
server/src/main/aidl/android/view/IDisplayFoldListener.aidl
Normal file
26
server/src/main/aidl/android/view/IDisplayFoldListener.aidl
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.view;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@hide}
|
||||||
|
*/
|
||||||
|
oneway interface IDisplayFoldListener
|
||||||
|
{
|
||||||
|
/** Called when the foldedness of a display changes */
|
||||||
|
void onDisplayFoldChanged(int displayId, boolean folded);
|
||||||
|
}
|
||||||
25
server/src/main/aidl/android/view/IRotationWatcher.aidl
Normal file
25
server/src/main/aidl/android/view/IRotationWatcher.aidl
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/* //device/java/android/android/hardware/ISensorListener.aidl
|
||||||
|
**
|
||||||
|
** Copyright 2008, The Android Open Source Project
|
||||||
|
**
|
||||||
|
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
** you may not use this file except in compliance with the License.
|
||||||
|
** You may obtain a copy of the License at
|
||||||
|
**
|
||||||
|
** http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
**
|
||||||
|
** Unless required by applicable law or agreed to in writing, software
|
||||||
|
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
** See the License for the specific language governing permissions and
|
||||||
|
** limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.view;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@hide}
|
||||||
|
*/
|
||||||
|
interface IRotationWatcher {
|
||||||
|
oneway void onRotationChanged(int rotation);
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@ package com.genymobile.scrcpy;
|
|||||||
|
|
||||||
import com.genymobile.scrcpy.wrappers.ClipboardManager;
|
import com.genymobile.scrcpy.wrappers.ClipboardManager;
|
||||||
import com.genymobile.scrcpy.wrappers.DisplayControl;
|
import com.genymobile.scrcpy.wrappers.DisplayControl;
|
||||||
import com.genymobile.scrcpy.wrappers.DisplayManager;
|
|
||||||
import com.genymobile.scrcpy.wrappers.InputManager;
|
import com.genymobile.scrcpy.wrappers.InputManager;
|
||||||
import com.genymobile.scrcpy.wrappers.ServiceManager;
|
import com.genymobile.scrcpy.wrappers.ServiceManager;
|
||||||
import com.genymobile.scrcpy.wrappers.SurfaceControl;
|
import com.genymobile.scrcpy.wrappers.SurfaceControl;
|
||||||
@@ -11,10 +10,10 @@ import com.genymobile.scrcpy.wrappers.WindowManager;
|
|||||||
import android.content.IOnPrimaryClipChangedListener;
|
import android.content.IOnPrimaryClipChangedListener;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.HandlerThread;
|
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
import android.view.IDisplayFoldListener;
|
||||||
|
import android.view.IRotationWatcher;
|
||||||
import android.view.InputDevice;
|
import android.view.InputDevice;
|
||||||
import android.view.InputEvent;
|
import android.view.InputEvent;
|
||||||
import android.view.KeyCharacterMap;
|
import android.view.KeyCharacterMap;
|
||||||
@@ -34,21 +33,26 @@ public final class Device {
|
|||||||
public static final int LOCK_VIDEO_ORIENTATION_UNLOCKED = -1;
|
public static final int LOCK_VIDEO_ORIENTATION_UNLOCKED = -1;
|
||||||
public static final int LOCK_VIDEO_ORIENTATION_INITIAL = -2;
|
public static final int LOCK_VIDEO_ORIENTATION_INITIAL = -2;
|
||||||
|
|
||||||
public interface DisplayChangeListener {
|
public interface RotationListener {
|
||||||
void onDisplayChanged();
|
void onRotationChanged(int rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface FoldListener {
|
||||||
|
void onFoldChanged(int displayId, boolean folded);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ClipboardListener {
|
public interface ClipboardListener {
|
||||||
void onClipboardTextChanged(String text);
|
void onClipboardTextChanged(String text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final Size deviceSize;
|
||||||
private final Rect crop;
|
private final Rect crop;
|
||||||
private int maxSize;
|
private int maxSize;
|
||||||
private final int lockVideoOrientation;
|
private final int lockVideoOrientation;
|
||||||
|
|
||||||
private Size deviceSize;
|
|
||||||
private ScreenInfo screenInfo;
|
private ScreenInfo screenInfo;
|
||||||
private DisplayChangeListener displayChangeListener;
|
private RotationListener rotationListener;
|
||||||
|
private FoldListener foldListener;
|
||||||
private ClipboardListener clipboardListener;
|
private ClipboardListener clipboardListener;
|
||||||
private final AtomicBoolean isSettingClipboard = new AtomicBoolean();
|
private final AtomicBoolean isSettingClipboard = new AtomicBoolean();
|
||||||
|
|
||||||
@@ -82,26 +86,46 @@ public final class Device {
|
|||||||
screenInfo = ScreenInfo.computeScreenInfo(displayInfo.getRotation(), deviceSize, crop, maxSize, lockVideoOrientation);
|
screenInfo = ScreenInfo.computeScreenInfo(displayInfo.getRotation(), deviceSize, crop, maxSize, lockVideoOrientation);
|
||||||
layerStack = displayInfo.getLayerStack();
|
layerStack = displayInfo.getLayerStack();
|
||||||
|
|
||||||
HandlerThread displayListenerThread = new HandlerThread("DisplayListenerThread");
|
ServiceManager.getWindowManager().registerRotationWatcher(new IRotationWatcher.Stub() {
|
||||||
displayListenerThread.start();
|
|
||||||
|
|
||||||
Handler displayListenerHandler = new Handler(displayListenerThread.getLooper());
|
|
||||||
ServiceManager.getDisplayManager().registerDisplayListener(new DisplayManager.DisplayListener() {
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisplayChanged(int displayId) {
|
public void onRotationChanged(int rotation) {
|
||||||
if (Device.this.displayId != displayId) {
|
synchronized (Device.this) {
|
||||||
return;
|
screenInfo = screenInfo.withDeviceRotation(rotation);
|
||||||
}
|
|
||||||
|
|
||||||
DisplayInfo displayInfo = ServiceManager.getDisplayManager().getDisplayInfo(displayId);
|
// notify
|
||||||
deviceSize = displayInfo.getSize();
|
if (rotationListener != null) {
|
||||||
screenInfo = ScreenInfo.computeScreenInfo(displayInfo.getRotation(), deviceSize, crop, maxSize, lockVideoOrientation);
|
rotationListener.onRotationChanged(rotation);
|
||||||
|
}
|
||||||
if (displayChangeListener != null) {
|
|
||||||
displayChangeListener.onDisplayChanged();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, displayListenerHandler);
|
}, displayId);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
ServiceManager.getWindowManager().registerDisplayFoldListener(new IDisplayFoldListener.Stub() {
|
||||||
|
@Override
|
||||||
|
public void onDisplayFoldChanged(int displayId, boolean folded) {
|
||||||
|
if (Device.this.displayId != displayId) {
|
||||||
|
// Ignore events related to other display ids
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (Device.this) {
|
||||||
|
DisplayInfo displayInfo = ServiceManager.getDisplayManager().getDisplayInfo(displayId);
|
||||||
|
if (displayInfo == null) {
|
||||||
|
Ln.e("Display " + displayId + " not found\n" + LogUtils.buildDisplayListMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
screenInfo = ScreenInfo.computeScreenInfo(displayInfo.getRotation(), displayInfo.getSize(), options.getCrop(),
|
||||||
|
options.getMaxSize(), options.getLockVideoOrientation());
|
||||||
|
// notify
|
||||||
|
if (foldListener != null) {
|
||||||
|
foldListener.onFoldChanged(displayId, folded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (options.getControl() && options.getClipboardAutosync()) {
|
if (options.getControl() && options.getClipboardAutosync()) {
|
||||||
// If control and autosync are enabled, synchronize Android clipboard to the computer automatically
|
// If control and autosync are enabled, synchronize Android clipboard to the computer automatically
|
||||||
@@ -230,8 +254,12 @@ public final class Device {
|
|||||||
return ServiceManager.getPowerManager().isScreenOn();
|
return ServiceManager.getPowerManager().isScreenOn();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void setDisplayChangeListener(DisplayChangeListener displayChangeListener) {
|
public synchronized void setRotationListener(RotationListener rotationListener) {
|
||||||
this.displayChangeListener = displayChangeListener;
|
this.rotationListener = rotationListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void setFoldListener(FoldListener foldlistener) {
|
||||||
|
this.foldListener = foldlistener;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void setClipboardListener(ClipboardListener clipboardListener) {
|
public synchronized void setClipboardListener(ClipboardListener clipboardListener) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import android.os.Build;
|
|||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
|
||||||
public class ScreenCapture extends SurfaceCapture implements Device.DisplayChangeListener {
|
public class ScreenCapture extends SurfaceCapture implements Device.RotationListener, Device.FoldListener {
|
||||||
|
|
||||||
private final Device device;
|
private final Device device;
|
||||||
private IBinder display;
|
private IBinder display;
|
||||||
@@ -18,7 +18,8 @@ public class ScreenCapture extends SurfaceCapture implements Device.DisplayChang
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() {
|
public void init() {
|
||||||
device.setDisplayChangeListener(this);
|
device.setRotationListener(this);
|
||||||
|
device.setFoldListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -40,7 +41,8 @@ public class ScreenCapture extends SurfaceCapture implements Device.DisplayChang
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void release() {
|
public void release() {
|
||||||
device.setDisplayChangeListener(null);
|
device.setRotationListener(null);
|
||||||
|
device.setFoldListener(null);
|
||||||
if (display != null) {
|
if (display != null) {
|
||||||
SurfaceControl.destroyDisplay(display);
|
SurfaceControl.destroyDisplay(display);
|
||||||
}
|
}
|
||||||
@@ -58,7 +60,12 @@ public class ScreenCapture extends SurfaceCapture implements Device.DisplayChang
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisplayChanged() {
|
public void onFoldChanged(int displayId, boolean folded) {
|
||||||
|
requestReset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRotationChanged(int rotation) {
|
||||||
requestReset();
|
requestReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -285,16 +285,28 @@ public final class Workarounds {
|
|||||||
Method getParcelMethod = attributionSourceState.getClass().getDeclaredMethod("getParcel");
|
Method getParcelMethod = attributionSourceState.getClass().getDeclaredMethod("getParcel");
|
||||||
Parcel attributionSourceParcel = (Parcel) getParcelMethod.invoke(attributionSourceState);
|
Parcel attributionSourceParcel = (Parcel) getParcelMethod.invoke(attributionSourceState);
|
||||||
|
|
||||||
// private native int native_setup(Object audiorecordThis,
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||||
// Object /*AudioAttributes*/ attributes,
|
// private native int native_setup(Object audiorecordThis,
|
||||||
// int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
|
// Object /*AudioAttributes*/ attributes,
|
||||||
// int buffSizeInBytes, int[] sessionId, @NonNull Parcel attributionSource,
|
// int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
|
||||||
// long nativeRecordInJavaObj, int maxSharedAudioHistoryMs);
|
// int buffSizeInBytes, int[] sessionId, @NonNull Parcel attributionSource,
|
||||||
Method nativeSetupMethod = AudioRecord.class.getDeclaredMethod("native_setup", Object.class, Object.class, int[].class, int.class,
|
// long nativeRecordInJavaObj, int maxSharedAudioHistoryMs);
|
||||||
int.class, int.class, int.class, int[].class, Parcel.class, long.class, int.class);
|
Method nativeSetupMethod = AudioRecord.class.getDeclaredMethod("native_setup", Object.class, Object.class, int[].class,
|
||||||
nativeSetupMethod.setAccessible(true);
|
int.class, int.class, int.class, int.class, int[].class, Parcel.class, long.class, int.class);
|
||||||
initResult = (int) nativeSetupMethod.invoke(audioRecord, new WeakReference<AudioRecord>(audioRecord), attributes, sampleRateArray,
|
nativeSetupMethod.setAccessible(true);
|
||||||
channelMask, channelIndexMask, audioRecord.getAudioFormat(), bufferSizeInBytes, session, attributionSourceParcel, 0L, 0);
|
initResult = (int) nativeSetupMethod.invoke(audioRecord, new WeakReference<AudioRecord>(audioRecord), attributes,
|
||||||
|
sampleRateArray, channelMask, channelIndexMask, audioRecord.getAudioFormat(), bufferSizeInBytes, session,
|
||||||
|
attributionSourceParcel, 0L, 0);
|
||||||
|
} else {
|
||||||
|
// Android 14 added a new int parameter "halInputFlags"
|
||||||
|
// <https://github.com/aosp-mirror/platform_frameworks_base/commit/f6135d75db79b1d48fad3a3b3080d37be20a2313>
|
||||||
|
Method nativeSetupMethod = AudioRecord.class.getDeclaredMethod("native_setup", Object.class, Object.class, int[].class,
|
||||||
|
int.class, int.class, int.class, int.class, int[].class, Parcel.class, long.class, int.class, int.class);
|
||||||
|
nativeSetupMethod.setAccessible(true);
|
||||||
|
initResult = (int) nativeSetupMethod.invoke(audioRecord, new WeakReference<AudioRecord>(audioRecord), attributes,
|
||||||
|
sampleRateArray, channelMask, channelIndexMask, audioRecord.getAudioFormat(), bufferSizeInBytes, session,
|
||||||
|
attributionSourceParcel, 0L, 0, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,8 +41,13 @@ public final class ClipboardManager {
|
|||||||
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, String.class, int.class, int.class);
|
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, String.class, int.class, int.class);
|
||||||
getMethodVersion = 2;
|
getMethodVersion = 2;
|
||||||
} catch (NoSuchMethodException e3) {
|
} catch (NoSuchMethodException e3) {
|
||||||
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, int.class, String.class);
|
try {
|
||||||
getMethodVersion = 3;
|
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, int.class, String.class);
|
||||||
|
getMethodVersion = 3;
|
||||||
|
} catch (NoSuchMethodException e4) {
|
||||||
|
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, String.class, int.class, int.class, boolean.class);
|
||||||
|
getMethodVersion = 4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,8 +92,10 @@ public final class ClipboardManager {
|
|||||||
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID);
|
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID);
|
||||||
case 2:
|
case 2:
|
||||||
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0);
|
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0);
|
||||||
default:
|
case 3:
|
||||||
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID, null);
|
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID, null);
|
||||||
|
default:
|
||||||
|
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,20 +5,13 @@ import com.genymobile.scrcpy.DisplayInfo;
|
|||||||
import com.genymobile.scrcpy.Ln;
|
import com.genymobile.scrcpy.Ln;
|
||||||
import com.genymobile.scrcpy.Size;
|
import com.genymobile.scrcpy.Size;
|
||||||
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.view.Display;
|
import android.view.Display;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Proxy;
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public final class DisplayManager {
|
public final class DisplayManager {
|
||||||
|
|
||||||
// Constant copied from AOSP framework_base: core/java/android/hardware/display/DisplayManager.java
|
|
||||||
private static final long EVENT_FLAG_DISPLAY_CHANGED = 1L << 2;
|
|
||||||
|
|
||||||
private final Object manager; // instance of hidden class android.hardware.display.DisplayManagerGlobal
|
private final Object manager; // instance of hidden class android.hardware.display.DisplayManagerGlobal
|
||||||
|
|
||||||
public DisplayManager(Object manager) {
|
public DisplayManager(Object manager) {
|
||||||
@@ -101,47 +94,4 @@ public final class DisplayManager {
|
|||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerDisplayListener(DisplayListener listener, Handler handler) {
|
|
||||||
try {
|
|
||||||
Class<?> displayListenerClass = Class.forName("android.hardware.display.DisplayManager$DisplayListener");
|
|
||||||
Object displayListenerProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
|
|
||||||
new Class[]{displayListenerClass},
|
|
||||||
(proxy, method, args) -> {
|
|
||||||
if ("onDisplayChanged".equals(method.getName())) {
|
|
||||||
listener.onDisplayChanged((int) args[0]);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
|
||||||
try {
|
|
||||||
manager.getClass()
|
|
||||||
.getMethod("registerDisplayListener", displayListenerClass, Handler.class, long.class)
|
|
||||||
.invoke(manager, displayListenerProxy, handler, EVENT_FLAG_DISPLAY_CHANGED);
|
|
||||||
return;
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
// fall-through
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
manager.getClass()
|
|
||||||
.getMethod("registerDisplayListener", displayListenerClass, Handler.class)
|
|
||||||
.invoke(manager, displayListenerProxy, handler);
|
|
||||||
} catch (Exception e) {
|
|
||||||
// Screen size won't be updated, not a fatal error
|
|
||||||
Ln.e("Could not register display listener", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Partially copied from AOSP framework_base: core/java/android/hardware/display/DisplayManager.java
|
|
||||||
public interface DisplayListener {
|
|
||||||
/**
|
|
||||||
* Called whenever the properties of a logical {@link android.view.Display},
|
|
||||||
* such as size and density, have changed.
|
|
||||||
*
|
|
||||||
* @param displayId The id of the logical display that changed.
|
|
||||||
*/
|
|
||||||
void onDisplayChanged(int displayId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ package com.genymobile.scrcpy.wrappers;
|
|||||||
|
|
||||||
import com.genymobile.scrcpy.Ln;
|
import com.genymobile.scrcpy.Ln;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
import android.os.IInterface;
|
import android.os.IInterface;
|
||||||
|
import android.view.IDisplayFoldListener;
|
||||||
|
import android.view.IRotationWatcher;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@@ -91,4 +94,30 @@ public final class WindowManager {
|
|||||||
Ln.e("Could not invoke method", e);
|
Ln.e("Could not invoke method", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void registerRotationWatcher(IRotationWatcher rotationWatcher, int displayId) {
|
||||||
|
try {
|
||||||
|
Class<?> cls = manager.getClass();
|
||||||
|
try {
|
||||||
|
// display parameter added since this commit:
|
||||||
|
// https://android.googlesource.com/platform/frameworks/base/+/35fa3c26adcb5f6577849fd0df5228b1f67cf2c6%5E%21/#F1
|
||||||
|
cls.getMethod("watchRotation", IRotationWatcher.class, int.class).invoke(manager, rotationWatcher, displayId);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
// old version
|
||||||
|
cls.getMethod("watchRotation", IRotationWatcher.class).invoke(manager, rotationWatcher);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Ln.e("Could not register rotation watcher", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(29)
|
||||||
|
public void registerDisplayFoldListener(IDisplayFoldListener foldListener) {
|
||||||
|
try {
|
||||||
|
Class<?> cls = manager.getClass();
|
||||||
|
cls.getMethod("registerDisplayFoldListener", IDisplayFoldListener.class).invoke(manager, foldListener);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Ln.e("Could not register display fold listener", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user