Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Intents on start & exit #2265

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,73 @@ The target directory can be changed on start:
scrcpy --push-target=/sdcard/Download/
```

### Android features

#### Announce scrcpy state of execution

**(Advanced feature)**

Turn on the announcement of scrcpy current status.
Those announcements are done using the [broadcast intents] feature of Android.
If no value is provided with this argument, all intents are turned on.

[broadcast intents]: https://developer.android.com/reference/android/content/Intent

Currently, the only events that exist are:

| [Intent Action] | Description |
|:--------------------------------|:----------------------------------------------|
| `com.genymobile.scrcpy.START` | scrcpy starts
| `com.genymobile.scrcpy.STOP` | scrcpy stopped and cleaned up (best effort)


[Intent Action]: https://developer.android.com/reference/android/content/Intent#setAction(java.lang.String)
[Intent Extras]: https://developer.android.com/reference/android/content/Intent#putExtra(java.lang.String,%20android.os.Parcelable)


**Important:**
1. `stop` **may not happen** in specific cases.
Examples:
1. Debugging is turned off.
2. Scrcpy cleanup process is killed by android or another app.
2. This option is intended for advanced users. By using this
feature, all apps on your phone will know scrcpy has connected
Unless that is what you want, and you know what that means
do not use this feature
3. In order for this argument to produce visible results you must create
some automation to listen to android broadcast intents.
Such as with your own app or with automation apps such as [Tasker].


Following [Android intent rules], all intents fields/keys prefixed with:
`com.genymobile.scrcpy.`
In case of Actions, it is followed by the intent name in caps. For example,
the 'start' intent has the action:
`com.genymobile.scrcpy.STARTED`


[Android intent rules]: https://developer.android.com/reference/android/content/Intent#setAction(java.lang.String)


For convinience with automation tools such as [Tasker], scrcpy also writes to the data field of the intents.
The scheme is `scrcpy-status`.

[Tasker]: https://tasker.joaoapps.com/

**Example usages:**

```bash
scrcpy --broadcast-intents
```

```bash
scrcpy --broadcast-intents=start
```

```bash
scrcpy --broadcast-intents start,cleaned
```


### Audio forwarding

Expand Down
40 changes: 40 additions & 0 deletions app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,38 @@ scrcpy_print_usage(const char *arg0) {
" Unit suffixes are supported: 'K' (x1000) and 'M' (x1000000).\n"
" Default is " STR(DEFAULT_BIT_RATE) ".\n"
"\n"
" --broadcast-intents [value[, ...]]\n"
" (Advanced feature)\n"
" Turn on the broadcast of intents with the status of scrcpy \n"
" options are: start, stop, cleaned\n"
" Each of these will arm the corresponding intent\n"
" start: announce finished setting up\n"
" stop: announce shut down started (best effort)\n"
" cleaned: announce cleanup finished (best effort)\n"
" \n"
" If you ommit the value, all intents are turned on\n"
" \n"
" All intents have the action and extra fields prefixed with: \n"
" com.genymobile.scrcpy.\n"
" Which is then followed by the intent name in caps. For example,\n"
" the 'start' intent has the action:\n"
" com.genymobile.scrcpy.START\n"
"\n"
" There are two boolean extras use to ease\n"
" the parsing process of the intents:\n"
" 1. com.genymobile.scrcpy.STARTUP if present and true,\n"
" scrcpy is starting up.\n"
" 2. com.genymobile.scrcpy.SHUTDOWN if present and true,\n"
" scrcpy is shutting down.\n"
" \n"
" Notes:\n"
" 1. stop and cleaned may not happen in specific cases. For example, \n"
" if debugging is turned off, scrcpy process is immediately killed \n"
" 2. This option is intended for advanced users. By using this \n"
" feature, all apps on your phone will know scrcpy has connected\n"
" Unless that is what you want, and you know what that means\n"
" do not use this feature\n"
"\n"
" --codec-options key[:type]=value[,...]\n"
" Set a list of comma-separated key:type=value options for the\n"
" device encoder.\n"
Expand Down Expand Up @@ -656,6 +688,7 @@ guess_record_format(const char *filename) {
return 0;
}


#define OPT_RENDER_EXPIRED_FRAMES 1000
#define OPT_WINDOW_TITLE 1001
#define OPT_PUSH_TARGET 1002
Expand Down Expand Up @@ -684,6 +717,7 @@ guess_record_format(const char *filename) {
#define OPT_ENCODER_NAME 1025
#define OPT_POWER_OFF_ON_CLOSE 1026
#define OPT_V4L2_SINK 1027
#define OPT_INTENT_BROADCAST 1028

bool
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
Expand Down Expand Up @@ -739,6 +773,8 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
OPT_WINDOW_BORDERLESS},
{"power-off-on-close", no_argument, NULL,
OPT_POWER_OFF_ON_CLOSE},
{"intent-broadcast", optional_argument, NULL,
OPT_INTENT_BROADCAST},
{NULL, 0, NULL, 0 },
};

Expand Down Expand Up @@ -917,6 +953,10 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
opts->v4l2_device = optarg;
break;
#endif

case OPT_INTENT_BROADCAST:
opts->intent_broadcast = true;
break;
default:
// getopt prints the error message on stderr
return false;
Expand Down
1 change: 1 addition & 0 deletions app/src/scrcpy.c
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ scrcpy(const struct scrcpy_options *options) {
.encoder_name = options->encoder_name,
.force_adb_forward = options->force_adb_forward,
.power_off_on_close = options->power_off_on_close,
.intent_broadcast = options->intent_broadcast,
};
if (!server_start(&s->server, &params)) {
goto end;
Expand Down
9 changes: 9 additions & 0 deletions app/src/scrcpy.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ struct sc_port_range {

#define SC_WINDOW_POSITION_UNDEFINED (-0x8000)


enum sc_intent_broadcast {
SC_INTENT_BROADCAST_START = 1 << 0,
SC_INTENT_BROADCAST_STOP = 1 << 31,
};


struct scrcpy_options {
const char *serial;
const char *crop;
Expand Down Expand Up @@ -93,6 +100,7 @@ struct scrcpy_options {
bool forward_all_clicks;
bool legacy_paste;
bool power_off_on_close;
bool intent_broadcast;
};

#define SCRCPY_OPTIONS_DEFAULT { \
Expand Down Expand Up @@ -141,6 +149,7 @@ struct scrcpy_options {
.forward_all_clicks = false, \
.legacy_paste = false, \
.power_off_on_close = false, \
.intent_broadcast = false, \
}

bool
Expand Down
1 change: 1 addition & 0 deletions app/src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ execute_server(struct server *server, const struct server_params *params) {
params->codec_options ? params->codec_options : "-",
params->encoder_name ? params->encoder_name : "-",
params->power_off_on_close ? "true" : "false",
params->intent_broadcast ? "true" : "false",
};
#ifdef SERVER_DEBUGGER
LOGI("Server debugger waiting for a client on device port "
Expand Down
1 change: 1 addition & 0 deletions app/src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ struct server_params {
bool stay_awake;
bool force_adb_forward;
bool power_off_on_close;
bool intent_broadcast;
};

// init default values
Expand Down
33 changes: 28 additions & 5 deletions server/src/main/java/com/genymobile/scrcpy/CleanUp.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.genymobile.scrcpy;

import android.content.Intent;
import android.net.Uri;
import com.genymobile.scrcpy.wrappers.ContentProvider;
import com.genymobile.scrcpy.wrappers.ServiceManager;

Expand Down Expand Up @@ -34,9 +36,10 @@ public Config[] newArray(int size) {
}
};

private static final int FLAG_DISABLE_SHOW_TOUCHES = 1;
private static final int FLAG_RESTORE_NORMAL_POWER_MODE = 2;
private static final int FLAG_POWER_OFF_SCREEN = 4;
private static final int FLAG_DISABLE_SHOW_TOUCHES = 1 << 0;
private static final int FLAG_RESTORE_NORMAL_POWER_MODE = 1 << 1;
private static final int FLAG_POWER_OFF_SCREEN = 1 << 2;
private static final int FLAG_BROADCAST_STOPPED = 1 << 3;

private int displayId;

Expand All @@ -47,6 +50,7 @@ public Config[] newArray(int size) {
private boolean disableShowTouches;
private boolean restoreNormalPowerMode;
private boolean powerOffScreen;
private boolean broadcastStopped;

public Config() {
// Default constructor, the fields are initialized by CleanUp.configure()
Expand All @@ -59,6 +63,7 @@ protected Config(Parcel in) {
disableShowTouches = (options & FLAG_DISABLE_SHOW_TOUCHES) != 0;
restoreNormalPowerMode = (options & FLAG_RESTORE_NORMAL_POWER_MODE) != 0;
powerOffScreen = (options & FLAG_POWER_OFF_SCREEN) != 0;
broadcastStopped = (options & FLAG_BROADCAST_STOPPED) != 0;
}

@Override
Expand All @@ -75,11 +80,14 @@ public void writeToParcel(Parcel dest, int flags) {
if (powerOffScreen) {
options |= FLAG_POWER_OFF_SCREEN;
}
if (broadcastStopped) {
options |= FLAG_BROADCAST_STOPPED;
}
dest.writeByte(options);
}

private boolean hasWork() {
return disableShowTouches || restoreStayOn != -1 || restoreNormalPowerMode || powerOffScreen;
return disableShowTouches || restoreStayOn != -1 || restoreNormalPowerMode || powerOffScreen || broadcastStopped;
}

@Override
Expand Down Expand Up @@ -117,14 +125,17 @@ private CleanUp() {
// not instantiable
}

public static void configure(int displayId, int restoreStayOn, boolean disableShowTouches, boolean restoreNormalPowerMode, boolean powerOffScreen)
public static void configure(int displayId, int restoreStayOn, boolean disableShowTouches, boolean restoreNormalPowerMode,
boolean powerOffScreen, boolean broadcastStopped
)
throws IOException {
Config config = new Config();
config.displayId = displayId;
config.disableShowTouches = disableShowTouches;
config.restoreStayOn = restoreStayOn;
config.restoreNormalPowerMode = restoreNormalPowerMode;
config.powerOffScreen = powerOffScreen;
config.broadcastStopped = broadcastStopped;

if (config.hasWork()) {
startProcess(config);
Expand Down Expand Up @@ -187,5 +198,17 @@ public static void main(String... args) {
Device.setScreenPowerMode(Device.POWER_MODE_NORMAL);
}
}

if(config.broadcastStopped){
Ln.i("Announce stopped");
announceScrcpyStopped();
}
}

private static void announceScrcpyStopped() {

Intent cleaned = new Intent(Server.scrcpyPrefix("STOPPED"));
cleaned.setData(Uri.parse("scrcpy-status:stopped"));
Device.sendBroadcast(cleaned);
}
}
5 changes: 5 additions & 0 deletions server/src/main/java/com/genymobile/scrcpy/Device.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.genymobile.scrcpy.wrappers.WindowManager;

import android.content.IOnPrimaryClipChangedListener;
import android.content.Intent;
import android.graphics.Rect;
import android.os.Build;
import android.os.IBinder;
Expand Down Expand Up @@ -299,4 +300,8 @@ public static void rotateDevice() {
public static ContentProvider createSettingsProvider() {
return SERVICE_MANAGER.getActivityManager().createSettingsProvider();
}

public static void sendBroadcast(Intent intent) {
SERVICE_MANAGER.getActivityManager().sendBroadcast(intent);
}
}
12 changes: 12 additions & 0 deletions server/src/main/java/com/genymobile/scrcpy/Options.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import android.graphics.Rect;

import java.util.BitSet;
import java.util.EnumSet;

public class Options {
private Ln.Level logLevel;
private int maxSize;
Expand All @@ -18,6 +21,7 @@ public class Options {
private String codecOptions;
private String encoderName;
private boolean powerOffScreenOnClose;
private boolean broadcastIntents;

public Ln.Level getLogLevel() {
return logLevel;
Expand Down Expand Up @@ -83,6 +87,10 @@ public void setSendFrameMeta(boolean sendFrameMeta) {
this.sendFrameMeta = sendFrameMeta;
}

public void setBroadcastIntents(boolean broadcastIntents) {
this.broadcastIntents = broadcastIntents;
}

public boolean getControl() {
return control;
}
Expand Down Expand Up @@ -138,4 +146,8 @@ public void setPowerOffScreenOnClose(boolean powerOffScreenOnClose) {
public boolean getPowerOffScreenOnClose() {
return this.powerOffScreenOnClose;
}

public boolean getBroadcastIntents() {
return broadcastIntents;
}
}
Loading