Add shortcut to expand/collapse notification panel

Use Ctrl+n to expand, Ctrl+Shift+n to collapse.

Fixes <https://github.com/Genymobile/scrcpy/issues/392>
This commit is contained in:
Romain Vimont 2019-02-26 20:35:37 +01:00
parent 1c1fe5ec53
commit 7d10ec2b5a
9 changed files with 129 additions and 22 deletions

View file

@ -267,6 +267,8 @@ you are interested, see [issue 14].
| click on `VOLUME_DOWN` | `Ctrl`+`↓` _(down)_ (`Cmd`+`↓` on MacOS) | | click on `VOLUME_DOWN` | `Ctrl`+`↓` _(down)_ (`Cmd`+`↓` on MacOS) |
| click on `POWER` | `Ctrl`+`p` | | click on `POWER` | `Ctrl`+`p` |
| turn screen on | _Right-click²_ | | turn screen on | _Right-click²_ |
| expand notification panel | `Ctrl`+`n` |
| collapse notification panel | `Ctrl`+`Shift`+`n` |
| paste computer clipboard to device | `Ctrl`+`v` | | paste computer clipboard to device | `Ctrl`+`v` |
| enable/disable FPS counter (on stdout) | `Ctrl`+`i` | | enable/disable FPS counter (on stdout) | `Ctrl`+`i` |

View file

@ -20,7 +20,11 @@ enum control_event_type {
CONTROL_EVENT_TYPE_COMMAND, CONTROL_EVENT_TYPE_COMMAND,
}; };
#define CONTROL_EVENT_COMMAND_BACK_OR_SCREEN_ON 0 enum control_event_command {
CONTROL_EVENT_COMMAND_BACK_OR_SCREEN_ON,
CONTROL_EVENT_COMMAND_EXPAND_NOTIFICATION_PANEL,
CONTROL_EVENT_COMMAND_COLLAPSE_NOTIFICATION_PANEL,
};
struct control_event { struct control_event {
enum control_event_type type; enum control_event_type type;
@ -44,7 +48,7 @@ struct control_event {
Sint32 vscroll; Sint32 vscroll;
} scroll_event; } scroll_event;
struct { struct {
int action; enum control_event_command action;
} command_event; } command_event;
}; };
}; };

View file

@ -94,6 +94,26 @@ static void press_back_or_turn_screen_on(struct controller *controller) {
} }
} }
static void expand_notification_panel(struct controller *controller) {
struct control_event control_event;
control_event.type = CONTROL_EVENT_TYPE_COMMAND;
control_event.command_event.action = CONTROL_EVENT_COMMAND_EXPAND_NOTIFICATION_PANEL;
if (!controller_push_event(controller, &control_event)) {
LOGW("Cannot expand notification panel");
}
}
static void collapse_notification_panel(struct controller *controller) {
struct control_event control_event;
control_event.type = CONTROL_EVENT_TYPE_COMMAND;
control_event.command_event.action = CONTROL_EVENT_COMMAND_COLLAPSE_NOTIFICATION_PANEL;
if (!controller_push_event(controller, &control_event)) {
LOGW("Cannot collapse notification panel");
}
}
static void switch_fps_counter_state(struct frames *frames) { static void switch_fps_counter_state(struct frames *frames) {
mutex_lock(frames->mutex); mutex_lock(frames->mutex);
if (frames->fps_counter.started) { if (frames->fps_counter.started) {
@ -162,47 +182,42 @@ void input_manager_process_key(struct input_manager *input_manager,
// capture all Ctrl events // capture all Ctrl events
if (ctrl | meta) { if (ctrl | meta) {
SDL_bool shift = event->keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT);
if (shift) {
// currently, there is no shortcut involving SHIFT
return;
}
SDL_Keycode keycode = event->keysym.sym; SDL_Keycode keycode = event->keysym.sym;
int action = event->type == SDL_KEYDOWN ? ACTION_DOWN : ACTION_UP; int action = event->type == SDL_KEYDOWN ? ACTION_DOWN : ACTION_UP;
SDL_bool repeat = event->repeat; SDL_bool repeat = event->repeat;
SDL_bool shift = event->keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT);
switch (keycode) { switch (keycode) {
case SDLK_h: case SDLK_h:
if (ctrl && !meta && !repeat) { if (ctrl && !meta && !shift && !repeat) {
action_home(input_manager->controller, action); action_home(input_manager->controller, action);
} }
return; return;
case SDLK_b: // fall-through case SDLK_b: // fall-through
case SDLK_BACKSPACE: case SDLK_BACKSPACE:
if (ctrl && !meta && !repeat) { if (ctrl && !meta && !shift && !repeat) {
action_back(input_manager->controller, action); action_back(input_manager->controller, action);
} }
return; return;
case SDLK_s: case SDLK_s:
if (ctrl && !meta && !repeat) { if (ctrl && !meta && !shift && !repeat) {
action_app_switch(input_manager->controller, action); action_app_switch(input_manager->controller, action);
} }
return; return;
case SDLK_m: case SDLK_m:
if (ctrl && !meta && !repeat) { if (ctrl && !meta && !shift && !repeat) {
action_menu(input_manager->controller, action); action_menu(input_manager->controller, action);
} }
return; return;
case SDLK_p: case SDLK_p:
if (ctrl && !meta && !repeat) { if (ctrl && !meta && !shift && !repeat) {
action_power(input_manager->controller, action); action_power(input_manager->controller, action);
} }
return; return;
case SDLK_DOWN: case SDLK_DOWN:
#ifdef __APPLE__ #ifdef __APPLE__
if (!ctrl && meta) { if (!ctrl && meta && !shift) {
#else #else
if (ctrl && !meta) { if (ctrl && !meta && !shift) {
#endif #endif
// forward repeated events // forward repeated events
action_volume_down(input_manager->controller, action); action_volume_down(input_manager->controller, action);
@ -210,39 +225,53 @@ void input_manager_process_key(struct input_manager *input_manager,
return; return;
case SDLK_UP: case SDLK_UP:
#ifdef __APPLE__ #ifdef __APPLE__
if (!ctrl && meta) { if (!ctrl && meta && !shift) {
#else #else
if (ctrl && !meta) { if (ctrl && !meta && !shift) {
#endif #endif
// forward repeated events // forward repeated events
action_volume_up(input_manager->controller, action); action_volume_up(input_manager->controller, action);
} }
return; return;
case SDLK_v: case SDLK_v:
if (ctrl && !meta && !repeat && event->type == SDL_KEYDOWN) { if (ctrl && !meta && !shift && !repeat
&& event->type == SDL_KEYDOWN) {
clipboard_paste(input_manager->controller); clipboard_paste(input_manager->controller);
} }
return; return;
case SDLK_f: case SDLK_f:
if (ctrl && !meta && !repeat && event->type == SDL_KEYDOWN) { if (ctrl && !meta && !shift && !repeat
&& event->type == SDL_KEYDOWN) {
screen_switch_fullscreen(input_manager->screen); screen_switch_fullscreen(input_manager->screen);
} }
return; return;
case SDLK_x: case SDLK_x:
if (ctrl && !meta && !repeat && event->type == SDL_KEYDOWN) { if (ctrl && !meta && !shift && !repeat
&& event->type == SDL_KEYDOWN) {
screen_resize_to_fit(input_manager->screen); screen_resize_to_fit(input_manager->screen);
} }
return; return;
case SDLK_g: case SDLK_g:
if (ctrl && !meta && !repeat && event->type == SDL_KEYDOWN) { if (ctrl && !meta && !shift && !repeat
&& event->type == SDL_KEYDOWN) {
screen_resize_to_pixel_perfect(input_manager->screen); screen_resize_to_pixel_perfect(input_manager->screen);
} }
return; return;
case SDLK_i: case SDLK_i:
if (ctrl && !meta && !repeat && event->type == SDL_KEYDOWN) { if (ctrl && !meta && !shift && !repeat
&& event->type == SDL_KEYDOWN) {
switch_fps_counter_state(input_manager->frames); switch_fps_counter_state(input_manager->frames);
} }
return; return;
case SDLK_n:
if (ctrl && !meta && !repeat && event->type == SDL_KEYDOWN) {
if (shift) {
collapse_notification_panel(input_manager->controller);
} else {
expand_notification_panel(input_manager->controller);
}
}
return;
} }
return; return;

View file

@ -120,6 +120,12 @@ static void usage(const char *arg0) {
" Right-click (when screen is off)\n" " Right-click (when screen is off)\n"
" turn screen on\n" " turn screen on\n"
"\n" "\n"
" Ctrl+n\n"
" expand notification panel\n"
"\n"
" Ctrl+Shift+n\n"
" collapse notification panel\n"
"\n"
" Ctrl+v\n" " Ctrl+v\n"
" paste computer clipboard to device\n" " paste computer clipboard to device\n"
"\n" "\n"

View file

@ -12,6 +12,8 @@ public final class ControlEvent {
public static final int TYPE_COMMAND = 4; public static final int TYPE_COMMAND = 4;
public static final int COMMAND_BACK_OR_SCREEN_ON = 0; public static final int COMMAND_BACK_OR_SCREEN_ON = 0;
public static final int COMMAND_EXPAND_NOTIFICATION_PANEL = 1;
public static final int COMMAND_COLLAPSE_NOTIFICATION_PANEL = 2;
private int type; private int type;
private String text; private String text;

View file

@ -132,6 +132,14 @@ public final class Device {
this.rotationListener = rotationListener; this.rotationListener = rotationListener;
} }
public void expandNotificationPanel() {
serviceManager.getStatusBarManager().expandNotificationsPanel();
}
public void collapsePanels() {
serviceManager.getStatusBarManager().collapsePanels();
}
static Rect flipRect(Rect crop) { static Rect flipRect(Rect crop) {
return new Rect(crop.top, crop.left, crop.bottom, crop.right); return new Rect(crop.top, crop.left, crop.bottom, crop.right);
} }

View file

@ -1,6 +1,7 @@
package com.genymobile.scrcpy; package com.genymobile.scrcpy;
import com.genymobile.scrcpy.wrappers.InputManager; import com.genymobile.scrcpy.wrappers.InputManager;
import com.genymobile.scrcpy.wrappers.ServiceManager;
import android.graphics.Point; import android.graphics.Point;
import android.os.SystemClock; import android.os.SystemClock;
@ -172,6 +173,12 @@ public class EventController {
switch (action) { switch (action) {
case ControlEvent.COMMAND_BACK_OR_SCREEN_ON: case ControlEvent.COMMAND_BACK_OR_SCREEN_ON:
return pressBackOrTurnScreenOn(); return pressBackOrTurnScreenOn();
case ControlEvent.COMMAND_EXPAND_NOTIFICATION_PANEL:
device.expandNotificationPanel();
return true;
case ControlEvent.COMMAND_COLLAPSE_NOTIFICATION_PANEL:
device.collapsePanels();
return true;
default: default:
Ln.w("Unsupported command: " + action); Ln.w("Unsupported command: " + action);
} }

View file

@ -14,6 +14,7 @@ public final class ServiceManager {
private DisplayManager displayManager; private DisplayManager displayManager;
private InputManager inputManager; private InputManager inputManager;
private PowerManager powerManager; private PowerManager powerManager;
private StatusBarManager statusBarManager;
public ServiceManager() { public ServiceManager() {
try { try {
@ -60,4 +61,11 @@ public final class ServiceManager {
} }
return powerManager; return powerManager;
} }
public StatusBarManager getStatusBarManager() {
if (statusBarManager == null) {
statusBarManager = new StatusBarManager(getService("statusbar", "com.android.internal.statusbar.IStatusBarService"));
}
return statusBarManager;
}
} }

View file

@ -0,0 +1,41 @@
package com.genymobile.scrcpy.wrappers;
import android.annotation.SuppressLint;
import android.os.IInterface;
import android.view.InputEvent;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class StatusBarManager {
private final IInterface manager;
private final Method expandNotificationsPanelMethod;
private final Method collapsePanelsMethod;
public StatusBarManager(IInterface manager) {
this.manager = manager;
try {
expandNotificationsPanelMethod = manager.getClass().getMethod("expandNotificationsPanel");
collapsePanelsMethod = manager.getClass().getMethod("collapsePanels");
} catch (NoSuchMethodException e) {
throw new AssertionError(e);
}
}
public void expandNotificationsPanel() {
try {
expandNotificationsPanelMethod.invoke(manager);
} catch (InvocationTargetException | IllegalAccessException e) {
throw new AssertionError(e);
}
}
public void collapsePanels() {
try {
collapsePanelsMethod.invoke(manager);
} catch (InvocationTargetException | IllegalAccessException e) {
throw new AssertionError(e);
}
}
}