Skip to content

Commit

Permalink
feat: use TYPE_SECURE_SYSTEM_OVERLAY for showing pointer over system …
Browse files Browse the repository at this point in the history
…windows
  • Loading branch information
Xtr126 committed Feb 24, 2024
1 parent 80ec2cb commit 7f23fef
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 74 deletions.
2 changes: 0 additions & 2 deletions app/src/main/aidl/xtr/keymapper/IRemoteServiceCallback.aidl
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import xtr.keymapper.keymap.KeymapProfile;
interface IRemoteServiceCallback {
void launchEditor();
void alertMouseAimActivated();
void cursorSetX(int x);
void cursorSetY(int y);
KeymapProfile requestKeymapProfile();
KeymapConfig requestKeymapConfig();
void switchProfiles();
Expand Down
55 changes: 2 additions & 53 deletions app/src/main/java/xtr/keymapper/TouchPointer.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@
public class TouchPointer extends Service {
private final IBinder binder = new TouchPointerBinder();
public MainActivity.Callback activityCallback;
private WindowManager mWindowManager;
public View cursorView;
private IRemoteService mService;
public String selectedProfile = null;
private final Handler mHandler = new Handler(Looper.getMainLooper());
Expand All @@ -53,32 +51,6 @@ public TouchPointer getService() {
}
}

// set the layout parameters of the cursor
private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
// Don't let the cursor grab the input focus
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
WindowManager.LayoutParams.FLAG_FULLSCREEN |
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
// Make the underlying application window visible
// through the cursor
PixelFormat.TRANSLUCENT);

private void showCursor() {
super.onCreate();
LayoutInflater layoutInflater = getSystemService(LayoutInflater.class);
mWindowManager = getSystemService(WindowManager.class);
// Inflate the layout for the cursor
cursorView = CursorBinding.inflate(layoutInflater).getRoot();

if(cursorView.getWindowToken()==null)
if (cursorView.getParent() == null)
mWindowManager.addView(cursorView, mParams);
}

@Override
public IBinder onBind(Intent intent) {
return binder;
Expand Down Expand Up @@ -111,8 +83,6 @@ public int onStartCommand(Intent i, int flags, int startId) {
startForeground(2, notification);
}

if (cursorView == null) showCursor();

// Launch default profile
this.selectedProfile = "Default";
KeymapProfile keymapProfile = new KeymapProfiles(this).getProfile(selectedProfile);
Expand Down Expand Up @@ -146,6 +116,7 @@ private void connectRemoteService(KeymapProfile profile) {
return;
}
KeymapConfig keymapConfig = new KeymapConfig(this);
WindowManager mWindowManager = getSystemService(WindowManager.class);
Display display = mWindowManager.getDefaultDisplay();
Point size = new Point();
display.getRealSize(size); // TODO: getRealSize() deprecated in API level 31
Expand Down Expand Up @@ -177,18 +148,12 @@ private void connectRemoteService(KeymapProfile profile) {

@Override
public void onDestroy() {
if (cursorView != null) {
if (cursorView.isAttachedToWindow())
mWindowManager.removeView(cursorView);
cursorView.invalidate();
}
if (mService != null) try {
mService.unregisterActivityObserver(mActivityObserverCallback);
mService.stopServer();
} catch (Exception e) {
Log.e("stopServer", e.toString(), e);
}
cursorView = null;
mService = null;
activityCallback = null;
super.onDestroy();
Expand All @@ -214,16 +179,6 @@ public void alertMouseAimActivated() {
).show());
}

@Override
public void cursorSetX(int x) {
mHandler.post(() -> cursorView.setX(x));
}

@Override
public void cursorSetY(int y) {
mHandler.post(() -> cursorView.setY(y));
}

@Override
public KeymapProfile requestKeymapProfile() {
return new KeymapProfiles(TouchPointer.this).getProfile(selectedProfile);
Expand Down Expand Up @@ -272,26 +227,22 @@ private void reloadKeymap() {

@Override
public void onForegroundActivitiesChanged(String packageName) {
if (packageName.equals(lastPackageName) || cursorView == null) return;
if (packageName.equals(lastPackageName)) return;
lastPackageName = packageName;
mWindowManager.removeView(cursorView);
Context context = TouchPointer.this;
KeymapProfiles keymapProfiles = new KeymapProfiles(context);
if (!keymapProfiles.profileExistsWithPackageName(packageName)) {
// No profile found, prompt user to create a new profile
mHandler.post(() -> {
cursorView.setVisibility(View.VISIBLE);
ProfileSelector.showEnableProfileDialog(context, packageName, enabled ->
ProfileSelector.createNewProfileForApp(context, packageName, enabled, profile -> {
TouchPointer.this.selectedProfile = profile;
reloadKeymap();
}));
mWindowManager.addView(cursorView, mParams);
});
} else {
// App specific profiles selection dialog
mHandler.post(() -> {
cursorView.setVisibility(View.VISIBLE);
ProfileSelector.select(context, profile -> {
// Reloading profile
TouchPointer.this.selectedProfile = profile;
Expand All @@ -302,13 +253,11 @@ public void onForegroundActivitiesChanged(String packageName) {
} else {
try {
mService.pauseMouse();
cursorView.setVisibility(View.GONE);
} catch (RemoteException ignored) {
}
Toast.makeText(TouchPointer.this, "Keymapping disabled for " + packageName, Toast.LENGTH_SHORT).show();
}
}, packageName);
mWindowManager.addView(cursorView, mParams);
});
}
}
Expand Down
1 change: 0 additions & 1 deletion app/src/main/java/xtr/keymapper/activity/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,6 @@ public void onServiceConnected(ComponentName className,
TouchPointer.TouchPointerBinder binder = (TouchPointer.TouchPointerBinder) service;
pointerOverlay = binder.getService();
pointerOverlay.activityCallback = mCallback;
setButtonState(pointerOverlay.cursorView == null);
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
Expand Down
24 changes: 7 additions & 17 deletions app/src/main/java/xtr/keymapper/server/InputService.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import android.os.RemoteException;
import android.view.MotionEvent;
import android.view.View;

import xtr.keymapper.IRemoteServiceCallback;
import xtr.keymapper.keymap.KeymapConfig;
Expand All @@ -23,17 +24,19 @@ public class InputService implements IInputInterface {
private final Input input = new Input();
public static final int UP = 0, DOWN = 1, MOVE = 2;
private final IRemoteServiceCallback mCallback;
final int supportsUinput;
int supportsUinput;
boolean stopEvents = false;
boolean pointerUp = false;
private final boolean isWaylandClient;
private final String touchpadInputMode;
private final View cursorView;

public InputService(KeymapProfile profile, KeymapConfig keymapConfig, IRemoteServiceCallback mCallback, int screenWidth, int screenHeight, boolean isWaylandClient) throws RemoteException {
public InputService(KeymapProfile profile, KeymapConfig keymapConfig, IRemoteServiceCallback mCallback, int screenWidth, int screenHeight, View cursorView, boolean isWaylandClient) throws RemoteException {
this.keymapProfile = profile;
this.keymapConfig = keymapConfig;
this.mCallback = mCallback;
this.isWaylandClient = isWaylandClient;
this.cursorView = cursorView;
supportsUinput = initMouseCursor(screenWidth, screenHeight);

this.touchpadInputMode = keymapConfig.touchpadInputMode;
Expand All @@ -47,11 +50,6 @@ else if (touchpadInputMode.equals(KeymapConfig.TOUCHPAD_RELATIVE))

keyEventHandler = new KeyEventHandler(this);
keyEventHandler.init();

if (isWaylandClient) {
mCallback.cursorSetX(0);
mCallback.cursorSetY(0);
}
}

public void injectEvent(float x, float y, int action, int pointerId) {
Expand Down Expand Up @@ -104,22 +102,14 @@ public IRemoteServiceCallback getCallback() {
}

public void moveCursorX(float x) {
if (isWaylandClient) return;
try {
mCallback.cursorSetX((int) x);
} catch (RemoteException ignored) {
}
cursorView.setX(x);
// To avoid conflict with touch input when moving virtual pointer
if (input.pointerCount < 1) cursorSetX((int) x);
else if (input.pointerCount == 1 && pointerUp) cursorSetX((int) x);
}

public void moveCursorY(float y) {
if (isWaylandClient) return;
try {
mCallback.cursorSetY((int) y);
} catch (RemoteException ignored) {
}
cursorView.setY(y);
// To avoid conflict with touch input when moving virtual pointer
if (input.pointerCount < 1) cursorSetY((int) y);
else if (input.pointerCount == 1 && pointerUp) cursorSetY((int) y);
Expand Down
44 changes: 43 additions & 1 deletion app/src/main/java/xtr/keymapper/server/RemoteService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,27 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.PixelFormat;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import xtr.keymapper.ActivityObserver;
import xtr.keymapper.IRemoteService;
import xtr.keymapper.IRemoteServiceCallback;
import xtr.keymapper.OnKeyEventListener;
import xtr.keymapper.R;
import xtr.keymapper.Utils;
import xtr.keymapper.databinding.CursorBinding;
import xtr.keymapper.keymap.KeymapConfig;
import xtr.keymapper.keymap.KeymapProfile;

Expand All @@ -23,6 +34,7 @@ public class RemoteService extends IRemoteService.Stub {
boolean isWaylandClient = false;
private ActivityObserverService activityObserverService;
String nativeLibraryDir = System.getProperty("java.library.path");
private View cursorView;

public RemoteService() {

Expand All @@ -34,6 +46,31 @@ public RemoteService(Context context) {
init(context);
}

public void prepareCursorOverlayWindow(Context context) throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException {
LayoutInflater layoutInflater = context.getSystemService(LayoutInflater.class);
context.setTheme(R.style.Theme_XtMapper);
cursorView = CursorBinding.inflate(layoutInflater).getRoot();;

WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.class.getField("TYPE_SECURE_SYSTEM_OVERLAY").getInt(null),
// Don't let the cursor grab the input focus
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
WindowManager.LayoutParams.FLAG_FULLSCREEN |
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
// Make the underlying application window visible
// through the cursor
PixelFormat.TRANSLUCENT);

Binder sWindowToken = new Binder();
WindowManager windowManager = context.getSystemService(WindowManager.class);
Method setDefaultTokenMethod = windowManager.getClass().getMethod("setDefaultToken", IBinder.class);
setDefaultTokenMethod.invoke(windowManager, sWindowToken);
windowManager.addView(cursorView, params);
}

public RemoteService init(Context context) {
PackageManager pm = context.getPackageManager();
String packageName = context.getPackageName();
Expand All @@ -44,6 +81,11 @@ public RemoteService init(Context context) {
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(e);
}
try {
prepareCursorOverlayWindow(context);
} catch (Exception e) {
Log.e("overlayWindow", e.getMessage(), e);
}
return this;
}

Expand Down Expand Up @@ -109,7 +151,7 @@ public boolean isRoot() {
public void startServer(KeymapProfile profile, KeymapConfig keymapConfig, IRemoteServiceCallback cb, int screenWidth, int screenHeight) throws RemoteException {
if (inputService != null) stopServer();
cb.asBinder().linkToDeath(this::stopServer, 0);
inputService = new InputService(profile, keymapConfig, cb, screenWidth, screenHeight, isWaylandClient);
inputService = new InputService(profile, keymapConfig, cb, screenWidth, screenHeight, cursorView, isWaylandClient);
if (!isWaylandClient) {
inputService.setMouseLock(true);
inputService.openDevice(currentDevice);
Expand Down

0 comments on commit 7f23fef

Please sign in to comment.