Skip to content

Commit

Permalink
Merge pull request #14 from cagnulein/changing-screen-timeout
Browse files Browse the repository at this point in the history
Changing screen timeout
  • Loading branch information
cagnulein authored Jul 4, 2024
2 parents ba59c9d + e646bfd commit 788601d
Show file tree
Hide file tree
Showing 13 changed files with 1,099 additions and 3 deletions.
Binary file modified app/src/main/assets/scrcpy-server.jar
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,8 @@ public boolean onKeyUp(int keyCode, KeyEvent event) {
private boolean handleKeyEvent(int keyCode, KeyEvent event) {
Log.d("keyboard", event.toString());
// Se non hai gestito l'evento, passa al gestore predefinito
scrcpy.sendKeyevent(keyCode);
if(scrcpy != null)
scrcpy.sendKeyevent(keyCode);
return super.onKeyDown(keyCode, event);
}

Expand Down Expand Up @@ -625,6 +626,10 @@ private void licenseReply(String response) {
private void licenseRequest() {
runOnUiThread(() -> {
final EditText editText_patreon = findViewById(R.id.editText_patreon);
if(editText_patreon == null) {
handler.postDelayed(licenseRunnable, 30000); // 30 seconds delay
return;
}
String userEmail = editText_patreon.getText().toString();
if(userEmail.length() == 0) {
handler.postDelayed(licenseRunnable, 30000); // 30 seconds delay
Expand Down
150 changes: 150 additions & 0 deletions server/src/main/java/org/cagnulein/android_remote/CleanUp.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package org.cagnulein.android_remote;

import android.util.Log;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;

/**
* Handle the cleanup of scrcpy, even if the main process is killed.
* <p>
* This is useful to restore some state when scrcpy is closed, even on device disconnection (which kills the scrcpy process).
*/
public final class CleanUp {

private static final int MSG_TYPE_MASK = 0b11;
private static final int MSG_TYPE_RESTORE_STAY_ON = 0;
private static final int MSG_TYPE_DISABLE_SHOW_TOUCHES = 1;
private static final int MSG_TYPE_RESTORE_NORMAL_POWER_MODE = 2;
private static final int MSG_TYPE_POWER_OFF_SCREEN = 3;

private static final int MSG_PARAM_SHIFT = 2;

private final OutputStream out;

public CleanUp(OutputStream out) {
this.out = out;
}

public static CleanUp configure(int displayId) throws IOException {
String[] cmd = {"app_process", "/", CleanUp.class.getName(), String.valueOf(displayId)};

ProcessBuilder builder = new ProcessBuilder(cmd);
builder.environment().put("CLASSPATH", Server.SERVER_PATH);
Process process = builder.start();
return new CleanUp(process.getOutputStream());
}

private boolean sendMessage(int type, int param) {
assert (type & ~MSG_TYPE_MASK) == 0;
int msg = type | param << MSG_PARAM_SHIFT;
try {
out.write(msg);
out.flush();
return true;
} catch (IOException e) {
Log.w("Cleanup", "Could not configure cleanup (type=" + type + ", param=" + param + ")", e);
return false;
}
}

public boolean setRestoreStayOn(int restoreValue) {
// Restore the value (between 0 and 7), -1 to not restore
// <https://developer.android.com/reference/android/provider/Settings.Global#STAY_ON_WHILE_PLUGGED_IN>
assert restoreValue >= -1 && restoreValue <= 7;
return sendMessage(MSG_TYPE_RESTORE_STAY_ON, restoreValue & 0b1111);
}

public boolean setDisableShowTouches(boolean disableOnExit) {
return sendMessage(MSG_TYPE_DISABLE_SHOW_TOUCHES, disableOnExit ? 1 : 0);
}

public boolean setRestoreNormalPowerMode(boolean restoreOnExit) {
return sendMessage(MSG_TYPE_RESTORE_NORMAL_POWER_MODE, restoreOnExit ? 1 : 0);
}

public boolean setPowerOffScreen(boolean powerOffScreenOnExit) {
return sendMessage(MSG_TYPE_POWER_OFF_SCREEN, powerOffScreenOnExit ? 1 : 0);
}

public static void unlinkSelf() {
try {
new File(Server.SERVER_PATH).delete();
} catch (Exception e) {
Ln.e("Could not unlink server", e);
}
}

public static void main(String... args) {
unlinkSelf();

int displayId = Integer.parseInt(args[0]);

int restoreStayOn = -1;
boolean disableShowTouches = false;
boolean restoreNormalPowerMode = false;
boolean powerOffScreen = false;

try {
// Wait for the server to die
int msg;
while ((msg = System.in.read()) != -1) {
int type = msg & MSG_TYPE_MASK;
int param = msg >> MSG_PARAM_SHIFT;
switch (type) {
case MSG_TYPE_RESTORE_STAY_ON:
restoreStayOn = param > 7 ? -1 : param;
break;
case MSG_TYPE_DISABLE_SHOW_TOUCHES:
disableShowTouches = param != 0;
break;
case MSG_TYPE_RESTORE_NORMAL_POWER_MODE:
restoreNormalPowerMode = param != 0;
break;
case MSG_TYPE_POWER_OFF_SCREEN:
powerOffScreen = param != 0;
break;
default:
Ln.w("Unexpected msg type: " + type);
break;
}
}
} catch (IOException e) {
// Expected when the server is dead
}

Ln.i("Cleaning up");

if (disableShowTouches) {
Ln.i("Disabling \"show touches\"");
try {
Settings.putValue(Settings.TABLE_SYSTEM, "show_touches", "0");
} catch (SettingsException e) {
Ln.e("Could not restore \"show_touches\"", e);
}
}

if (restoreStayOn != -1) {
Ln.i("Restoring \"stay awake\"");
try {
Settings.putValue(Settings.TABLE_GLOBAL, "stay_on_while_plugged_in", String.valueOf(restoreStayOn));
} catch (SettingsException e) {
Ln.e("Could not restore \"stay_on_while_plugged_in\"", e);
}
}

/* VIOLA
if (Device.isScreenOn()) {
if (powerOffScreen) {
Ln.i("Power off screen");
Device.powerOffScreen(displayId);
} else if (restoreNormalPowerMode) {
Ln.i("Restoring normal power mode");
Device.setScreenPowerMode(Device.POWER_MODE_NORMAL);
}
}*/

System.exit(0);
}
}
43 changes: 43 additions & 0 deletions server/src/main/java/org/cagnulein/android_remote/Command.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.cagnulein.android_remote;

import java.io.IOException;
import java.util.Arrays;
import java.util.Scanner;

public final class Command {
private Command() {
// not instantiable
}

public static void exec(String... cmd) throws IOException, InterruptedException {
Process process = Runtime.getRuntime().exec(cmd);
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new IOException("Command " + Arrays.toString(cmd) + " returned with value " + exitCode);
}
}

public static String execReadLine(String... cmd) throws IOException, InterruptedException {
String result = null;
Process process = Runtime.getRuntime().exec(cmd);
Scanner scanner = new Scanner(process.getInputStream());
if (scanner.hasNextLine()) {
result = scanner.nextLine();
}
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new IOException("Command " + Arrays.toString(cmd) + " returned with value " + exitCode);
}
return result;
}

public static String execReadOutput(String... cmd) throws IOException, InterruptedException {
Process process = Runtime.getRuntime().exec(cmd);
String output = IO.toString(process.getInputStream());
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new IOException("Command " + Arrays.toString(cmd) + " returned with value " + exitCode);
}
return output;
}
}
53 changes: 53 additions & 0 deletions server/src/main/java/org/cagnulein/android_remote/FakeContext.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.cagnulein.android_remote;

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 {

public static final String PACKAGE_NAME = "com.android.shell";
public static final int ROOT_UID = 0; // Like android.os.Process.ROOT_UID, but before API 29

private static final FakeContext INSTANCE = new FakeContext();

public static FakeContext get() {
return INSTANCE;
}

private FakeContext() {
super(Workarounds.getSystemContext());
}

@Override
public String getPackageName() {
return PACKAGE_NAME;
}

@Override
public String getOpPackageName() {
return PACKAGE_NAME;
}

@TargetApi(Build.VERSION_CODES.S)
@Override
public AttributionSource getAttributionSource() {
AttributionSource.Builder builder = new AttributionSource.Builder(Process.SHELL_UID);
builder.setPackageName(PACKAGE_NAME);
return builder.build();
}

// @Override to be added on SDK upgrade for Android 14
@SuppressWarnings("unused")
public int getDeviceId() {
return 0;
}

@Override
public Context getApplicationContext() {
return this;
}
}
58 changes: 58 additions & 0 deletions server/src/main/java/org/cagnulein/android_remote/IO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.cagnulein.android_remote;

import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;

import com.genymobile.scrcpy.BuildConfig;

import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.Scanner;

public final class IO {
private IO() {
// not instantiable
}

public static void writeFully(FileDescriptor fd, ByteBuffer from) throws IOException {
// ByteBuffer position is not updated as expected by Os.write() on old Android versions, so
// count the remaining bytes manually.
// See <https://github.com/Genymobile/scrcpy/issues/291>.
int remaining = from.remaining();
while (remaining > 0) {
try {
int w = Os.write(fd, from);
if (BuildConfig.DEBUG && w < 0) {
// w should not be negative, since an exception is thrown on error
throw new AssertionError("Os.write() returned a negative value (" + w + ")");
}
remaining -= w;
} catch (ErrnoException e) {
if (e.errno != OsConstants.EINTR) {
throw new IOException(e);
}
}
}
}

public static void writeFully(FileDescriptor fd, byte[] buffer, int offset, int len) throws IOException {
writeFully(fd, ByteBuffer.wrap(buffer, offset, len));
}

public static String toString(InputStream inputStream) {
StringBuilder builder = new StringBuilder();
Scanner scanner = new Scanner(inputStream);
while (scanner.hasNextLine()) {
builder.append(scanner.nextLine()).append('\n');
}
return builder.toString();
}

public static boolean isBrokenPipe(IOException e) {
Throwable cause = e.getCause();
return cause instanceof ErrnoException && ((ErrnoException) cause).errno == OsConstants.EPIPE;
}
}
25 changes: 25 additions & 0 deletions server/src/main/java/org/cagnulein/android_remote/Server.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,36 @@
package org.cagnulein.android_remote;

import android.os.BatteryManager;

import java.io.File;
import java.io.IOException;

public final class Server {

private static String ip = null;
public static final String SERVER_PATH;

private Server() {
// not instantiable
}

static {
String[] classPaths = System.getProperty("java.class.path").split(File.pathSeparator);
// By convention, scrcpy is always executed with the absolute path of scrcpy-server.jar as the first item in the classpath
SERVER_PATH = classPaths[0];
}


private static void scrcpy(Options options) throws IOException {
final Device device = new Device(options);

String oldValue = "1800000";
try {
oldValue = Settings.getAndPutValue(Settings.TABLE_SYSTEM, "screen_off_timeout", String.valueOf(1800000));
} catch (SettingsException e) {
Ln.e("Could not change \"screen_off_timeout\"", e);
}

try (DroidConnection connection = DroidConnection.open(ip)) {
ScreenEncoder screenEncoder = new ScreenEncoder(options.getBitRate());

Expand All @@ -27,6 +46,12 @@ private static void scrcpy(Options options) throws IOException {
Ln.d("Screen streaming stopped");

}

try {
Settings.getAndPutValue(Settings.TABLE_SYSTEM, "screen_off_timeout", oldValue);
} catch (SettingsException e) {
Ln.e("Could not change \"screen_off_timeout\"", e);
}
}
}

Expand Down
Loading

0 comments on commit 788601d

Please sign in to comment.