Compare commits
6 Commits
android11_
...
clock_nosl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0969f4eae7 | ||
|
|
2fff9b9edf | ||
|
|
57f879d68a | ||
|
|
3626d90004 | ||
|
|
02f4ff7534 | ||
|
|
a3871130cc |
@@ -277,10 +277,6 @@ if get_option('buildtype') == 'debug'
|
|||||||
'src/util/strbuf.c',
|
'src/util/strbuf.c',
|
||||||
'src/util/term.c',
|
'src/util/term.c',
|
||||||
]],
|
]],
|
||||||
['test_clock', [
|
|
||||||
'tests/test_clock.c',
|
|
||||||
'src/clock.c',
|
|
||||||
]],
|
|
||||||
['test_control_msg_serialize', [
|
['test_control_msg_serialize', [
|
||||||
'tests/test_control_msg_serialize.c',
|
'tests/test_control_msg_serialize.c',
|
||||||
'src/control_msg.c',
|
'src/control_msg.c',
|
||||||
|
|||||||
106
app/src/clock.c
106
app/src/clock.c
@@ -1,116 +1,34 @@
|
|||||||
#include "clock.h"
|
#include "clock.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
|
|
||||||
#define SC_CLOCK_NDEBUG // comment to debug
|
#define SC_CLOCK_NDEBUG // comment to debug
|
||||||
|
|
||||||
void
|
void
|
||||||
sc_clock_init(struct sc_clock *clock) {
|
sc_clock_init(struct sc_clock *clock) {
|
||||||
clock->count = 0;
|
clock->range = 0;
|
||||||
clock->head = 0;
|
clock->offset = 0;
|
||||||
clock->left_sum.system = 0;
|
|
||||||
clock->left_sum.stream = 0;
|
|
||||||
clock->right_sum.system = 0;
|
|
||||||
clock->right_sum.stream = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Estimate the affine function f(stream) = slope * stream + offset
|
|
||||||
static void
|
|
||||||
sc_clock_estimate(struct sc_clock *clock,
|
|
||||||
double *out_slope, sc_tick *out_offset) {
|
|
||||||
assert(clock->count);
|
|
||||||
|
|
||||||
if (clock->count == 1) {
|
|
||||||
// If there is only 1 point, we can't compute a slope. Assume it is 1.
|
|
||||||
struct sc_clock_point *single_point = &clock->right_sum;
|
|
||||||
*out_slope = 1;
|
|
||||||
*out_offset = single_point->system - single_point->stream;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sc_clock_point left_avg = {
|
|
||||||
.system = clock->left_sum.system / (clock->count / 2),
|
|
||||||
.stream = clock->left_sum.stream / (clock->count / 2),
|
|
||||||
};
|
|
||||||
struct sc_clock_point right_avg = {
|
|
||||||
.system = clock->right_sum.system / ((clock->count + 1) / 2),
|
|
||||||
.stream = clock->right_sum.stream / ((clock->count + 1) / 2),
|
|
||||||
};
|
|
||||||
|
|
||||||
double slope = (double) (right_avg.system - left_avg.system)
|
|
||||||
/ (right_avg.stream - left_avg.stream);
|
|
||||||
|
|
||||||
if (clock->count < SC_CLOCK_RANGE) {
|
|
||||||
/* The first frames are typically received and decoded with more delay
|
|
||||||
* than the others, causing a wrong slope estimation on start. To
|
|
||||||
* compensate, assume an initial slope of 1, then progressively use the
|
|
||||||
* estimated slope. */
|
|
||||||
slope = (clock->count * slope + (SC_CLOCK_RANGE - clock->count))
|
|
||||||
/ SC_CLOCK_RANGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sc_clock_point global_avg = {
|
|
||||||
.system = (clock->left_sum.system + clock->right_sum.system)
|
|
||||||
/ clock->count,
|
|
||||||
.stream = (clock->left_sum.stream + clock->right_sum.stream)
|
|
||||||
/ clock->count,
|
|
||||||
};
|
|
||||||
|
|
||||||
sc_tick offset = global_avg.system - (sc_tick) (global_avg.stream * slope);
|
|
||||||
|
|
||||||
*out_slope = slope;
|
|
||||||
*out_offset = offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
sc_clock_update(struct sc_clock *clock, sc_tick system, sc_tick stream) {
|
sc_clock_update(struct sc_clock *clock, sc_tick system, sc_tick stream) {
|
||||||
struct sc_clock_point *point = &clock->points[clock->head];
|
if (clock->range < SC_CLOCK_RANGE) {
|
||||||
|
++clock->range;
|
||||||
if (clock->count == SC_CLOCK_RANGE || clock->count & 1) {
|
|
||||||
// One point passes from the right sum to the left sum
|
|
||||||
|
|
||||||
unsigned mid;
|
|
||||||
if (clock->count == SC_CLOCK_RANGE) {
|
|
||||||
mid = (clock->head + SC_CLOCK_RANGE / 2) % SC_CLOCK_RANGE;
|
|
||||||
} else {
|
|
||||||
// Only for the first frames
|
|
||||||
mid = clock->count / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sc_clock_point *mid_point = &clock->points[mid];
|
|
||||||
clock->left_sum.system += mid_point->system;
|
|
||||||
clock->left_sum.stream += mid_point->stream;
|
|
||||||
clock->right_sum.system -= mid_point->system;
|
|
||||||
clock->right_sum.stream -= mid_point->stream;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clock->count == SC_CLOCK_RANGE) {
|
sc_tick offset = system - stream;
|
||||||
// The current point overwrites the previous value in the circular
|
clock->offset = ((clock->range - 1) * clock->offset + offset)
|
||||||
// array, update the left sum accordingly
|
/ clock->range;
|
||||||
clock->left_sum.system -= point->system;
|
|
||||||
clock->left_sum.stream -= point->stream;
|
|
||||||
} else {
|
|
||||||
++clock->count;
|
|
||||||
}
|
|
||||||
|
|
||||||
point->system = system;
|
|
||||||
point->stream = stream;
|
|
||||||
|
|
||||||
clock->right_sum.system += system;
|
|
||||||
clock->right_sum.stream += stream;
|
|
||||||
|
|
||||||
clock->head = (clock->head + 1) % SC_CLOCK_RANGE;
|
|
||||||
|
|
||||||
// Update estimation
|
|
||||||
sc_clock_estimate(clock, &clock->slope, &clock->offset);
|
|
||||||
|
|
||||||
#ifndef SC_CLOCK_NDEBUG
|
#ifndef SC_CLOCK_NDEBUG
|
||||||
LOGD("Clock estimation: %f * pts + %" PRItick, clock->slope, clock->offset);
|
LOGD("Clock estimation: pts + %" PRItick, clock->offset);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
sc_tick
|
sc_tick
|
||||||
sc_clock_to_system_time(struct sc_clock *clock, sc_tick stream) {
|
sc_clock_to_system_time(struct sc_clock *clock, sc_tick stream) {
|
||||||
assert(clock->count); // sc_clock_update() must have been called
|
assert(clock->range); // sc_clock_update() must have been called
|
||||||
return (sc_tick) (stream * clock->slope) + clock->offset;
|
return stream + clock->offset;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,9 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include "util/tick.h"
|
#include "util/tick.h"
|
||||||
|
|
||||||
#define SC_CLOCK_RANGE 32
|
#define SC_CLOCK_RANGE 32
|
||||||
static_assert(!(SC_CLOCK_RANGE & 1), "SC_CLOCK_RANGE must be even");
|
|
||||||
|
|
||||||
struct sc_clock_point {
|
struct sc_clock_point {
|
||||||
sc_tick system;
|
sc_tick system;
|
||||||
@@ -21,40 +18,18 @@ struct sc_clock_point {
|
|||||||
*
|
*
|
||||||
* f(stream) = slope * stream + offset
|
* f(stream) = slope * stream + offset
|
||||||
*
|
*
|
||||||
* To that end, it stores the SC_CLOCK_RANGE last clock points (the timestamps
|
* Theoretically, the slope encodes the drift between the device clock and the
|
||||||
* of a frame expressed both in stream time and system time) in a circular
|
* computer clock. It is expected to be very close to 1.
|
||||||
* array.
|
|
||||||
*
|
*
|
||||||
* To estimate the slope, it splits the last SC_CLOCK_RANGE points into two
|
* Since the clock is used to estimate very close points in the future (which
|
||||||
* sets of SC_CLOCK_RANGE/2 points, and computes their centroid ("average
|
* are reestimated on every clock update, see delay_buffer), the error caused
|
||||||
* point"). The slope of the estimated affine function is that of the line
|
* by clock drift is totally negligible, so it is better to assume that the
|
||||||
* passing through these two points.
|
* slope is 1 than to estimate it (the estimation error would be larger).
|
||||||
*
|
*
|
||||||
* To estimate the offset, it computes the centroid of all the SC_CLOCK_RANGE
|
* Therefore, only the offset is estimated.
|
||||||
* points. The resulting affine function passes by this centroid.
|
|
||||||
*
|
|
||||||
* With a circular array, the rolling sums (and average) are quick to compute.
|
|
||||||
* In practice, the estimation is stable and the evolution is smooth.
|
|
||||||
*/
|
*/
|
||||||
struct sc_clock {
|
struct sc_clock {
|
||||||
// Circular array
|
unsigned range;
|
||||||
struct sc_clock_point points[SC_CLOCK_RANGE];
|
|
||||||
|
|
||||||
// Number of points in the array (count <= SC_CLOCK_RANGE)
|
|
||||||
unsigned count;
|
|
||||||
|
|
||||||
// Index of the next point to write
|
|
||||||
unsigned head;
|
|
||||||
|
|
||||||
// Sum of the first count/2 points
|
|
||||||
struct sc_clock_point left_sum;
|
|
||||||
|
|
||||||
// Sum of the last (count+1)/2 points
|
|
||||||
struct sc_clock_point right_sum;
|
|
||||||
|
|
||||||
// Estimated slope and offset
|
|
||||||
// (computed on sc_clock_update(), used by sc_clock_to_system_time())
|
|
||||||
double slope;
|
|
||||||
sc_tick offset;
|
sc_tick offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ sc_delay_buffer_frame_sink_push(struct sc_frame_sink *sink,
|
|||||||
sc_clock_update(&db->clock, sc_tick_now(), pts);
|
sc_clock_update(&db->clock, sc_tick_now(), pts);
|
||||||
sc_cond_signal(&db->wait_cond);
|
sc_cond_signal(&db->wait_cond);
|
||||||
|
|
||||||
if (db->first_frame_asap && db->clock.count == 1) {
|
if (db->first_frame_asap && db->clock.range == 1) {
|
||||||
sc_mutex_unlock(&db->mutex);
|
sc_mutex_unlock(&db->mutex);
|
||||||
return sc_frame_source_sinks_push(&db->frame_source, frame);
|
return sc_frame_source_sinks_push(&db->frame_source, frame);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
#include "common.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include "clock.h"
|
|
||||||
|
|
||||||
void test_small_rolling_sum(void) {
|
|
||||||
struct sc_clock clock;
|
|
||||||
sc_clock_init(&clock);
|
|
||||||
|
|
||||||
assert(clock.count == 0);
|
|
||||||
assert(clock.left_sum.system == 0);
|
|
||||||
assert(clock.left_sum.stream == 0);
|
|
||||||
assert(clock.right_sum.system == 0);
|
|
||||||
assert(clock.right_sum.stream == 0);
|
|
||||||
|
|
||||||
sc_clock_update(&clock, 2, 3);
|
|
||||||
assert(clock.count == 1);
|
|
||||||
assert(clock.left_sum.system == 0);
|
|
||||||
assert(clock.left_sum.stream == 0);
|
|
||||||
assert(clock.right_sum.system == 2);
|
|
||||||
assert(clock.right_sum.stream == 3);
|
|
||||||
|
|
||||||
sc_clock_update(&clock, 10, 20);
|
|
||||||
assert(clock.count == 2);
|
|
||||||
assert(clock.left_sum.system == 2);
|
|
||||||
assert(clock.left_sum.stream == 3);
|
|
||||||
assert(clock.right_sum.system == 10);
|
|
||||||
assert(clock.right_sum.stream == 20);
|
|
||||||
|
|
||||||
sc_clock_update(&clock, 40, 80);
|
|
||||||
assert(clock.count == 3);
|
|
||||||
assert(clock.left_sum.system == 2);
|
|
||||||
assert(clock.left_sum.stream == 3);
|
|
||||||
assert(clock.right_sum.system == 50);
|
|
||||||
assert(clock.right_sum.stream == 100);
|
|
||||||
|
|
||||||
sc_clock_update(&clock, 400, 800);
|
|
||||||
assert(clock.count == 4);
|
|
||||||
assert(clock.left_sum.system == 12);
|
|
||||||
assert(clock.left_sum.stream == 23);
|
|
||||||
assert(clock.right_sum.system == 440);
|
|
||||||
assert(clock.right_sum.stream == 880);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_large_rolling_sum(void) {
|
|
||||||
const unsigned half_range = SC_CLOCK_RANGE / 2;
|
|
||||||
|
|
||||||
struct sc_clock clock1;
|
|
||||||
sc_clock_init(&clock1);
|
|
||||||
for (unsigned i = 0; i < 5 * half_range; ++i) {
|
|
||||||
sc_clock_update(&clock1, i, 2 * i + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sc_clock clock2;
|
|
||||||
sc_clock_init(&clock2);
|
|
||||||
for (unsigned i = 3 * half_range; i < 5 * half_range; ++i) {
|
|
||||||
sc_clock_update(&clock2, i, 2 * i + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(clock1.count == SC_CLOCK_RANGE);
|
|
||||||
assert(clock2.count == SC_CLOCK_RANGE);
|
|
||||||
|
|
||||||
// The values before the last SC_CLOCK_RANGE points in clock1 should have
|
|
||||||
// no impact
|
|
||||||
assert(clock1.left_sum.system == clock2.left_sum.system);
|
|
||||||
assert(clock1.left_sum.stream == clock2.left_sum.stream);
|
|
||||||
assert(clock1.right_sum.system == clock2.right_sum.system);
|
|
||||||
assert(clock1.right_sum.stream == clock2.right_sum.stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
(void) argc;
|
|
||||||
(void) argv;
|
|
||||||
|
|
||||||
test_small_rolling_sum();
|
|
||||||
test_large_rolling_sum();
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
@@ -274,7 +274,7 @@ public final class AudioEncoder implements AsyncProcessor {
|
|||||||
Ln.e("Audio encoder '" + encoderName + "' for " + codec.getName() + " not found\n" + LogUtils.buildAudioEncoderListMessage());
|
Ln.e("Audio encoder '" + encoderName + "' for " + codec.getName() + " not found\n" + LogUtils.buildAudioEncoderListMessage());
|
||||||
throw new ConfigurationException("Unknown encoder: " + encoderName);
|
throw new ConfigurationException("Unknown encoder: " + encoderName);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Ln.e("Could not create video encoder '" + encoderName + "' for " + codec.getName() + "\n" + LogUtils.buildAudioEncoderListMessage());
|
Ln.e("Could not create audio encoder '" + encoderName + "' for " + codec.getName() + "\n" + LogUtils.buildAudioEncoderListMessage());
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,4 +38,10 @@ public final class FakeContext extends ContextWrapper {
|
|||||||
builder.setPackageName(PACKAGE_NAME);
|
builder.setPackageName(PACKAGE_NAME);
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Override to be added on SDK upgrade for Android 14
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public int getDeviceId() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ public class ClipboardManager {
|
|||||||
private Method getPrimaryClipMethod;
|
private Method getPrimaryClipMethod;
|
||||||
private Method setPrimaryClipMethod;
|
private Method setPrimaryClipMethod;
|
||||||
private Method addPrimaryClipChangedListener;
|
private Method addPrimaryClipChangedListener;
|
||||||
private boolean alternativeGetMethod;
|
private int getMethodVersion;
|
||||||
private boolean alternativeSetMethod;
|
private int setMethodVersion;
|
||||||
private boolean alternativeAddListenerMethod;
|
private int addListenerMethodVersion;
|
||||||
|
|
||||||
public ClipboardManager(IInterface manager) {
|
public ClipboardManager(IInterface manager) {
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
@@ -31,9 +31,15 @@ public class ClipboardManager {
|
|||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, int.class);
|
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, int.class);
|
||||||
} catch (NoSuchMethodException e) {
|
getMethodVersion = 0;
|
||||||
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, String.class, int.class);
|
} catch (NoSuchMethodException e1) {
|
||||||
alternativeGetMethod = true;
|
try {
|
||||||
|
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, String.class, int.class);
|
||||||
|
getMethodVersion = 1;
|
||||||
|
} catch (NoSuchMethodException e2) {
|
||||||
|
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, String.class, int.class, int.class);
|
||||||
|
getMethodVersion = 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,41 +53,62 @@ public class ClipboardManager {
|
|||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class, int.class);
|
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class, int.class);
|
||||||
} catch (NoSuchMethodException e) {
|
setMethodVersion = 0;
|
||||||
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class, String.class, int.class);
|
} catch (NoSuchMethodException e1) {
|
||||||
alternativeSetMethod = true;
|
try {
|
||||||
|
setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class, String.class, int.class);
|
||||||
|
setMethodVersion = 1;
|
||||||
|
} catch (NoSuchMethodException e2) {
|
||||||
|
setPrimaryClipMethod = manager.getClass()
|
||||||
|
.getMethod("setPrimaryClip", ClipData.class, String.class, String.class, int.class, int.class);
|
||||||
|
setMethodVersion = 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return setPrimaryClipMethod;
|
return setPrimaryClipMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ClipData getPrimaryClip(Method method, boolean alternativeMethod, IInterface manager)
|
private static ClipData getPrimaryClip(Method method, int methodVersion, IInterface manager)
|
||||||
throws InvocationTargetException, IllegalAccessException {
|
throws InvocationTargetException, IllegalAccessException {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME);
|
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME);
|
||||||
}
|
}
|
||||||
if (alternativeMethod) {
|
|
||||||
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID);
|
switch (methodVersion) {
|
||||||
|
case 0:
|
||||||
|
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID);
|
||||||
|
case 1:
|
||||||
|
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID);
|
||||||
|
default:
|
||||||
|
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0);
|
||||||
}
|
}
|
||||||
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setPrimaryClip(Method method, boolean alternativeMethod, IInterface manager, ClipData clipData)
|
private static void setPrimaryClip(Method method, int methodVersion, IInterface manager, ClipData clipData)
|
||||||
throws InvocationTargetException, IllegalAccessException {
|
throws InvocationTargetException, IllegalAccessException {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
method.invoke(manager, clipData, FakeContext.PACKAGE_NAME);
|
method.invoke(manager, clipData, FakeContext.PACKAGE_NAME);
|
||||||
} else if (alternativeMethod) {
|
return;
|
||||||
method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID);
|
}
|
||||||
} else {
|
|
||||||
method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID);
|
switch (methodVersion) {
|
||||||
|
case 0:
|
||||||
|
method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public CharSequence getText() {
|
public CharSequence getText() {
|
||||||
try {
|
try {
|
||||||
Method method = getGetPrimaryClipMethod();
|
Method method = getGetPrimaryClipMethod();
|
||||||
ClipData clipData = getPrimaryClip(method, alternativeGetMethod, manager);
|
ClipData clipData = getPrimaryClip(method, getMethodVersion, manager);
|
||||||
if (clipData == null || clipData.getItemCount() == 0) {
|
if (clipData == null || clipData.getItemCount() == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -96,7 +123,7 @@ public class ClipboardManager {
|
|||||||
try {
|
try {
|
||||||
Method method = getSetPrimaryClipMethod();
|
Method method = getSetPrimaryClipMethod();
|
||||||
ClipData clipData = ClipData.newPlainText(null, text);
|
ClipData clipData = ClipData.newPlainText(null, text);
|
||||||
setPrimaryClip(method, alternativeSetMethod, manager, clipData);
|
setPrimaryClip(method, setMethodVersion, manager, clipData);
|
||||||
return true;
|
return true;
|
||||||
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
Ln.e("Could not invoke method", e);
|
Ln.e("Could not invoke method", e);
|
||||||
@@ -104,14 +131,23 @@ public class ClipboardManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addPrimaryClipChangedListener(Method method, boolean alternativeMethod, IInterface manager,
|
private static void addPrimaryClipChangedListener(Method method, int methodVersion, IInterface manager,
|
||||||
IOnPrimaryClipChangedListener listener) throws InvocationTargetException, IllegalAccessException {
|
IOnPrimaryClipChangedListener listener) throws InvocationTargetException, IllegalAccessException {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
method.invoke(manager, listener, FakeContext.PACKAGE_NAME);
|
method.invoke(manager, listener, FakeContext.PACKAGE_NAME);
|
||||||
} else if (alternativeMethod) {
|
return;
|
||||||
method.invoke(manager, listener, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID);
|
}
|
||||||
} else {
|
|
||||||
method.invoke(manager, listener, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID);
|
switch (methodVersion) {
|
||||||
|
case 0:
|
||||||
|
method.invoke(manager, listener, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
method.invoke(manager, listener, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
method.invoke(manager, listener, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,10 +160,19 @@ public class ClipboardManager {
|
|||||||
try {
|
try {
|
||||||
addPrimaryClipChangedListener = manager.getClass()
|
addPrimaryClipChangedListener = manager.getClass()
|
||||||
.getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class, int.class);
|
.getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class, int.class);
|
||||||
} catch (NoSuchMethodException e) {
|
addListenerMethodVersion = 0;
|
||||||
addPrimaryClipChangedListener = manager.getClass()
|
} catch (NoSuchMethodException e1) {
|
||||||
.getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class, String.class, int.class);
|
try {
|
||||||
alternativeAddListenerMethod = true;
|
addPrimaryClipChangedListener = manager.getClass()
|
||||||
|
.getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class, String.class,
|
||||||
|
int.class);
|
||||||
|
addListenerMethodVersion = 1;
|
||||||
|
} catch (NoSuchMethodException e2) {
|
||||||
|
addPrimaryClipChangedListener = manager.getClass()
|
||||||
|
.getMethod("addPrimaryClipChangedListener", IOnPrimaryClipChangedListener.class, String.class, String.class,
|
||||||
|
int.class, int.class);
|
||||||
|
addListenerMethodVersion = 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,7 +182,7 @@ public class ClipboardManager {
|
|||||||
public boolean addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener) {
|
public boolean addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener) {
|
||||||
try {
|
try {
|
||||||
Method method = getAddPrimaryClipChangedListener();
|
Method method = getAddPrimaryClipChangedListener();
|
||||||
addPrimaryClipChangedListener(method, alternativeAddListenerMethod, manager, listener);
|
addPrimaryClipChangedListener(method, addListenerMethodVersion, manager, listener);
|
||||||
return true;
|
return true;
|
||||||
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
Ln.e("Could not invoke method", e);
|
Ln.e("Could not invoke method", e);
|
||||||
|
|||||||
Reference in New Issue
Block a user