Code cleanup
Added LorieService New touch parsing mechanism Added Preferences activity with options: show or not additional keyboard, fullscreen mode toggle, touch input mode toggle, show or not software keyboard if external keyboard is connected
This commit is contained in:
parent
48fd564d21
commit
12b7b00710
@ -5,6 +5,7 @@ Android NDK: current module
|
|||||||
[arm64-v8a] Compile++ : lorie <= client.cpp
|
[arm64-v8a] Compile++ : lorie <= client.cpp
|
||||||
[arm64-v8a] Compile++ : lorie <= clipboard.cpp
|
[arm64-v8a] Compile++ : lorie <= clipboard.cpp
|
||||||
[arm64-v8a] Compile++ : lorie <= compositor.cpp
|
[arm64-v8a] Compile++ : lorie <= compositor.cpp
|
||||||
|
[arm64-v8a] Compile++ : lorie <= egl-helper.cpp
|
||||||
[arm64-v8a] Compile++ : lorie <= message-queue.cpp
|
[arm64-v8a] Compile++ : lorie <= message-queue.cpp
|
||||||
[arm64-v8a] Compile++ : lorie <= renderer.cpp
|
[arm64-v8a] Compile++ : lorie <= renderer.cpp
|
||||||
[arm64-v8a] Compile++ : lorie <= seat.cpp
|
[arm64-v8a] Compile++ : lorie <= seat.cpp
|
||||||
@ -14,5 +15,5 @@ Android NDK: current module
|
|||||||
[arm64-v8a] Compile++ : lorie <= utils.cpp
|
[arm64-v8a] Compile++ : lorie <= utils.cpp
|
||||||
[arm64-v8a] Compile++ : lorie <= wayland.cpp
|
[arm64-v8a] Compile++ : lorie <= wayland.cpp
|
||||||
[arm64-v8a] Compile++ : lorie <= android-app.cpp
|
[arm64-v8a] Compile++ : lorie <= android-app.cpp
|
||||||
[arm64-v8a] Compile++ : lorie <= GLContext.cpp
|
[arm64-v8a] Compile : lorie <= utils.c
|
||||||
[arm64-v8a] SharedLibrary : liblorie.so
|
[arm64-v8a] SharedLibrary : liblorie.so
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:274:6: error: use of undeclared identifier 'jcompositor'; did you mean 'compositor'?
|
|
||||||
if (jcompositor == 0) return;
|
|
||||||
^~~~~~~~~~~
|
|
||||||
compositor
|
|
||||||
/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:271:50: note: 'compositor' declared here
|
|
||||||
jlong compositor, jint type,
|
|
||||||
^
|
|
||||||
/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:275:39: error: use of undeclared identifier 'jcompositor'; did you mean 'compositor'?
|
|
||||||
LorieBackendAndroid *b = fromLong(jcompositor);
|
|
||||||
^~~~~~~~~~~
|
|
||||||
compositor
|
|
||||||
/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:271:50: note: 'compositor' declared here
|
|
||||||
jlong compositor, jint type,
|
|
||||||
^
|
|
||||||
/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:282:9: error: use of undeclared identifier 'key_code'; did you mean 'keyCode'?
|
|
||||||
if (key_code && !characters) {
|
|
||||||
^~~~~~~~
|
|
||||||
keyCode
|
|
||||||
/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:272:49: note: 'keyCode' declared here
|
|
||||||
jint keyCode, jint jshift,
|
|
||||||
^
|
|
||||||
/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:285:7: error: use of undeclared identifier 'xkb_names'
|
|
||||||
if (xkb_names.layout != "us") {
|
|
||||||
^
|
|
||||||
/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:290:10: error: use of undeclared identifier 'key_code'; did you mean 'keyCode'?
|
|
||||||
if (!key_code && characters) {
|
|
||||||
^~~~~~~~
|
|
||||||
keyCode
|
|
||||||
/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:272:49: note: 'keyCode' declared here
|
|
||||||
jint keyCode, jint jshift,
|
|
||||||
^
|
|
||||||
/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:293:26: error: 'xkb_names' is a private member of 'LorieBackendAndroid'
|
|
||||||
if (layout && b->xkb_names.layout != layout) {
|
|
||||||
^
|
|
||||||
/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:45:24: note: declared private here
|
|
||||||
struct xkb_rule_names xkb_names = {0};
|
|
||||||
^
|
|
||||||
/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:298:90: error: use of undeclared identifier 'key_code'; did you mean 'keyCode'?
|
|
||||||
LOGE("Keyboard input: keyCode: %d; eventCode: %d; characters: %s; shift: %d, type: %d", key_code, event_code, characters, shift, type);
|
|
||||||
^~~~~~~~
|
|
||||||
keyCode
|
|
||||||
/home/twaik/TermuxX11/app/src/main/jni/lorie/include/log.h:26:34: note: expanded from macro 'LOGE'
|
|
||||||
#define LOGE(...) LOG(LOG_ERROR, __VA_ARGS__)
|
|
||||||
^
|
|
||||||
/home/twaik/TermuxX11/app/src/main/jni/lorie/include/log.h:20:41: note: expanded from macro 'LOG'
|
|
||||||
#define LOG(prio, ...) LogMessage(prio, __VA_ARGS__)
|
|
||||||
^
|
|
||||||
/home/twaik/TermuxX11/app/src/main/jni/lorie/backend/android/android-app.cpp:272:49: note: 'keyCode' declared here
|
|
||||||
jint keyCode, jint jshift,
|
|
||||||
^
|
|
||||||
7 errors generated.
|
|
||||||
make: *** [/home/twaik/TermuxX11/app/build/intermediates/ndkBuild/debug/obj/local/x86_64/objs-debug/lorie/backend/android/android-app.o] Error 1
|
|
@ -2,4 +2,50 @@ Android NDK: WARNING:/home/twaik/TermuxX11/app/src/main/jni/lorie/Android.mk:lor
|
|||||||
Android NDK: This is likely to result in incorrect builds. Try using LOCAL_STATIC_LIBRARIES
|
Android NDK: This is likely to result in incorrect builds. Try using LOCAL_STATIC_LIBRARIES
|
||||||
Android NDK: or LOCAL_SHARED_LIBRARIES instead to list the library dependencies of the
|
Android NDK: or LOCAL_SHARED_LIBRARIES instead to list the library dependencies of the
|
||||||
Android NDK: current module
|
Android NDK: current module
|
||||||
|
[x86_64] Compile++ : lorie <= client.cpp
|
||||||
|
[x86_64] Compile++ : lorie <= clipboard.cpp
|
||||||
|
[x86_64] Compile++ : lorie <= compositor.cpp
|
||||||
|
[x86_64] Compile++ : lorie <= egl-helper.cpp
|
||||||
|
[x86_64] Compile++ : lorie <= message-queue.cpp
|
||||||
|
[x86_64] Compile++ : lorie <= renderer.cpp
|
||||||
|
[x86_64] Compile++ : lorie <= seat.cpp
|
||||||
|
[x86_64] Compile++ : lorie <= surface.cpp
|
||||||
|
[x86_64] Compile++ : lorie <= output.cpp
|
||||||
|
[x86_64] Compile++ : lorie <= log.cpp
|
||||||
|
[x86_64] Compile++ : lorie <= utils.cpp
|
||||||
|
[x86_64] Compile++ : lorie <= wayland.cpp
|
||||||
[x86_64] Compile++ : lorie <= android-app.cpp
|
[x86_64] Compile++ : lorie <= android-app.cpp
|
||||||
|
[x86_64] Compile : lorie <= utils.c
|
||||||
|
[x86_64] Compile : xkbcommon <= parser.c
|
||||||
|
[x86_64] Compile : xkbcommon <= paths.c
|
||||||
|
[x86_64] Compile : xkbcommon <= state.c
|
||||||
|
[x86_64] Compile : xkbcommon <= table.c
|
||||||
|
[x86_64] Compile : xkbcommon <= action.c
|
||||||
|
[x86_64] Compile : xkbcommon <= ast-build.c
|
||||||
|
[x86_64] Compile : xkbcommon <= compat.c
|
||||||
|
[x86_64] Compile : xkbcommon <= expr.c
|
||||||
|
[x86_64] Compile : xkbcommon <= include.c
|
||||||
|
[x86_64] Compile : xkbcommon <= keycodes.c
|
||||||
|
[x86_64] Compile : xkbcommon <= keymap.c
|
||||||
|
[x86_64] Compile : xkbcommon <= keymap-dump.c
|
||||||
|
[x86_64] Compile : xkbcommon <= keywords.c
|
||||||
|
[x86_64] Compile : xkbcommon <= parser.c
|
||||||
|
[x86_64] Compile : xkbcommon <= rules.c
|
||||||
|
[x86_64] Compile : xkbcommon <= scanner.c
|
||||||
|
[x86_64] Compile : xkbcommon <= symbols.c
|
||||||
|
[x86_64] Compile : xkbcommon <= types.c
|
||||||
|
[x86_64] Compile : xkbcommon <= vmod.c
|
||||||
|
[x86_64] Compile : xkbcommon <= xkbcomp.c
|
||||||
|
[x86_64] Compile : xkbcommon <= atom.c
|
||||||
|
[x86_64] Compile : xkbcommon <= context.c
|
||||||
|
[x86_64] Compile : xkbcommon <= context-priv.c
|
||||||
|
[x86_64] Compile : xkbcommon <= keysym.c
|
||||||
|
[x86_64] Compile : xkbcommon <= keysym-utf.c
|
||||||
|
[x86_64] Compile : xkbcommon <= keymap.c
|
||||||
|
[x86_64] Compile : xkbcommon <= keymap-priv.c
|
||||||
|
[x86_64] Compile : xkbcommon <= state.c
|
||||||
|
[x86_64] Compile : xkbcommon <= text.c
|
||||||
|
[x86_64] Compile : xkbcommon <= utf8.c
|
||||||
|
[x86_64] Compile : xkbcommon <= utils.c
|
||||||
|
[x86_64] SharedLibrary : libxkbcommon.so
|
||||||
|
[x86_64] SharedLibrary : liblorie.so
|
||||||
|
2
app/.gitignore
vendored
2
app/.gitignore
vendored
@ -1 +1,3 @@
|
|||||||
/build
|
/build
|
||||||
|
/release
|
||||||
|
.externalNativeBuild
|
||||||
|
BIN
app/release/app-release.apk
Normal file
BIN
app/release/app-release.apk
Normal file
Binary file not shown.
BIN
app/release/app-release.apk.zip
Normal file
BIN
app/release/app-release.apk.zip
Normal file
Binary file not shown.
1
app/release/output.json
Normal file
1
app/release/output.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"1.0","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
|
@ -1,17 +1,36 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="com.termux.x11"
|
package="com.termux.x11"
|
||||||
android:sharedUserId="com.termux">
|
android:sharedUserId="com.termux">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
|
||||||
|
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"
|
||||||
|
tools:ignore="ProtectedPermissions" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="false"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme"
|
||||||
|
tools:ignore="GoogleAppIndexingWarning" >
|
||||||
|
<service
|
||||||
|
android:name=".LorieService"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="false"
|
||||||
|
android:persistent="true" />
|
||||||
|
<service
|
||||||
|
android:name=".LorieTestService"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="true"
|
||||||
|
android:process="com.termux.x11.separated"
|
||||||
|
android:persistent="true" />
|
||||||
<activity android:name=".MainActivity"
|
<activity android:name=".MainActivity"
|
||||||
android:theme="@style/NoActionBar"
|
android:theme="@style/NoActionBar"
|
||||||
|
android:launchMode="singleInstance"
|
||||||
android:configChanges="fontScale|orientation|screenSize|keyboard|keyboardHidden|layoutDirection|locale|mcc|mnc|navigation|screenLayout|touchscreen|uiMode|smallestScreenSize|density">
|
android:configChanges="fontScale|orientation|screenSize|keyboard|keyboardHidden|layoutDirection|locale|mcc|mnc|navigation|screenLayout|touchscreen|uiMode|smallestScreenSize|density">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
@ -19,6 +38,12 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:theme="@style/Theme.AppCompat.DayNight"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:name=".LoriePreferences" >
|
||||||
|
</activity>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
12
app/src/main/aidl/com/termux/x11/ITermuxService.aidl
Normal file
12
app/src/main/aidl/com/termux/x11/ITermuxService.aidl
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// ITermuxService.aidl
|
||||||
|
package com.termux.x11;
|
||||||
|
|
||||||
|
import android.view.Surface;
|
||||||
|
|
||||||
|
interface ITermuxService {
|
||||||
|
oneway void windowChanged(in Surface surface, int width, int height);
|
||||||
|
oneway void pointerMotion(int x, int y);
|
||||||
|
oneway void pointerScroll(int axis, float value);
|
||||||
|
oneway void pointerButton(int button, int type);
|
||||||
|
oneway void keyboardKey(int key, int type, int shift, String characters);
|
||||||
|
}
|
118
app/src/main/java/com/termux/x11/LoriePreferences.java
Normal file
118
app/src/main/java/com/termux/x11/LoriePreferences.java
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
package com.termux.x11;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.inputmethodservice.Keyboard;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.Preference;
|
||||||
|
import android.preference.PreferenceFragment;
|
||||||
|
import android.preference.PreferenceScreen;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.support.v7.app.ActionBar;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
public class LoriePreferences extends AppCompatActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
|
static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard";
|
||||||
|
LoriePreferenceFragment loriePreferenceFragment;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
loriePreferenceFragment = new LoriePreferenceFragment();
|
||||||
|
|
||||||
|
getFragmentManager().beginTransaction().replace(android.R.id.content, loriePreferenceFragment).commit();
|
||||||
|
|
||||||
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
if (actionBar != null) {
|
||||||
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
|
actionBar.setHomeButtonEnabled(true);
|
||||||
|
actionBar.setTitle("Preferences");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
if (loriePreferenceFragment != null)
|
||||||
|
loriePreferenceFragment.getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
if (loriePreferenceFragment != null)
|
||||||
|
loriePreferenceFragment.getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
int id = item.getItemId();
|
||||||
|
|
||||||
|
if (id == android.R.id.home) {
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||||
|
LorieService.start(LorieService.ACTION_PREFERENCES_CHAGED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LoriePreferenceFragment extends PreferenceFragment implements PreferenceScreen.OnPreferenceClickListener, Preference.OnPreferenceChangeListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(final Bundle savedInstanceState)
|
||||||
|
{
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
addPreferencesFromResource(R.xml.preferences);
|
||||||
|
|
||||||
|
String showImeEnabled = Settings.Secure.getString(getActivity().getContentResolver(), SHOW_IME_WITH_HARD_KEYBOARD);
|
||||||
|
SharedPreferences.Editor p = getPreferenceManager().getSharedPreferences().edit();
|
||||||
|
p.putBoolean("showIMEWhileExternalConnected", showImeEnabled.equals("1"));
|
||||||
|
p.apply();
|
||||||
|
|
||||||
|
PreferenceScreen s = getPreferenceScreen();
|
||||||
|
for (int i=0; i<s.getPreferenceCount(); i++) {
|
||||||
|
s.getPreference(i).setOnPreferenceClickListener(this);
|
||||||
|
s.getPreference(i).setOnPreferenceChangeListener(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
|
String key = preference.getKey();
|
||||||
|
Log.e("Preferences", "changed preference: " + key);
|
||||||
|
if (key.equals("showIMEWhileExternalConnected")) {
|
||||||
|
boolean enabled = newValue.toString().equals("true");
|
||||||
|
try {
|
||||||
|
Settings.Secure.putString(getActivity().getContentResolver(), SHOW_IME_WITH_HARD_KEYBOARD, enabled ? "1" : "0");
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (e instanceof SecurityException) {
|
||||||
|
new AlertDialog.Builder(getActivity())
|
||||||
|
.setTitle("Permission denied")
|
||||||
|
.setMessage("Android requires WRITE_SECURE_SETTINGS permission to change this setting.\n" +
|
||||||
|
"Please, launch this command using ADB:\n" +
|
||||||
|
"adb shell pm grant com.termux.x11 android.permission.WRITE_SECURE_SETTINGS")
|
||||||
|
.setNegativeButton("OK", null)
|
||||||
|
.create()
|
||||||
|
.show();
|
||||||
|
} else e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,340 +1,389 @@
|
|||||||
package com.termux.x11;
|
package com.termux.x11;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.ActivityManager;
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.NotificationChannel;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.app.Service;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.RequiresApi;
|
||||||
|
import android.support.v4.app.NotificationCompat;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.InputDevice;
|
import android.view.InputDevice;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.PointerIcon;
|
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import android.view.SurfaceHolder;
|
import android.view.SurfaceHolder;
|
||||||
import android.view.SurfaceView;
|
import android.view.SurfaceView;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.widget.Toast;
|
||||||
import android.view.WindowManager;
|
|
||||||
import android.widget.FrameLayout;
|
|
||||||
import android.widget.RelativeLayout;
|
|
||||||
import android.widget.ScrollView;
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("ConstantConditions")
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint({"ClickableViewAccessibility", "StaticFieldLeak"})
|
||||||
public class LorieService extends FrameLayout implements SurfaceHolder.Callback, View.OnTouchListener, View.OnKeyListener, KeyboardUtils.SoftKeyboardToggleListener, View.OnHoverListener, View.OnGenericMotionListener {
|
public class LorieService extends Service {
|
||||||
private final static int SV2_ID = 0xACCEDED;
|
|
||||||
private static final int BTN_LEFT = 0x110;
|
|
||||||
private static final int BTN_RIGHT = 0x111;
|
|
||||||
private static final int BTN_MIDDLE = 0x112;
|
|
||||||
|
|
||||||
private static final int TOUCH_MODE_TOUCHSCREEN = 0x01;
|
static final String ACTION_STOP_SERVICE = "com.termux.x11.service_stop";
|
||||||
private static final int TOUCH_MODE_TOUCHPAD = 0x02;
|
static final String ACTION_START_FROM_ACTIVITY = "com.termux.x11.start_from_activity";
|
||||||
|
static final String ACTION_START_PREFERENCES_ACTIVITY = "com.termux.x11.start_preferences_activity";
|
||||||
|
static final String ACTION_PREFERENCES_CHAGED = "com.termux.x11.preferences_changed";
|
||||||
|
|
||||||
private static final int WL_STATE_PRESSED = 1;
|
private static LorieService instance = null;
|
||||||
private static final int WL_STATE_RELEASED = 0;
|
//private
|
||||||
private static final int WL_POINTER_MOTION = 2;
|
//static
|
||||||
|
long compositor;
|
||||||
|
private static ServiceEventListener listener = new ServiceEventListener();
|
||||||
|
private static MainActivity act;
|
||||||
|
|
||||||
private static final int WL_POINTER_AXIS_VERTICAL_SCROLL = 0;
|
private TouchParser mTP;
|
||||||
private static final int WL_POINTER_AXIS_HORIZONTAL_SCROLL = 1;
|
|
||||||
|
|
||||||
private static int[] keys = {
|
public LorieService(){
|
||||||
KeyEvent.KEYCODE_ESCAPE,
|
instance = this;
|
||||||
KeyEvent.KEYCODE_TAB,
|
}
|
||||||
KeyEvent.KEYCODE_CTRL_LEFT,
|
|
||||||
KeyEvent.KEYCODE_ALT_LEFT,
|
|
||||||
KeyEvent.KEYCODE_DPAD_UP,
|
|
||||||
KeyEvent.KEYCODE_DPAD_DOWN,
|
|
||||||
KeyEvent.KEYCODE_DPAD_LEFT,
|
|
||||||
KeyEvent.KEYCODE_DPAD_RIGHT,
|
|
||||||
};
|
|
||||||
private long compositor;
|
|
||||||
private Activity act;
|
|
||||||
private AdditionalKeyboardView kbd;
|
|
||||||
private int touchscreenMode;
|
|
||||||
private boolean hardwareKeyboardConnected = false;
|
|
||||||
|
|
||||||
private touchPadListener tpListener;
|
static void setMainActivity(MainActivity activity) {
|
||||||
private touchScreenListener tsListener;
|
|
||||||
private hardwareMouseListener hmListener;
|
|
||||||
|
|
||||||
LorieService(Context ctx) {super(ctx);}
|
|
||||||
|
|
||||||
LorieService(Activity activity) {
|
|
||||||
super(activity);
|
|
||||||
act = activity;
|
act = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void start(String action) {
|
||||||
|
Intent intent = new Intent(act, LorieService.class);
|
||||||
|
intent.setAction(action);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
act.startForegroundService(intent);
|
||||||
|
} else {
|
||||||
|
act.startService(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
if (isServiceRunningInForeground(this, LorieService.class)) return;
|
||||||
|
|
||||||
compositor = createLorieThread();
|
compositor = createLorieThread();
|
||||||
if (compositor == 0) {
|
if (compositor == 0) {
|
||||||
Log.e("LorieService", "compositor thread was not created");
|
Log.e("LorieService", "compositor thread was not created");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int dp = (int) act.getResources().getDisplayMetrics().density;
|
instance = this;
|
||||||
ViewGroup.LayoutParams match_parent = new WindowManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
Toast.makeText(this, "Service was Created", Toast.LENGTH_LONG).show();
|
||||||
setLayoutParams(match_parent);
|
Log.e("LorieService", "created");
|
||||||
|
|
||||||
SurfaceView lorieView = new SurfaceView(act);
|
Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);
|
||||||
addView(lorieView, match_parent);
|
notificationIntent.putExtra("foo_bar_extra_key", "foo_bar_extra_value");
|
||||||
|
notificationIntent.setAction(Long.toString(System.currentTimeMillis()));
|
||||||
|
|
||||||
RelativeLayout rl = new RelativeLayout(act);
|
Intent exitIntent = new Intent(getApplicationContext(), LorieService.class);
|
||||||
ScrollView sv = new ScrollView(act);
|
exitIntent.setAction(ACTION_STOP_SERVICE);
|
||||||
kbd = new AdditionalKeyboardView(act);
|
|
||||||
RelativeLayout.LayoutParams svlp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
|
|
||||||
RelativeLayout.LayoutParams kbdlp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, 50*dp);
|
|
||||||
|
|
||||||
svlp.addRule(RelativeLayout.BELOW, SV2_ID);
|
Intent preferencesIntent = new Intent(getApplicationContext(), LoriePreferences.class);
|
||||||
kbdlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
|
preferencesIntent.setAction(ACTION_START_PREFERENCES_ACTIVITY);
|
||||||
kbd.setId(SV2_ID);
|
|
||||||
rl.addView(kbd, kbdlp);
|
|
||||||
rl.addView(sv, svlp);
|
|
||||||
addView(rl);
|
|
||||||
|
|
||||||
kbd.setVisibility(View.INVISIBLE);
|
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
PendingIntent pendingExitIntent = PendingIntent.getService(getApplicationContext(), 0, exitIntent, 0);
|
||||||
|
PendingIntent pendingPreferencesIntent = PendingIntent.getActivity(getApplicationContext(), 0, preferencesIntent, 0);
|
||||||
|
|
||||||
lorieView.getHolder().addCallback(this);
|
//For creating the Foreground Service
|
||||||
lorieView.setOnTouchListener(this);
|
int priority = Notification.PRIORITY_HIGH;
|
||||||
lorieView.setOnKeyListener(this);
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N)
|
||||||
|
priority = NotificationManager.IMPORTANCE_HIGH;
|
||||||
|
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||||
|
String channelId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? getNotificationChannel(notificationManager) : "";
|
||||||
|
Notification notification = new NotificationCompat.Builder(this, channelId)
|
||||||
|
.setContentTitle("Termux:X11")
|
||||||
|
.setSmallIcon(R.drawable.ic_x11_icon)
|
||||||
|
.setContentText("is running in foreground")
|
||||||
|
.setContentIntent(pendingIntent)
|
||||||
|
.setOngoing(true)
|
||||||
|
.setPriority(priority)
|
||||||
|
.setShowWhen(false)
|
||||||
|
.setColor(0xFF607D8B)
|
||||||
|
.addAction(0, "Preferences", pendingPreferencesIntent)
|
||||||
|
.addAction(0, "Exit", pendingExitIntent)
|
||||||
|
.build();
|
||||||
|
startForeground(1, notification);
|
||||||
|
|
||||||
lorieView.setFocusable(true);
|
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
lorieView.setFocusableInTouchMode(true);
|
String packageName = getPackageName();
|
||||||
lorieView.requestFocus();
|
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||||
|
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
|
||||||
kbd.reload(keys, lorieView, this);
|
Intent whitelist = new Intent();
|
||||||
|
whitelist.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
|
||||||
lorieView.setOnHoverListener(this);
|
whitelist.setData(Uri.parse("package:" + packageName));
|
||||||
lorieView.setOnGenericMotionListener(this);
|
whitelist.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
startActivity(whitelist);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
}
|
||||||
setPointerIcon(PointerIcon.getSystemIcon(act, PointerIcon.TYPE_NULL));
|
}
|
||||||
// prevents SurfaceView from being resized but interrupts additional keyboard showing process
|
|
||||||
// act.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
|
|
||||||
|
|
||||||
touchscreenMode = TOUCH_MODE_TOUCHSCREEN;
|
|
||||||
tpListener = new touchPadListener(this);
|
|
||||||
tsListener = new touchScreenListener(this);
|
|
||||||
hmListener = new hardwareMouseListener(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void finishInit() {
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
KeyboardUtils.addKeyboardToggleListener(act, this);
|
private String getNotificationChannel(NotificationManager notificationManager){
|
||||||
|
String channelId = getResources().getString(R.string.app_name);
|
||||||
|
String channelName = getResources().getString(R.string.app_name);
|
||||||
|
NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
|
||||||
|
channel.setImportance(NotificationManager.IMPORTANCE_NONE);
|
||||||
|
channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
|
||||||
|
notificationManager.createNotificationChannel(channel);
|
||||||
|
return channelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isServiceRunningInForeground(Context context, Class<?> serviceClass) {
|
||||||
|
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||||
|
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
|
||||||
|
if (serviceClass.getName().equals(service.service.getClassName())) {
|
||||||
|
return service.foreground;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void onPreferencesChanged() {
|
||||||
|
if (instance == null || act == null) return;
|
||||||
|
|
||||||
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(instance);
|
||||||
|
|
||||||
|
instance.mTP.setMode(
|
||||||
|
Integer.parseInt(
|
||||||
|
preferences.getString("touchMode", "1")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
act.showAdditionalKbd = preferences.getBoolean("showAdditionalKbd", true);
|
||||||
|
Log.e("LorieService", "Preferences changed");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
Log.e("LorieService", "start");
|
||||||
|
String action = intent.getAction();
|
||||||
|
|
||||||
|
if (action.equals(ACTION_START_FROM_ACTIVITY)) {
|
||||||
|
act.onLorieServiceStart(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.equals(ACTION_STOP_SERVICE)) {
|
||||||
|
Log.e("LorieService", action);
|
||||||
|
terminate();
|
||||||
|
sleep(500);
|
||||||
|
act.finish();
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
onPreferencesChanged();
|
||||||
|
|
||||||
|
return START_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
Log.e("LorieService", "destroyed");
|
||||||
|
}
|
||||||
|
|
||||||
|
static LorieService getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
Log.e("LorieService", "Instance was requested, but no instances available");
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setListeners(@NonNull SurfaceView view) {
|
||||||
|
Context a = view.getRootView().findViewById(android.R.id.content).getContext();
|
||||||
|
if (!(a instanceof MainActivity)) {
|
||||||
|
Log.e("LorieService", "Context is not an activity!!!");
|
||||||
|
}
|
||||||
|
|
||||||
|
act = (MainActivity) a;
|
||||||
|
|
||||||
|
view.setFocusable(true);
|
||||||
|
view.setFocusableInTouchMode(true);
|
||||||
|
view.requestFocus();
|
||||||
|
|
||||||
|
listener.svc = this;
|
||||||
|
listener.setAsListenerTo(view);
|
||||||
|
|
||||||
|
mTP = new TouchParser(view, listener);
|
||||||
|
onPreferencesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
static View.OnKeyListener getOnKeyListener() {
|
||||||
|
return listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
void terminate() {
|
void terminate() {
|
||||||
terminate(compositor);
|
terminate(compositor);
|
||||||
compositor = 0;
|
compositor = 0;
|
||||||
|
Log.e("LorieService", "terminate");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void surfaceCreated(SurfaceHolder holder) {
|
public IBinder onBind(Intent intent) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private static class ServiceEventListener implements SurfaceHolder.Callback, View.OnTouchListener, View.OnKeyListener, View.OnHoverListener, View.OnGenericMotionListener, TouchParser.OnTouchParseListener {
|
||||||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
LorieService svc;
|
||||||
DisplayMetrics dm = new DisplayMetrics();
|
|
||||||
int mmWidth, mmHeight;
|
|
||||||
act.getWindowManager().getDefaultDisplay().getMetrics(dm);
|
|
||||||
|
|
||||||
if (act.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
|
private void setAsListenerTo(SurfaceView view) {
|
||||||
mmWidth = (int) Math.round((width * 25.4) / dm.xdpi);
|
view.getHolder().addCallback(this);
|
||||||
mmHeight = (int) Math.round((height * 25.4) / dm.ydpi);
|
view.setOnTouchListener(this);
|
||||||
} else {
|
view.setOnHoverListener(this);
|
||||||
mmWidth = (int) Math.round((width * 25.4) / dm.ydpi);
|
view.setOnGenericMotionListener(this);
|
||||||
mmHeight = (int) Math.round((height * 25.4) / dm.xdpi);
|
view.setOnKeyListener(this);
|
||||||
|
surfaceChanged(view.getHolder(), 0, view.getWidth(), view.getHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
windowChanged(compositor, holder.getSurface(), width, height, mmWidth, mmHeight);
|
public void onPointerButton(int button, int state) {
|
||||||
}
|
if (svc == null) return;
|
||||||
|
svc.pointerButton(button, state);
|
||||||
@Override
|
|
||||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onTouch(View v, MotionEvent e) {
|
|
||||||
switch(e.getSource()) {
|
|
||||||
case InputDevice.SOURCE_TOUCHSCREEN:
|
|
||||||
switch(touchscreenMode) {
|
|
||||||
case TOUCH_MODE_TOUCHPAD: return tpListener.onTouch(v, e);
|
|
||||||
case TOUCH_MODE_TOUCHSCREEN: return tsListener.onTouch(v, e);
|
|
||||||
default: return tsListener.onTouch(v, e);
|
|
||||||
}
|
|
||||||
case InputDevice.SOURCE_MOUSE: return hmListener.onTouch(v, e);
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void toggleKeyboard() {
|
|
||||||
if (!hardwareKeyboardConnected)
|
|
||||||
KeyboardUtils.toggleKeyboardVisibility(act);
|
|
||||||
else {
|
|
||||||
boolean isKbdVisible = kbd.getVisibility() == View.VISIBLE;
|
|
||||||
kbd.setVisibility(isKbdVisible ? View.INVISIBLE : View.VISIBLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean rightPressed = false; // Prevent right button press event from being repeated
|
|
||||||
@Override
|
|
||||||
public boolean onKey(View v, int keyCode, KeyEvent e) {
|
|
||||||
int action = 0;
|
|
||||||
|
|
||||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
|
||||||
if (e.getSource() == InputDevice.SOURCE_MOUSE && rightPressed != (e.getAction() == KeyEvent.ACTION_DOWN)) {
|
|
||||||
pointerButton(compositor, BTN_RIGHT, (e.getAction() == KeyEvent.ACTION_DOWN) ? WL_STATE_PRESSED : WL_STATE_RELEASED);
|
|
||||||
rightPressed = (e.getAction() == KeyEvent.ACTION_DOWN);
|
|
||||||
} else if (e.getAction() == KeyEvent.ACTION_UP) {
|
|
||||||
toggleKeyboard();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.getAction() == KeyEvent.ACTION_DOWN) action = WL_STATE_PRESSED;
|
public void onPointerMotion(int x, int y) {
|
||||||
if (e.getAction() == KeyEvent.ACTION_UP) action = WL_STATE_RELEASED;
|
if (svc == null) return;
|
||||||
keyboardKey(compositor, action, keyCode, e.isShiftPressed() ? 1 : 0, e.getCharacters());
|
svc.pointerMotion(x, y);
|
||||||
//onKey(compositor, action, keyCode, e.isShiftPressed() ? 1 : 0, e.getCharacters());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onToggleSoftKeyboard(boolean isVisible) {
|
|
||||||
kbd.setVisibility((isVisible)?View.VISIBLE:View.INVISIBLE);
|
|
||||||
//Log.d("LorieActivity", "keyboard is " + (isVisible?"visible":"not visible"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onConfigurationChanged(Configuration config) {
|
|
||||||
hardwareKeyboardConnected = config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onGenericMotion(View v, MotionEvent e) {
|
|
||||||
onTouch(v, e);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onHover(View v, MotionEvent e) {
|
|
||||||
onTouch(v, e);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class hardwareMouseListener implements View.OnTouchListener {
|
|
||||||
private int savedBS = 0;
|
|
||||||
private int currentBS = 0;
|
|
||||||
private LorieService svc;
|
|
||||||
|
|
||||||
hardwareMouseListener(LorieService service) { svc = service; }
|
|
||||||
|
|
||||||
boolean isMouseButtonChanged(int mask) {
|
|
||||||
return (savedBS & mask) != (currentBS & mask);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int mouseButtonState(int mask) {
|
public void onPointerScroll(int axis, float value) {
|
||||||
return ((currentBS & mask) != 0) ? WL_STATE_PRESSED : WL_STATE_RELEASED;
|
if (svc == null) return;
|
||||||
|
svc.pointerScroll(axis, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onTouch(View v, MotionEvent e) {
|
public void onTouchDown(int id, float x, float y) {
|
||||||
if (e.getAction() == MotionEvent.ACTION_SCROLL) {
|
if (svc == null) return;
|
||||||
float scrollY = -5 * e.getAxisValue(MotionEvent.AXIS_VSCROLL);
|
svc.touchDown(id, x, y);
|
||||||
float scrollX = -5 * e.getAxisValue(MotionEvent.AXIS_HSCROLL);
|
|
||||||
|
|
||||||
if (scrollY != 0) pointerScroll(WL_POINTER_AXIS_VERTICAL_SCROLL, scrollY);
|
|
||||||
if (scrollX != 0) pointerScroll(WL_POINTER_AXIS_HORIZONTAL_SCROLL, scrollX);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
svc.pointerMotion(e.getX(), e.getY());
|
|
||||||
|
|
||||||
currentBS = e.getButtonState();
|
|
||||||
if (isMouseButtonChanged(MotionEvent.BUTTON_PRIMARY)) {
|
|
||||||
svc.pointerButton(svc.compositor, BTN_LEFT, mouseButtonState(MotionEvent.BUTTON_PRIMARY));
|
|
||||||
}
|
|
||||||
if (isMouseButtonChanged(MotionEvent.BUTTON_TERTIARY)) {
|
|
||||||
svc.pointerButton(svc.compositor, BTN_MIDDLE, mouseButtonState(MotionEvent.BUTTON_TERTIARY));
|
|
||||||
}
|
|
||||||
if (isMouseButtonChanged(MotionEvent.BUTTON_SECONDARY)) {
|
|
||||||
svc.pointerButton(svc.compositor, BTN_RIGHT, mouseButtonState(MotionEvent.BUTTON_SECONDARY));
|
|
||||||
}
|
|
||||||
savedBS = currentBS;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class touchScreenListener implements View.OnTouchListener {
|
|
||||||
private LorieService svc;
|
|
||||||
|
|
||||||
touchScreenListener(LorieService service) { svc = service; }
|
|
||||||
|
|
||||||
public boolean onTouch(View v, MotionEvent e) {
|
|
||||||
int type = WL_POINTER_MOTION;
|
|
||||||
switch (e.getAction()) {
|
|
||||||
case MotionEvent.ACTION_POINTER_DOWN:
|
|
||||||
case MotionEvent.ACTION_BUTTON_PRESS:
|
|
||||||
case MotionEvent.ACTION_DOWN:
|
|
||||||
type = WL_STATE_PRESSED;
|
|
||||||
break;
|
|
||||||
case MotionEvent.ACTION_POINTER_UP:
|
|
||||||
case MotionEvent.ACTION_BUTTON_RELEASE:
|
|
||||||
case MotionEvent.ACTION_UP:
|
|
||||||
type = WL_STATE_RELEASED;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
svc.pointerMotion(e.getX(), e.getY());
|
|
||||||
if (type != WL_POINTER_MOTION)
|
|
||||||
svc.pointerButton(svc.compositor, BTN_LEFT, type);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
public void onTouchMotion(int id, float x, float y) {
|
||||||
|
if (svc == null) return;
|
||||||
|
svc.touchMotion(id, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
private class touchPadListener implements View.OnTouchListener {
|
public void onTouchUp(int id) {
|
||||||
private int startX, startY;
|
if (svc == null) return;
|
||||||
private int curX, curY;
|
svc.touchUp(id);
|
||||||
private int dX, dY;
|
}
|
||||||
private long curTime;
|
|
||||||
private LorieService svc;
|
|
||||||
|
|
||||||
touchPadListener(LorieService service) { svc = service; }
|
public void onTouchFrame() {
|
||||||
|
if (svc == null) return;
|
||||||
|
svc.touchFrame();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onTouch(View v, MotionEvent e) {
|
public boolean onTouch(View v, MotionEvent e) {
|
||||||
int X, Y;
|
if (svc == null) return false;
|
||||||
switch (e.getActionMasked()) {
|
return svc.mTP.onTouchEvent(e);
|
||||||
case MotionEvent.ACTION_DOWN:
|
}
|
||||||
startX = (int) e.getX();
|
|
||||||
startY = (int) e.getY();
|
|
||||||
curTime= System.currentTimeMillis();
|
|
||||||
break;
|
|
||||||
case MotionEvent.ACTION_MOVE:
|
|
||||||
|
|
||||||
X = (int) e.getX();
|
@Override
|
||||||
Y = (int) e.getY();
|
public boolean onGenericMotion(View v, MotionEvent e) {
|
||||||
//ClientThread.update(Integer.toString(X - startX) + " " + Integer.toString(Y - startY));
|
if (svc == null) return false;
|
||||||
curX = X;
|
return svc.mTP.onTouchEvent(e);
|
||||||
curY = Y;
|
}
|
||||||
if (dX < curX - startX) dX = curX - startX;
|
|
||||||
if (dY < curY - startY) dY = curY - startY;
|
@Override
|
||||||
break;
|
public boolean onHover(View v, MotionEvent e) {
|
||||||
case MotionEvent.ACTION_UP:
|
if (svc == null) return false;
|
||||||
long duration=System.currentTimeMillis()-curTime;
|
return svc.mTP.onTouchEvent(e);
|
||||||
if(duration<100) {
|
}
|
||||||
Log.d("Click",Long.toString(duration));
|
|
||||||
//ClientThread.update("3");
|
private boolean isSource(KeyEvent e, int source) {
|
||||||
}
|
return (e.getSource() & source) == source;
|
||||||
break;
|
}
|
||||||
|
|
||||||
|
private boolean rightPressed = false; // Prevent right button press event from being repeated
|
||||||
|
private boolean middlePressed = false; // Prevent middle button press event from being repeated
|
||||||
|
@Override
|
||||||
|
public boolean onKey(View v, int keyCode, KeyEvent e) {
|
||||||
|
if (svc == null) return false;
|
||||||
|
int action = 0;
|
||||||
|
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||||
|
if (
|
||||||
|
isSource(e, InputDevice.SOURCE_MOUSE) &&
|
||||||
|
rightPressed != (e.getAction() == KeyEvent.ACTION_DOWN)
|
||||||
|
) {
|
||||||
|
svc.pointerButton(TouchParser.BTN_RIGHT, (e.getAction() == KeyEvent.ACTION_DOWN) ? TouchParser.ACTION_DOWN : TouchParser.ACTION_UP);
|
||||||
|
rightPressed = (e.getAction() == KeyEvent.ACTION_DOWN);
|
||||||
|
} else if (e.getAction() == KeyEvent.ACTION_UP) {
|
||||||
|
KeyboardUtils.toggleKeyboardVisibility(act);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
keyCode == KeyEvent.KEYCODE_MENU &&
|
||||||
|
isSource(e, InputDevice.SOURCE_MOUSE) &&
|
||||||
|
middlePressed != (e.getAction() == KeyEvent.ACTION_DOWN)
|
||||||
|
) {
|
||||||
|
svc.pointerButton(TouchParser.BTN_MIDDLE, (e.getAction() == KeyEvent.ACTION_DOWN) ? TouchParser.ACTION_DOWN : TouchParser.ACTION_UP);
|
||||||
|
middlePressed = (e.getAction() == KeyEvent.ACTION_DOWN);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.getAction() == KeyEvent.ACTION_DOWN) action = TouchParser.ACTION_DOWN;
|
||||||
|
if (e.getAction() == KeyEvent.ACTION_UP) action = TouchParser.ACTION_UP;
|
||||||
|
svc.keyboardKey(action, keyCode, e.isShiftPressed() ? 1 : 0, e.getCharacters());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||||
|
DisplayMetrics dm = new DisplayMetrics();
|
||||||
|
int mmWidth, mmHeight;
|
||||||
|
act.getWindowManager().getDefaultDisplay().getMetrics(dm);
|
||||||
|
|
||||||
|
if (act.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||||
|
mmWidth = (int) Math.round((width * 25.4) / dm.xdpi);
|
||||||
|
mmHeight = (int) Math.round((height * 25.4) / dm.ydpi);
|
||||||
|
} else {
|
||||||
|
mmWidth = (int) Math.round((width * 25.4) / dm.ydpi);
|
||||||
|
mmHeight = (int) Math.round((height * 25.4) / dm.xdpi);
|
||||||
|
}
|
||||||
|
|
||||||
|
svc.windowChanged(holder.getSurface(), width, height, mmWidth, mmHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void surfaceCreated(SurfaceHolder holder) {}
|
||||||
|
@Override public void surfaceDestroyed(SurfaceHolder holder) {}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void sleep(long millis) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(millis);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void windowChanged(Surface s, int w, int h, int pw, int ph) {windowChanged(compositor, s, w, h, pw, ph);}
|
||||||
|
private void touchDown(int id, float x, float y) { touchDown(compositor, id, (int) x, (int) y); }
|
||||||
|
private void touchMotion(int id, float x, float y) { touchMotion(compositor, id, (int) x, (int) y); }
|
||||||
|
private void touchUp(int id) { touchUp(compositor, id); }
|
||||||
|
private void touchFrame() { touchFrame(compositor); }
|
||||||
private void pointerMotion(float x, float y) { pointerMotion(compositor, (int) x, (int) y); }
|
private void pointerMotion(float x, float y) { pointerMotion(compositor, (int) x, (int) y); }
|
||||||
private void pointerScroll(int axis, float value) { pointerScroll(compositor, axis, value); }
|
private void pointerScroll(int axis, float value) { pointerScroll(compositor, axis, value); }
|
||||||
private void pointerButton(int button, int type) { pointerButton(compositor, button, type); }
|
private void pointerButton(int button, int type) { pointerButton(compositor, button, type); }
|
||||||
|
private void keyboardKey(int key, int type, int shift, String characters) {keyboardKey(compositor, key, type, shift, characters);}
|
||||||
|
|
||||||
private native long createLorieThread();
|
private native long createLorieThread();
|
||||||
private native void windowChanged(long compositor, Surface surface, int width, int height, int mmWidth, int mmHeight);
|
private native void windowChanged(long compositor, Surface surface, int width, int height, int mmWidth, int mmHeight);
|
||||||
|
private native void touchDown(long compositor, int id, int x, int y);
|
||||||
|
private native void touchMotion(long compositor, int id, int x, int y);
|
||||||
|
private native void touchUp(long compositor, int id);
|
||||||
|
private native void touchFrame(long compositor);
|
||||||
private native void pointerMotion(long compositor, int x, int y);
|
private native void pointerMotion(long compositor, int x, int y);
|
||||||
private native void pointerScroll(long compositor, int axis, float value);
|
private native void pointerScroll(long compositor, int axis, float value);
|
||||||
private native void pointerButton(long compositor, int button, int type);
|
private native void pointerButton(long compositor, int button, int type);
|
||||||
|
69
app/src/main/java/com/termux/x11/LorieTestService.java
Normal file
69
app/src/main/java/com/termux/x11/LorieTestService.java
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package com.termux.x11;
|
||||||
|
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.NotificationChannel;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.support.annotation.RequiresApi;
|
||||||
|
import android.support.v4.app.NotificationCompat;
|
||||||
|
|
||||||
|
public class LorieTestService extends Service {
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void start(Context context) {
|
||||||
|
Intent intent = new Intent(context, LorieTestService.class);
|
||||||
|
intent.setAction("start");
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
context.startForegroundService(intent);
|
||||||
|
} else {
|
||||||
|
context.startService(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);
|
||||||
|
notificationIntent.putExtra("foo_bar_extra_key", "foo_bar_extra_value");
|
||||||
|
notificationIntent.setAction(Long.toString(System.currentTimeMillis()));
|
||||||
|
|
||||||
|
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
|
||||||
|
//For creating the Foreground Service
|
||||||
|
int priority = Notification.PRIORITY_HIGH;
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N)
|
||||||
|
priority = NotificationManager.IMPORTANCE_HIGH;
|
||||||
|
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||||
|
String channelId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? getNotificationChannel(notificationManager) : "";
|
||||||
|
Notification notification = new NotificationCompat.Builder(this, channelId)
|
||||||
|
.setContentTitle("Termux:X11 Test service")
|
||||||
|
.setSmallIcon(R.drawable.ic_x11_icon)
|
||||||
|
.setContentText("foreground service")
|
||||||
|
.setContentIntent(pendingIntent)
|
||||||
|
.setOngoing(true)
|
||||||
|
.setPriority(priority)
|
||||||
|
.setShowWhen(false)
|
||||||
|
.setColor(0xFF607D8B)
|
||||||
|
.build();
|
||||||
|
startForeground(3, notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
private String getNotificationChannel(NotificationManager notificationManager){
|
||||||
|
String channelId = getResources().getString(R.string.app_name);
|
||||||
|
String channelName = getResources().getString(R.string.app_name);
|
||||||
|
NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
|
||||||
|
channel.setImportance(NotificationManager.IMPORTANCE_NONE);
|
||||||
|
channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
|
||||||
|
notificationManager.createNotificationChannel(channel);
|
||||||
|
return channelId;
|
||||||
|
}
|
||||||
|
}
|
@ -1,36 +1,100 @@
|
|||||||
package com.termux.x11;
|
package com.termux.x11;
|
||||||
|
|
||||||
import android.content.res.Configuration;
|
import android.content.SharedPreferences;
|
||||||
|
import android.inputmethodservice.InputMethodService;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.provider.Settings;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.PointerIcon;
|
||||||
|
import android.view.SurfaceView;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.Window;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
|
||||||
|
public class MainActivity extends AppCompatActivity implements KeyboardUtils.SoftKeyboardToggleListener {
|
||||||
|
|
||||||
|
private static int[] keys = {
|
||||||
|
KeyEvent.KEYCODE_ESCAPE,
|
||||||
|
KeyEvent.KEYCODE_TAB,
|
||||||
|
KeyEvent.KEYCODE_CTRL_LEFT,
|
||||||
|
KeyEvent.KEYCODE_ALT_LEFT,
|
||||||
|
KeyEvent.KEYCODE_DPAD_UP,
|
||||||
|
KeyEvent.KEYCODE_DPAD_DOWN,
|
||||||
|
KeyEvent.KEYCODE_DPAD_LEFT,
|
||||||
|
KeyEvent.KEYCODE_DPAD_RIGHT,
|
||||||
|
};
|
||||||
|
|
||||||
|
AdditionalKeyboardView kbd;
|
||||||
|
boolean showAdditionalKbd = true;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
|
||||||
LorieService svc;
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
|
||||||
|
LorieService.setMainActivity(this);
|
||||||
|
LorieService.start(LorieService.ACTION_START_FROM_ACTIVITY);
|
||||||
|
|
||||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||||
|
|
||||||
svc = new LorieService(this);
|
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
setContentView(svc);
|
|
||||||
svc.finishInit();
|
setContentView(R.layout.main_activity);
|
||||||
svc.onConfigurationChanged(getResources().getConfiguration());
|
|
||||||
|
kbd = findViewById(R.id.additionalKbd);
|
||||||
|
kbd.setVisibility(View.INVISIBLE);
|
||||||
|
KeyboardUtils.addKeyboardToggleListener(this, this);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||||
|
getWindow().
|
||||||
|
getDecorView().
|
||||||
|
setPointerIcon(PointerIcon.getSystemIcon(this, PointerIcon.TYPE_NULL));
|
||||||
|
getResources().getConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onLorieServiceStart(LorieService instance) {
|
||||||
|
SurfaceView lorieView = findViewById(R.id.lorieView);
|
||||||
|
|
||||||
|
instance.setListeners(lorieView);
|
||||||
|
kbd.reload(keys, lorieView, LorieService.getOnKeyListener());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConfigurationChanged(Configuration newConfig) {
|
public void onWindowFocusChanged(boolean hasFocus)
|
||||||
super.onConfigurationChanged(newConfig );
|
{
|
||||||
svc.onConfigurationChanged(newConfig);
|
super.onWindowFocusChanged(hasFocus);
|
||||||
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
Window window = getWindow();
|
||||||
|
View decorView = window.getDecorView();
|
||||||
|
|
||||||
|
if (hasFocus && preferences.getBoolean("fullscreen", false))
|
||||||
|
{
|
||||||
|
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||||
|
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||||
|
decorView.setSystemUiVisibility(
|
||||||
|
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||||
|
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||||
|
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||||
|
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||||
|
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||||
|
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
|
||||||
|
} else {
|
||||||
|
window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||||
|
decorView.setSystemUiVisibility(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onToggleSoftKeyboard(boolean isVisible) {
|
||||||
|
if (kbd != null && showAdditionalKbd)
|
||||||
|
kbd.setVisibility((isVisible)?View.VISIBLE:View.INVISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {}
|
public void onBackPressed() {}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
svc.terminate();
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
547
app/src/main/java/com/termux/x11/TouchParser.java
Normal file
547
app/src/main/java/com/termux/x11/TouchParser.java
Normal file
@ -0,0 +1,547 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2008 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.termux.x11;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.InputDevice;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewConfiguration;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class TouchParser {
|
||||||
|
|
||||||
|
private static final int WL_STATE_PRESSED = 1;
|
||||||
|
private static final int WL_STATE_RELEASED = 0;
|
||||||
|
|
||||||
|
private static final int WL_POINTER_AXIS_VERTICAL_SCROLL = 0;
|
||||||
|
private static final int WL_POINTER_AXIS_HORIZONTAL_SCROLL = 1;
|
||||||
|
|
||||||
|
static final int TOUCH_MODE_DIRECT = 1;
|
||||||
|
static final int TOUCH_MODE_MOUSE = 2;
|
||||||
|
static final int TOUCH_MODE_TOUCHPAD = 3;
|
||||||
|
|
||||||
|
static final int BTN_LEFT = 0x110;
|
||||||
|
static final int BTN_RIGHT = 0x111;
|
||||||
|
static final int BTN_MIDDLE = 0x112;
|
||||||
|
|
||||||
|
static final int ACTION_DOWN = WL_STATE_PRESSED;
|
||||||
|
static final int ACTION_UP = WL_STATE_RELEASED;
|
||||||
|
|
||||||
|
private static final int AXIS_X = WL_POINTER_AXIS_HORIZONTAL_SCROLL;
|
||||||
|
private static final int AXIS_Y = WL_POINTER_AXIS_VERTICAL_SCROLL;
|
||||||
|
|
||||||
|
public interface OnTouchParseListener {
|
||||||
|
void onPointerButton(int button, int state);
|
||||||
|
void onPointerMotion(int x, int y);
|
||||||
|
void onPointerScroll(int axis, float value);
|
||||||
|
|
||||||
|
void onTouchDown(int id, float x, float y);
|
||||||
|
void onTouchMotion(int id, float x, float y);
|
||||||
|
void onTouchUp(int id);
|
||||||
|
void onTouchFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int mTouchSlopSquare;
|
||||||
|
private int mDoubleTapTouchSlopSquare;
|
||||||
|
private int mDoubleTapSlopSquare;
|
||||||
|
|
||||||
|
private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
|
||||||
|
private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
|
||||||
|
private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
|
||||||
|
private static final int DOUBLE_TAP_MIN_TIME = 40;
|
||||||
|
private static final int FLAG_IS_GENERATED_GESTURE = 0x8;
|
||||||
|
private static final int TOUCH_SLOP = 8;
|
||||||
|
private static final int DOUBLE_TAP_SLOP = 100;
|
||||||
|
private static final int DOUBLE_TAP_TOUCH_SLOP = TOUCH_SLOP;
|
||||||
|
|
||||||
|
// constants for Message.what used by GestureHandler below
|
||||||
|
private static final int SHOW_PRESS = 1;
|
||||||
|
private static final int LONG_PRESS = 2;
|
||||||
|
private static final int TAP = 3;
|
||||||
|
private static final int RIGHT_TAP = 4;
|
||||||
|
private static final int TOUCH_FRAME = 5;
|
||||||
|
|
||||||
|
private final Handler mHandler;
|
||||||
|
private final OnTouchParseListener mListener;
|
||||||
|
private final HardwareMouseListener hmListener;
|
||||||
|
|
||||||
|
private boolean mStillDown;
|
||||||
|
private boolean mDeferConfirmSingleTap;
|
||||||
|
private boolean mInLongPress;
|
||||||
|
private boolean mInContextClick;
|
||||||
|
private boolean mAlwaysInTapRegion;
|
||||||
|
private boolean mAlwaysInBiggerTapRegion;
|
||||||
|
|
||||||
|
private MotionEvent mCurrentDownEvent;
|
||||||
|
private MotionEvent mPreviousUpEvent;
|
||||||
|
|
||||||
|
private boolean mIsDoubleTapping;
|
||||||
|
|
||||||
|
private float mLastFocusX;
|
||||||
|
private float mLastFocusY;
|
||||||
|
private float mDownFocusX;
|
||||||
|
private float mDownFocusY;
|
||||||
|
|
||||||
|
private boolean mIsLongpressEnabled;
|
||||||
|
|
||||||
|
private View target;
|
||||||
|
private Point cursor;
|
||||||
|
private int mMode = TOUCH_MODE_DIRECT;
|
||||||
|
|
||||||
|
private boolean mInDrag = false;
|
||||||
|
private boolean mWasInDrag = false;
|
||||||
|
|
||||||
|
@SuppressLint("HandlerLeak")
|
||||||
|
private class GestureHandler extends Handler {
|
||||||
|
GestureHandler() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
GestureHandler(Handler handler) {
|
||||||
|
super(handler.getLooper());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
switch (msg.what) {
|
||||||
|
case SHOW_PRESS:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LONG_PRESS:
|
||||||
|
dispatchLongPress();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TAP:
|
||||||
|
// If the user's finger is still down, do not count it as a tap
|
||||||
|
if (!mStillDown) {
|
||||||
|
Log.e("tp", "tap");
|
||||||
|
mListener.onPointerButton(BTN_LEFT, ACTION_DOWN);
|
||||||
|
mListener.onPointerButton(BTN_LEFT, ACTION_UP);
|
||||||
|
} else {
|
||||||
|
mDeferConfirmSingleTap = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RIGHT_TAP:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Unknown message " + msg); //never
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TouchParser(View view, OnTouchParseListener listener) {
|
||||||
|
mListener = listener;
|
||||||
|
hmListener = new HardwareMouseListener();
|
||||||
|
mHandler = new GestureHandler();
|
||||||
|
|
||||||
|
target = view;
|
||||||
|
cursor = new Point(target.getWidth()/2, target.getHeight()/2);
|
||||||
|
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
Context context;
|
||||||
|
if (target == null) {
|
||||||
|
Log.e("TouchParser","TouchParser did not receive target view");
|
||||||
|
context = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
context = target.getRootView().findViewById(android.R.id.content).getContext();
|
||||||
|
|
||||||
|
mIsLongpressEnabled = true;
|
||||||
|
|
||||||
|
// Fallback to support pre-donuts releases
|
||||||
|
int touchSlop, doubleTapSlop, doubleTapTouchSlop;
|
||||||
|
if (context == null) {
|
||||||
|
//noinspection deprecation
|
||||||
|
touchSlop = ViewConfiguration.getTouchSlop();
|
||||||
|
doubleTapTouchSlop = touchSlop; // Hack rather than adding a hiden method for this
|
||||||
|
doubleTapSlop = DOUBLE_TAP_SLOP;
|
||||||
|
//noinspection
|
||||||
|
} else {
|
||||||
|
final ViewConfiguration configuration = ViewConfiguration.get(context);
|
||||||
|
touchSlop = configuration.getScaledTouchSlop();
|
||||||
|
doubleTapTouchSlop = DOUBLE_TAP_TOUCH_SLOP;
|
||||||
|
doubleTapSlop = configuration.getScaledDoubleTapSlop();
|
||||||
|
}
|
||||||
|
mTouchSlopSquare = touchSlop * touchSlop;
|
||||||
|
mDoubleTapTouchSlopSquare = doubleTapTouchSlop * doubleTapTouchSlop;
|
||||||
|
mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMode(int mode) {
|
||||||
|
mMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsLongpressEnabled(boolean isLongpressEnabled) {
|
||||||
|
mIsLongpressEnabled = isLongpressEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLongpressEnabled() {
|
||||||
|
return mIsLongpressEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean onTouchEvent(MotionEvent ev) {
|
||||||
|
if ((ev.getSource() & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) {
|
||||||
|
return hmListener.onTouch(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mMode == TOUCH_MODE_DIRECT) {
|
||||||
|
// get pointer index from the event object
|
||||||
|
int pointerIndex = ev.getActionIndex();
|
||||||
|
|
||||||
|
// get pointer ID
|
||||||
|
int pointerId = ev.getPointerId(pointerIndex);
|
||||||
|
|
||||||
|
switch(ev.getActionMasked()) {
|
||||||
|
case MotionEvent.ACTION_DOWN:
|
||||||
|
case MotionEvent.ACTION_POINTER_DOWN:
|
||||||
|
mListener.onTouchDown(pointerId, ev.getX(pointerIndex), ev.getY(pointerIndex));
|
||||||
|
break;
|
||||||
|
case MotionEvent.ACTION_UP:
|
||||||
|
case MotionEvent.ACTION_POINTER_UP:
|
||||||
|
mListener.onTouchUp(pointerId);
|
||||||
|
break;
|
||||||
|
case MotionEvent.ACTION_MOVE:
|
||||||
|
for (int i=0; i< ev.getPointerCount(); i++)
|
||||||
|
mListener.onTouchMotion(ev.getPointerId(i), ev.getX(i), ev.getY(i));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mListener.onTouchFrame();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int action = ev.getAction();
|
||||||
|
final boolean pointerUp =
|
||||||
|
(action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP;
|
||||||
|
final int skipIndex = pointerUp ? ev.getActionIndex() : -1;
|
||||||
|
final boolean isGeneratedGesture =
|
||||||
|
(ev.getFlags() & FLAG_IS_GENERATED_GESTURE) != 0;
|
||||||
|
|
||||||
|
// Determine focal point
|
||||||
|
float sumX = 0, sumY = 0;
|
||||||
|
final int count = ev.getPointerCount();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
if (skipIndex == i) continue;
|
||||||
|
sumX += ev.getX(i);
|
||||||
|
sumY += ev.getY(i);
|
||||||
|
}
|
||||||
|
final int div = pointerUp ? count - 1 : count;
|
||||||
|
final float focusX = sumX / div;
|
||||||
|
final float focusY = sumY / div;
|
||||||
|
|
||||||
|
boolean handled = false;
|
||||||
|
|
||||||
|
switch (action & MotionEvent.ACTION_MASK) {
|
||||||
|
case MotionEvent.ACTION_POINTER_DOWN:
|
||||||
|
mDownFocusX = mLastFocusX = focusX;
|
||||||
|
mDownFocusY = mLastFocusY = focusY;
|
||||||
|
// Cancel long press and taps
|
||||||
|
stopDrag();
|
||||||
|
cancelTaps();
|
||||||
|
mHandler.sendEmptyMessageDelayed(RIGHT_TAP, TAP_TIMEOUT);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_POINTER_UP:
|
||||||
|
mDownFocusX = mLastFocusX = focusX;
|
||||||
|
mDownFocusY = mLastFocusY = focusY;
|
||||||
|
stopDrag();
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_DOWN:
|
||||||
|
boolean hadTapMessage = mHandler.hasMessages(TAP);
|
||||||
|
if (hadTapMessage) {
|
||||||
|
mHandler.removeMessages(TAP);
|
||||||
|
mHandler.removeMessages(RIGHT_TAP);
|
||||||
|
}
|
||||||
|
if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null)
|
||||||
|
&& hadTapMessage
|
||||||
|
&& isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
|
||||||
|
// This is a second tap
|
||||||
|
mIsDoubleTapping = true;
|
||||||
|
} else {
|
||||||
|
// This is a first tap
|
||||||
|
mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
mDownFocusX = mLastFocusX = focusX;
|
||||||
|
mDownFocusY = mLastFocusY = focusY;
|
||||||
|
if (mCurrentDownEvent != null) {
|
||||||
|
mCurrentDownEvent.recycle();
|
||||||
|
}
|
||||||
|
mCurrentDownEvent = MotionEvent.obtain(ev);
|
||||||
|
mAlwaysInTapRegion = true;
|
||||||
|
mAlwaysInBiggerTapRegion = true;
|
||||||
|
mStillDown = true;
|
||||||
|
mInLongPress = false;
|
||||||
|
mDeferConfirmSingleTap = false;
|
||||||
|
|
||||||
|
if (mIsLongpressEnabled) {
|
||||||
|
mHandler.removeMessages(LONG_PRESS);
|
||||||
|
if (!mIsDoubleTapping)
|
||||||
|
mHandler.sendEmptyMessageAtTime(LONG_PRESS,
|
||||||
|
mCurrentDownEvent.getDownTime() + LONGPRESS_TIMEOUT);
|
||||||
|
}
|
||||||
|
if (!mIsDoubleTapping)
|
||||||
|
mHandler.sendEmptyMessageAtTime(SHOW_PRESS,
|
||||||
|
mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
|
||||||
|
handled = true;
|
||||||
|
|
||||||
|
if (mMode == TOUCH_MODE_MOUSE)
|
||||||
|
mListener.onPointerMotion((int) ev.getX(), (int) ev.getY());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_MOVE:
|
||||||
|
if (mInContextClick) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
final float scrollX = mLastFocusX - focusX;
|
||||||
|
final float scrollY = mLastFocusY - focusY;
|
||||||
|
if (mAlwaysInTapRegion) {
|
||||||
|
final int deltaX = (int) (focusX - mDownFocusX);
|
||||||
|
final int deltaY = (int) (focusY - mDownFocusY);
|
||||||
|
int distance = (deltaX * deltaX) + (deltaY * deltaY);
|
||||||
|
int slopSquare = isGeneratedGesture ? 0 : mTouchSlopSquare;
|
||||||
|
if (distance > slopSquare) {
|
||||||
|
handled = true;
|
||||||
|
onScroll(ev, scrollX, scrollY);
|
||||||
|
mLastFocusX = focusX;
|
||||||
|
mLastFocusY = focusY;
|
||||||
|
mAlwaysInTapRegion = false;
|
||||||
|
mHandler.removeMessages(TAP);
|
||||||
|
mHandler.removeMessages(RIGHT_TAP);
|
||||||
|
mHandler.removeMessages(SHOW_PRESS);
|
||||||
|
mHandler.removeMessages(LONG_PRESS);
|
||||||
|
}
|
||||||
|
int doubleTapSlopSquare = isGeneratedGesture ? 0 : mDoubleTapTouchSlopSquare;
|
||||||
|
if (distance > doubleTapSlopSquare) {
|
||||||
|
mAlwaysInBiggerTapRegion = false;
|
||||||
|
}
|
||||||
|
} else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
|
||||||
|
handled = true;
|
||||||
|
onScroll(ev, scrollX, scrollY);
|
||||||
|
mLastFocusX = focusX;
|
||||||
|
mLastFocusY = focusY;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_UP:
|
||||||
|
mStillDown = false;
|
||||||
|
MotionEvent currentUpEvent = MotionEvent.obtain(ev);
|
||||||
|
if (mMode == TOUCH_MODE_MOUSE) {
|
||||||
|
stopDrag();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mIsDoubleTapping) {
|
||||||
|
Log.e("1", "1");
|
||||||
|
mListener.onPointerButton(BTN_LEFT, ACTION_DOWN);
|
||||||
|
mListener.onPointerButton(BTN_LEFT, ACTION_UP);
|
||||||
|
mListener.onPointerButton(BTN_LEFT, ACTION_DOWN);
|
||||||
|
mListener.onPointerButton(BTN_LEFT, ACTION_UP);
|
||||||
|
handled = true;
|
||||||
|
} else if (mInLongPress) {
|
||||||
|
Log.e("2", "2");
|
||||||
|
mHandler.removeMessages(TAP);
|
||||||
|
mInLongPress = false;
|
||||||
|
mListener.onPointerButton(BTN_LEFT, ACTION_UP);
|
||||||
|
} else if (mAlwaysInTapRegion) {
|
||||||
|
Log.e("3", "3");
|
||||||
|
//if (mDeferConfirmSingleTap) {
|
||||||
|
//handled = mListener.onSingleTapConfirmed(ev);
|
||||||
|
//}
|
||||||
|
} else if (mHandler.hasMessages(RIGHT_TAP)) {
|
||||||
|
//Log.e("touch", "right tap confirmed");
|
||||||
|
mListener.onPointerButton(BTN_RIGHT, ACTION_DOWN);
|
||||||
|
mListener.onPointerButton(BTN_RIGHT, ACTION_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mPreviousUpEvent != null) {
|
||||||
|
mPreviousUpEvent.recycle();
|
||||||
|
}
|
||||||
|
// Hold the event we obtained above - listeners may have changed the original.
|
||||||
|
mPreviousUpEvent = currentUpEvent;
|
||||||
|
mIsDoubleTapping = false;
|
||||||
|
mInLongPress = false;
|
||||||
|
mDeferConfirmSingleTap = false;
|
||||||
|
mInDrag = false;
|
||||||
|
mWasInDrag = false;
|
||||||
|
mHandler.removeMessages(SHOW_PRESS);
|
||||||
|
mHandler.removeMessages(LONG_PRESS);
|
||||||
|
mHandler.removeMessages(RIGHT_TAP);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_CANCEL:
|
||||||
|
cancel();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onScroll(MotionEvent ev, float scrollX, float scrollY) {
|
||||||
|
if (ev.getPointerCount() == 1) {
|
||||||
|
if (mMode == TOUCH_MODE_MOUSE) {
|
||||||
|
if (!mWasInDrag) {
|
||||||
|
startDrag();
|
||||||
|
mListener.onPointerMotion((int) ev.getX(), (int) ev.getY());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.x -= (int) scrollX;
|
||||||
|
cursor.y -= (int) scrollY;
|
||||||
|
|
||||||
|
if (cursor.x < 0) cursor.x = 0;
|
||||||
|
if (cursor.y < 0) cursor.y = 0;
|
||||||
|
if (cursor.x > target.getWidth()) cursor.x = target.getWidth();
|
||||||
|
if (cursor.y > target.getHeight()) cursor.y = target.getHeight();
|
||||||
|
|
||||||
|
mListener.onPointerMotion(cursor.x, cursor.y);
|
||||||
|
} else if (ev.getPointerCount() == 2) {
|
||||||
|
if (scrollX != 0)
|
||||||
|
mListener.onPointerScroll(MotionEvent.AXIS_Y, (int)scrollX);
|
||||||
|
if (scrollY != 0)
|
||||||
|
mListener.onPointerScroll(MotionEvent.AXIS_X, (int)scrollY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startDrag() {
|
||||||
|
if (mWasInDrag) return;
|
||||||
|
if (!mInDrag) {
|
||||||
|
Log.e("drag", "start");
|
||||||
|
mListener.onPointerButton(BTN_LEFT, ACTION_DOWN);
|
||||||
|
mInDrag = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopDrag() {
|
||||||
|
if (mInDrag) {
|
||||||
|
Log.e("drag", "stop");
|
||||||
|
mListener.onPointerButton(BTN_LEFT, ACTION_UP);
|
||||||
|
mInDrag = false;
|
||||||
|
}
|
||||||
|
mWasInDrag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancel() {
|
||||||
|
mHandler.removeMessages(SHOW_PRESS);
|
||||||
|
mHandler.removeMessages(LONG_PRESS);
|
||||||
|
mHandler.removeMessages(TAP);
|
||||||
|
mHandler.removeMessages(RIGHT_TAP);
|
||||||
|
mIsDoubleTapping = false;
|
||||||
|
mStillDown = false;
|
||||||
|
mAlwaysInTapRegion = false;
|
||||||
|
mAlwaysInBiggerTapRegion = false;
|
||||||
|
mDeferConfirmSingleTap = false;
|
||||||
|
mInLongPress = false;
|
||||||
|
mInContextClick = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancelTaps() {
|
||||||
|
mHandler.removeMessages(SHOW_PRESS);
|
||||||
|
mHandler.removeMessages(LONG_PRESS);
|
||||||
|
mHandler.removeMessages(TAP);
|
||||||
|
mIsDoubleTapping = false;
|
||||||
|
mAlwaysInTapRegion = false;
|
||||||
|
mAlwaysInBiggerTapRegion = false;
|
||||||
|
mDeferConfirmSingleTap = false;
|
||||||
|
mInLongPress = false;
|
||||||
|
mInContextClick = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,
|
||||||
|
MotionEvent secondDown) {
|
||||||
|
if (!mAlwaysInBiggerTapRegion) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final long deltaTime = secondDown.getEventTime() - firstUp.getEventTime();
|
||||||
|
if (deltaTime > DOUBLE_TAP_TIMEOUT || deltaTime < DOUBLE_TAP_MIN_TIME) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int deltaX = (int) firstDown.getX() - (int) secondDown.getX();
|
||||||
|
int deltaY = (int) firstDown.getY() - (int) secondDown.getY();
|
||||||
|
final boolean isGeneratedGesture =
|
||||||
|
(firstDown.getFlags() & FLAG_IS_GENERATED_GESTURE) != 0;
|
||||||
|
int slopSquare = isGeneratedGesture ? 0 : mDoubleTapSlopSquare;
|
||||||
|
return (deltaX * deltaX + deltaY * deltaY < slopSquare);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dispatchLongPress() {
|
||||||
|
mHandler.removeMessages(TAP);
|
||||||
|
mDeferConfirmSingleTap = false;
|
||||||
|
mInLongPress = true;
|
||||||
|
Log.e("tp", "long press");
|
||||||
|
mListener.onPointerButton(BTN_LEFT, ACTION_DOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private class HardwareMouseListener {
|
||||||
|
private int savedBS = 0;
|
||||||
|
private int currentBS = 0;
|
||||||
|
|
||||||
|
boolean isMouseButtonChanged(int mask) {
|
||||||
|
return (savedBS & mask) != (currentBS & mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
int mouseButtonState(int mask) {
|
||||||
|
return ((currentBS & mask) != 0) ? ACTION_DOWN : ACTION_UP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
boolean onTouch(MotionEvent e) {
|
||||||
|
if (e.getAction() == MotionEvent.ACTION_SCROLL) {
|
||||||
|
float scrollY = -5 * e.getAxisValue(MotionEvent.AXIS_VSCROLL);
|
||||||
|
float scrollX = -5 * e.getAxisValue(MotionEvent.AXIS_HSCROLL);
|
||||||
|
|
||||||
|
if (scrollY != 0) mListener.onPointerScroll(AXIS_Y, scrollY);
|
||||||
|
if (scrollX != 0) mListener.onPointerScroll(AXIS_X, scrollX);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
mListener.onPointerMotion((int) e.getX(), (int) e.getY());
|
||||||
|
|
||||||
|
currentBS = e.getButtonState();
|
||||||
|
if (isMouseButtonChanged(MotionEvent.BUTTON_PRIMARY)) {
|
||||||
|
mListener.onPointerButton(BTN_LEFT, mouseButtonState(MotionEvent.BUTTON_PRIMARY));
|
||||||
|
}
|
||||||
|
if (isMouseButtonChanged(MotionEvent.BUTTON_TERTIARY)) {
|
||||||
|
mListener.onPointerButton(BTN_MIDDLE, mouseButtonState(MotionEvent.BUTTON_TERTIARY));
|
||||||
|
}
|
||||||
|
if (isMouseButtonChanged(MotionEvent.BUTTON_SECONDARY)) {
|
||||||
|
mListener.onPointerButton(BTN_RIGHT, mouseButtonState(MotionEvent.BUTTON_SECONDARY));
|
||||||
|
}
|
||||||
|
savedBS = currentBS;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -122,7 +122,7 @@ void LorieBackendAndroid::get_keymap(int *fd, int *size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LorieBackendAndroid::window_change_callback(EGLNativeWindowType win, uint32_t width, uint32_t height, uint32_t physical_width, uint32_t physical_height) {
|
void LorieBackendAndroid::window_change_callback(EGLNativeWindowType win, uint32_t width, uint32_t height, uint32_t physical_width, uint32_t physical_height) {
|
||||||
|
LOGE("WindowChangeCallback");
|
||||||
helper.setWindow(win);
|
helper.setWindow(win);
|
||||||
output_resize(width, height, physical_width, physical_height);
|
output_resize(width, height, physical_width, physical_height);
|
||||||
}
|
}
|
||||||
@ -194,6 +194,42 @@ JNI_DECLARE(LorieService, windowChanged)(JNIEnv *env, jobject __unused instance,
|
|||||||
b->queue.call(&LorieBackendAndroid::window_change_callback, b, win, width, height, mmWidth, mmHeight);
|
b->queue.call(&LorieBackendAndroid::window_change_callback, b, win, width, height, mmWidth, mmHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" JNIEXPORT void JNICALL
|
||||||
|
JNI_DECLARE(LorieService, touchDown)(JNIEnv __unused *env, jobject __unused instance, jlong jcompositor, jint id, jint x, jint y) {
|
||||||
|
if (jcompositor == 0) return;
|
||||||
|
LorieBackendAndroid *b = fromLong(jcompositor);
|
||||||
|
LOGV("JNI: touch down");
|
||||||
|
|
||||||
|
b->touch_down(static_cast<uint32_t>(id), static_cast<uint32_t>(x), static_cast<uint32_t>(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" JNIEXPORT void JNICALL
|
||||||
|
JNI_DECLARE(LorieService, touchMotion)(JNIEnv __unused *env, jobject __unused instance, jlong jcompositor, jint id, jint x, jint y) {
|
||||||
|
if (jcompositor == 0) return;
|
||||||
|
LorieBackendAndroid *b = fromLong(jcompositor);
|
||||||
|
LOGV("JNI: touch motion");
|
||||||
|
|
||||||
|
b->touch_motion(static_cast<uint32_t>(id), static_cast<uint32_t>(x), static_cast<uint32_t>(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" JNIEXPORT void JNICALL
|
||||||
|
JNI_DECLARE(LorieService, touchUp)(JNIEnv __unused *env, jobject __unused instance, jlong jcompositor, jint id) {
|
||||||
|
if (jcompositor == 0) return;
|
||||||
|
LorieBackendAndroid *b = fromLong(jcompositor);
|
||||||
|
LOGV("JNI: touch up");
|
||||||
|
|
||||||
|
b->touch_up(static_cast<uint32_t>(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" JNIEXPORT void JNICALL
|
||||||
|
JNI_DECLARE(LorieService, touchFrame)(JNIEnv __unused *env, jobject __unused instance, jlong jcompositor) {
|
||||||
|
if (jcompositor == 0) return;
|
||||||
|
LorieBackendAndroid *b = fromLong(jcompositor);
|
||||||
|
LOGV("JNI: touch frame");
|
||||||
|
|
||||||
|
b->touch_frame();
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" JNIEXPORT void JNICALL
|
extern "C" JNIEXPORT void JNICALL
|
||||||
JNI_DECLARE(LorieService, pointerMotion)(JNIEnv __unused *env, jobject __unused instance, jlong jcompositor, jint x, jint y) {
|
JNI_DECLARE(LorieService, pointerMotion)(JNIEnv __unused *env, jobject __unused instance, jlong jcompositor, jint x, jint y) {
|
||||||
if (jcompositor == 0) return;
|
if (jcompositor == 0) return;
|
||||||
@ -208,8 +244,8 @@ JNI_DECLARE(LorieService, pointerScroll)(JNIEnv __unused *env, jobject __unused
|
|||||||
if (jcompositor == 0) return;
|
if (jcompositor == 0) return;
|
||||||
LorieBackendAndroid *b = fromLong(jcompositor);
|
LorieBackendAndroid *b = fromLong(jcompositor);
|
||||||
|
|
||||||
LOGV("JNI: pointer scroll %d %d", axis, value);
|
LOGV("JNI: pointer scroll %d %f", axis, value);
|
||||||
b->pointer_scroll(axis, value);
|
b->pointer_scroll(static_cast<uint32_t>(axis), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" JNIEXPORT void JNICALL
|
extern "C" JNIEXPORT void JNICALL
|
||||||
@ -221,21 +257,6 @@ JNI_DECLARE(LorieService, pointerButton)(JNIEnv __unused *env, jobject __unused
|
|||||||
b->pointer_button(static_cast<uint32_t>(button), static_cast<uint32_t>(type));
|
b->pointer_button(static_cast<uint32_t>(button), static_cast<uint32_t>(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*extern "C" JNIEXPORT void JNICALL
|
|
||||||
JNI_DECLARE(LorieService, onTouch)(JNIEnv __unused *env, jobject __unused instance, jlong __unused jcompositor, jint type, jint button, jint x, jint y) {
|
|
||||||
if (jcompositor == 0) return;
|
|
||||||
LorieBackendAndroid *b = fromLong(jcompositor);
|
|
||||||
|
|
||||||
if (type == WL_POINTER_MOTION) {
|
|
||||||
LOGV("JNI: pointer motion %dx%d", x, y);
|
|
||||||
b->pointer_motion(x, y);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LOGV("JNI: pointer button %d type %d", button, type);
|
|
||||||
b->pointer_button(button, type);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
extern "C" void get_character_data(char** layout, int *shift, int *ec, char *ch);
|
extern "C" void get_character_data(char** layout, int *shift, int *ec, char *ch);
|
||||||
extern "C" void android_keycode_get_eventcode(int kc, int *ec, int *shift);
|
extern "C" void android_keycode_get_eventcode(int kc, int *ec, int *shift);
|
||||||
|
|
||||||
@ -257,7 +278,6 @@ JNI_DECLARE(LorieService, keyboardKey)(JNIEnv *env, jobject __unused instance,
|
|||||||
LOGE("kc: %d ec: %d", key_code, event_code);
|
LOGE("kc: %d ec: %d", key_code, event_code);
|
||||||
if (strcmp(b->xkb_names.layout, "us") && event_code != 0) {
|
if (strcmp(b->xkb_names.layout, "us") && event_code != 0) {
|
||||||
b->queue.call(&LorieBackendAndroid::layout_change_callback, b, (char*)"us");
|
b->queue.call(&LorieBackendAndroid::layout_change_callback, b, (char*)"us");
|
||||||
//lorie_layout_change_event(backend, "us");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!key_code && characters) {
|
if (!key_code && characters) {
|
||||||
@ -265,7 +285,6 @@ JNI_DECLARE(LorieService, keyboardKey)(JNIEnv *env, jobject __unused instance,
|
|||||||
get_character_data(&layout, &shift, &event_code, characters);
|
get_character_data(&layout, &shift, &event_code, characters);
|
||||||
if (layout && b->xkb_names.layout != layout) {
|
if (layout && b->xkb_names.layout != layout) {
|
||||||
b->queue.call(&LorieBackendAndroid::layout_change_callback, b, layout);
|
b->queue.call(&LorieBackendAndroid::layout_change_callback, b, layout);
|
||||||
//lorie_layout_change_event(backend, layout);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOGE("Keyboard input: keyCode: %d; eventCode: %d; characters: %s; shift: %d, type: %d", key_code, event_code, characters, shift, type);
|
LOGE("Keyboard input: keyCode: %d; eventCode: %d; characters: %s; shift: %d, type: %d", key_code, event_code, characters, shift, type);
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
|
|
||||||
#ifndef LOG_TAG
|
#ifndef LOG_TAG
|
||||||
#define LOG_TAG "XLorie"
|
#define LOG_TAG "LorieNative"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define LOG(prio, ...) __android_log_print(prio, LOG_TAG, __VA_ARGS__)
|
#define LOG(prio, ...) __android_log_print(prio, LOG_TAG, __VA_ARGS__)
|
||||||
|
@ -15,6 +15,10 @@ LorieCompositor::LorieCompositor() :
|
|||||||
wrapper(terminate),
|
wrapper(terminate),
|
||||||
wrapper(output_redraw),
|
wrapper(output_redraw),
|
||||||
wrapper(output_resize),
|
wrapper(output_resize),
|
||||||
|
wrapper(touch_down),
|
||||||
|
wrapper(touch_motion),
|
||||||
|
wrapper(touch_up),
|
||||||
|
wrapper(touch_frame),
|
||||||
wrapper(pointer_motion),
|
wrapper(pointer_motion),
|
||||||
wrapper(pointer_scroll),
|
wrapper(pointer_scroll),
|
||||||
wrapper(pointer_button),
|
wrapper(pointer_button),
|
||||||
@ -97,21 +101,62 @@ void LorieCompositor::real_output_redraw() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LorieCompositor::real_output_resize(uint32_t width, uint32_t height, uint32_t physical_width, uint32_t physical_height) {
|
void LorieCompositor::real_output_resize(uint32_t width, uint32_t height, uint32_t physical_width, uint32_t physical_height) {
|
||||||
|
// Xwayland segfaults without that line
|
||||||
|
if (width == 0 || height == 0 || physical_width == 0 || physical_height == 0) return;
|
||||||
renderer.resize(width, height, physical_width, physical_height);
|
renderer.resize(width, height, physical_width, physical_height);
|
||||||
output_redraw();
|
output_redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LorieCompositor::real_touch_down(uint32_t id, uint32_t x, uint32_t y) {
|
||||||
|
LorieClient *client = get_toplevel_client();
|
||||||
|
if (toplevel == nullptr || client == nullptr) return;
|
||||||
|
|
||||||
|
wl_fixed_t surface_x = wl_fixed_from_int (x);
|
||||||
|
wl_fixed_t surface_y = wl_fixed_from_int (y);
|
||||||
|
|
||||||
|
client->touch.send_down(next_serial(), LorieUtils::timestamp(), toplevel->resource, id, surface_x, surface_y);
|
||||||
|
renderer.setCursorVisibility(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LorieCompositor::real_touch_motion(uint32_t id, uint32_t x, uint32_t y) {
|
||||||
|
LorieClient *client = get_toplevel_client();
|
||||||
|
if (toplevel == nullptr || client == nullptr) return;
|
||||||
|
|
||||||
|
wl_fixed_t surface_x = wl_fixed_from_int (x);
|
||||||
|
wl_fixed_t surface_y = wl_fixed_from_int (y);
|
||||||
|
|
||||||
|
client->touch.send_motion(LorieUtils::timestamp(), id, surface_x, surface_y);
|
||||||
|
renderer.setCursorVisibility(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LorieCompositor::real_touch_up(uint32_t id) {
|
||||||
|
LorieClient *client = get_toplevel_client();
|
||||||
|
if (toplevel == nullptr || client == nullptr) return;
|
||||||
|
|
||||||
|
client->touch.send_up(next_serial(), LorieUtils::timestamp(), id);
|
||||||
|
renderer.setCursorVisibility(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LorieCompositor::real_touch_frame() {
|
||||||
|
LorieClient *client = get_toplevel_client();
|
||||||
|
if (toplevel == nullptr || client == nullptr) return;
|
||||||
|
|
||||||
|
client->touch.send_frame();
|
||||||
|
renderer.setCursorVisibility(false);
|
||||||
|
}
|
||||||
|
|
||||||
void LorieCompositor::real_pointer_motion(uint32_t x, uint32_t y) {
|
void LorieCompositor::real_pointer_motion(uint32_t x, uint32_t y) {
|
||||||
LorieClient *client = get_toplevel_client();
|
LorieClient *client = get_toplevel_client();
|
||||||
if (client == nullptr) return;
|
if (client == nullptr) return;
|
||||||
|
|
||||||
wl_fixed_t surface_x = wl_fixed_from_int (x);
|
wl_fixed_t surface_x = wl_fixed_from_int (x);
|
||||||
wl_fixed_t surface_y = wl_fixed_from_int (y);
|
wl_fixed_t surface_y = wl_fixed_from_int (y);
|
||||||
|
|
||||||
client->pointer.send_motion(LorieUtils::timestamp(), surface_x, surface_y);
|
client->pointer.send_motion(LorieUtils::timestamp(), surface_x, surface_y);
|
||||||
client->pointer.send_frame();
|
client->pointer.send_frame();
|
||||||
|
|
||||||
renderer.cursorMove(x, y);
|
renderer.setCursorVisibility(true);
|
||||||
|
renderer.cursorMove(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LorieCompositor::real_pointer_scroll(uint32_t axis, float value) {
|
void LorieCompositor::real_pointer_scroll(uint32_t axis, float value) {
|
||||||
@ -123,6 +168,7 @@ void LorieCompositor::real_pointer_scroll(uint32_t axis, float value) {
|
|||||||
client->pointer.send_axis_discrete(axis, (scroll>=0)?1:-1);
|
client->pointer.send_axis_discrete(axis, (scroll>=0)?1:-1);
|
||||||
client->pointer.send_axis(LorieUtils::timestamp(), axis, scroll);
|
client->pointer.send_axis(LorieUtils::timestamp(), axis, scroll);
|
||||||
client->pointer.send_frame();
|
client->pointer.send_frame();
|
||||||
|
renderer.setCursorVisibility(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LorieCompositor::real_pointer_button(uint32_t button, uint32_t state) {
|
void LorieCompositor::real_pointer_button(uint32_t button, uint32_t state) {
|
||||||
@ -132,6 +178,7 @@ void LorieCompositor::real_pointer_button(uint32_t button, uint32_t state) {
|
|||||||
LOGI("pointer button: %d %d", button, state);
|
LOGI("pointer button: %d %d", button, state);
|
||||||
client->pointer.send_button (next_serial(), LorieUtils::timestamp(), button, state);
|
client->pointer.send_button (next_serial(), LorieUtils::timestamp(), button, state);
|
||||||
client->pointer.send_frame();
|
client->pointer.send_frame();
|
||||||
|
renderer.setCursorVisibility(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LorieCompositor::real_keyboard_key(uint32_t key, uint32_t state) {
|
void LorieCompositor::real_keyboard_key(uint32_t key, uint32_t state) {
|
||||||
|
@ -25,6 +25,11 @@ public:
|
|||||||
void real_terminate();
|
void real_terminate();
|
||||||
void real_output_redraw();
|
void real_output_redraw();
|
||||||
void real_output_resize(uint32_t width, uint32_t height, uint32_t physical_width, uint32_t physical_height);
|
void real_output_resize(uint32_t width, uint32_t height, uint32_t physical_width, uint32_t physical_height);
|
||||||
|
|
||||||
|
void real_touch_down(uint32_t id, uint32_t x, uint32_t y);
|
||||||
|
void real_touch_motion(uint32_t id, uint32_t x, uint32_t y);
|
||||||
|
void real_touch_up(uint32_t id);
|
||||||
|
void real_touch_frame();
|
||||||
void real_pointer_motion(uint32_t x, uint32_t y); // absolute values
|
void real_pointer_motion(uint32_t x, uint32_t y); // absolute values
|
||||||
void real_pointer_scroll(uint32_t axis, float value);
|
void real_pointer_scroll(uint32_t axis, float value);
|
||||||
void real_pointer_button(uint32_t button, uint32_t state);
|
void real_pointer_button(uint32_t button, uint32_t state);
|
||||||
@ -37,6 +42,10 @@ public:
|
|||||||
wrapper(terminate);
|
wrapper(terminate);
|
||||||
wrapper(output_redraw);
|
wrapper(output_redraw);
|
||||||
wrapper(output_resize);
|
wrapper(output_resize);
|
||||||
|
wrapper(touch_down);
|
||||||
|
wrapper(touch_motion);
|
||||||
|
wrapper(touch_up);
|
||||||
|
wrapper(touch_frame);
|
||||||
wrapper(pointer_motion);
|
wrapper(pointer_motion);
|
||||||
wrapper(pointer_scroll);
|
wrapper(pointer_scroll);
|
||||||
wrapper(pointer_button);
|
wrapper(pointer_button);
|
||||||
|
@ -14,10 +14,13 @@ public:
|
|||||||
uint32_t physical_width = 270;
|
uint32_t physical_width = 270;
|
||||||
uint32_t physical_height = 158;
|
uint32_t physical_height = 158;
|
||||||
|
|
||||||
|
bool cursorVisible = false;
|
||||||
|
|
||||||
uint32_t hotspot_x, hotspot_y;
|
uint32_t hotspot_x, hotspot_y;
|
||||||
|
|
||||||
void resize(uint32_t w, uint32_t h, uint32_t pw, uint32_t ph);
|
void resize(uint32_t w, uint32_t h, uint32_t pw, uint32_t ph);
|
||||||
void cursorMove(uint32_t x, uint32_t y);
|
void cursorMove(uint32_t x, uint32_t y);
|
||||||
|
void setCursorVisibility(bool visibility);
|
||||||
~LorieRenderer();
|
~LorieRenderer();
|
||||||
private:
|
private:
|
||||||
static void idleDraw(void *data);
|
static void idleDraw(void *data);
|
||||||
|
@ -171,6 +171,11 @@ void LorieRenderer::cursorMove(uint32_t x, uint32_t y) {
|
|||||||
requestRedraw();
|
requestRedraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LorieRenderer::setCursorVisibility(bool visibility) {
|
||||||
|
if (cursorVisible != visibility)
|
||||||
|
cursorVisible = visibility;
|
||||||
|
}
|
||||||
|
|
||||||
void LorieRenderer::idleDraw(void *data) {
|
void LorieRenderer::idleDraw(void *data) {
|
||||||
LorieRenderer* r = static_cast<LorieRenderer*> (data);
|
LorieRenderer* r = static_cast<LorieRenderer*> (data);
|
||||||
|
|
||||||
@ -207,7 +212,8 @@ void LorieRenderer::redraw() {
|
|||||||
compositor.toplevel->texture.draw(-1.0, -1.0, 1.0, 1.0);
|
compositor.toplevel->texture.draw(-1.0, -1.0, 1.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
drawCursor();
|
if (cursorVisible)
|
||||||
|
drawCursor();
|
||||||
|
|
||||||
compositor.swap_buffers();
|
compositor.swap_buffers();
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,8 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
void LorieSeat::on_create() {
|
void LorieSeat::on_create() {
|
||||||
send_capabilities (WL_SEAT_CAPABILITY_POINTER|WL_SEAT_CAPABILITY_KEYBOARD);
|
uint32_t capabilities = (*client)->compositor.input_capabilities();
|
||||||
|
send_capabilities (capabilities);
|
||||||
send_name("default");
|
send_name("default");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,13 @@ extern "C" {
|
|||||||
extern void *blacklist[];
|
extern void *blacklist[];
|
||||||
#define skip_blacklisted(f) for (int z=0; blacklist[z]!=NULL; z++) if (blacklist[z]==f) return;
|
#define skip_blacklisted(f) for (int z=0; blacklist[z]!=NULL; z++) if (blacklist[z]==f) return;
|
||||||
|
|
||||||
|
static bool lock_needed =
|
||||||
|
#ifndef __ANDROID__
|
||||||
|
true;
|
||||||
|
#else
|
||||||
|
false;
|
||||||
|
#endif
|
||||||
|
|
||||||
static pthread_mutex_t lock;
|
static pthread_mutex_t lock;
|
||||||
|
|
||||||
// see https://github.com/littlstar/asprintf.c/blob/master/asprintf.c
|
// see https://github.com/littlstar/asprintf.c/blob/master/asprintf.c
|
||||||
@ -102,16 +109,16 @@ static void LogMessageInternal(int priority, const char *format, ...) {
|
|||||||
void LogMessage(int priority, const char *format, ...) {
|
void LogMessage(int priority, const char *format, ...) {
|
||||||
if (logFunction == NULL) return;
|
if (logFunction == NULL) return;
|
||||||
|
|
||||||
pthread_mutex_lock(&::lock);
|
if (lock_needed) pthread_mutex_lock(&::lock);
|
||||||
va_list argptr;
|
va_list argptr;
|
||||||
va_start(argptr, format);
|
va_start(argptr, format);
|
||||||
logFunction(priority, format, argptr);
|
logFunction(priority, format, argptr);
|
||||||
va_end(argptr);
|
va_end(argptr);
|
||||||
pthread_mutex_unlock(&::lock);
|
if (lock_needed) pthread_mutex_unlock(&::lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogInit(void) {
|
void LogInit(void) {
|
||||||
if (pthread_mutex_init(&::lock, NULL) != 0) {
|
if (lock_needed && pthread_mutex_init(&::lock, NULL) != 0) {
|
||||||
LogMessageInternal(LOG_ERROR, "Logger mutex init failed\n");
|
LogMessageInternal(LOG_ERROR, "Logger mutex init failed\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
22
app/src/main/res/drawable-anydpi-v24/ic_x11_icon.xml
Normal file
22
app/src/main/res/drawable-anydpi-v24/ic_x11_icon.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="429.43842"
|
||||||
|
android:viewportHeight="429.43842"
|
||||||
|
android:tint="#FFFFFF">
|
||||||
|
<group android:translateX="-0.7807971"
|
||||||
|
android:translateY="42.219204">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M71.148,325.001L182.245,179.745L67.868,20.032L141.383,20.093L232.521,145.676L96.25,325.001L71.148,325.001zM288.595,325.104L198.087,199.306L335.491,19.896L359.973,19.896L248.423,165.838L362.111,325.104L288.595,325.104z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M215.484,78.126C206.633,78.126 197.177,78.811 188.664,79.628C203.83,99.408 216.414,116.158 230.897,135.18C222.768,117.687 206.062,100.912 211.837,92.285C217.554,83.746 228.088,85.183 228.891,85.183C245.779,85.183 262.036,87.09 277.234,90.621L280.797,85.714C260.526,80.496 238.508,78.126 215.484,78.126zM306.953,94.089L303.672,98.777C350.498,117.113 382.172,152.137 382.172,192.277C382.172,251.383 313.507,299.339 228.891,299.339C144.274,299.339 75.578,251.383 75.578,192.277C75.578,160.089 95.95,131.197 128.172,111.558L118.266,96.464C67.72,117.896 34.172,155.514 34.172,198.339C34.172,264.973 115.394,319.027 215.484,319.027C315.574,319.027 396.828,264.973 396.828,198.339C396.828,153.906 360.706,115.047 306.953,94.089z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M464.649,155.856h0.505v0h-0.505z"
|
||||||
|
android:strokeAlpha="0.4074074"
|
||||||
|
android:fillColor="#000cff"
|
||||||
|
android:fillAlpha="0.4074074"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
BIN
app/src/main/res/drawable-hdpi/ic_x11_icon.png
Normal file
BIN
app/src/main/res/drawable-hdpi/ic_x11_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 786 B |
BIN
app/src/main/res/drawable-mdpi/ic_x11_icon.png
Normal file
BIN
app/src/main/res/drawable-mdpi/ic_x11_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 522 B |
BIN
app/src/main/res/drawable-xhdpi/ic_x11_icon.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/ic_x11_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_x11_icon.png
Normal file
BIN
app/src/main/res/drawable-xxhdpi/ic_x11_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
27
app/src/main/res/layout/main_activity.xml
Normal file
27
app/src/main/res/layout/main_activity.xml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent" android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<SurfaceView
|
||||||
|
android:id="@+id/lorieView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:id="@+id/sv"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_below="@id/additionalKbd" />
|
||||||
|
|
||||||
|
<com.termux.x11.AdditionalKeyboardView
|
||||||
|
android:id="@+id/additionalKbd"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="35dp"
|
||||||
|
android:layout_alignParentBottom="true" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
</FrameLayout>
|
7
app/src/main/res/layout/preferences_toolbar.xml
Normal file
7
app/src/main/res/layout/preferences_toolbar.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorPrimary"/>
|
13
app/src/main/res/values/arrays.xml
Normal file
13
app/src/main/res/values/arrays.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string-array name="touchscreenInputModesEntries">
|
||||||
|
<item>Direct mode</item>
|
||||||
|
<item>Emulate mouse</item>
|
||||||
|
<item>Emulate touchpad</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="touchscreenInputModesValues">
|
||||||
|
<item>1</item>
|
||||||
|
<item>2</item>
|
||||||
|
<item>3</item>
|
||||||
|
</string-array>
|
||||||
|
</resources>
|
21
app/src/main/res/xml/preferences.xml
Normal file
21
app/src/main/res/xml/preferences.xml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<CheckBoxPreference android:title="Show additional keyboard"
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:summary="Show keyboard with additional keys if soft keyboard is visible"
|
||||||
|
android:key="showAdditionalKbd" />
|
||||||
|
<CheckBoxPreference android:title="Fullscreen mode"
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="fullscreen" />
|
||||||
|
<ListPreference
|
||||||
|
android:title="Touchscreen input mode"
|
||||||
|
android:key="touchMode"
|
||||||
|
android:defaultValue="1"
|
||||||
|
android:entries="@array/touchscreenInputModesEntries"
|
||||||
|
android:entryValues="@array/touchscreenInputModesValues" />
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:title="Show IME with external keyboard"
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="showIMEWhileExternalConnected"
|
||||||
|
android:summary="Show software keyboard while hardware keyboard is connected" />
|
||||||
|
</PreferenceScreen>
|
Loading…
Reference in New Issue
Block a user