Skip to content

Commit

Permalink
Refactoring and redesigning Preferences screen
Browse files Browse the repository at this point in the history
New configurable user actions
Removed string array duplication in arrays.xml
  • Loading branch information
twaik committed Jul 7, 2024
1 parent 9e422b7 commit 033ee16
Show file tree
Hide file tree
Showing 12 changed files with 702 additions and 425 deletions.
16 changes: 8 additions & 8 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ android {
minSdkVersion 26
targetSdkVersion 34
versionCode 15
def commit='-' + 'git rev-parse --verify --short HEAD'.execute().text.trim()
def commit= 'git rev-parse --verify --short HEAD'.execute().text.trim()
def version = "1.03.01"
versionName "${version}-${commit.length()==1?"nongit":commit}-${(new Date()).format("dd.MM.yy")}"
buildConfigField "String", "COMMIT", "\"" + ("git rev-parse HEAD\n".execute().getText().trim() ?: (System.getenv('CURRENT_COMMIT') ?: "NO_COMMIT")) + "\""
Expand Down Expand Up @@ -63,18 +63,18 @@ afterEvaluate {

for (int i = 0; i < preferenceNodes.length; i++) {
def node = preferenceNodes.item(i)
if (node.nodeName == 'EditTextPreference')
preferences << [ type: 'String', key: node.getAttribute("android:key"), default: node.getAttribute("android:defaultValue") ]
if (node.nodeName == 'EditTextPreference' && node.getAttribute("app:key") != "extra_keys_config")
preferences << [ type: 'String', key: node.getAttribute("app:key"), default: node.getAttribute("app:defaultValue") ]
else if (node.nodeName == 'SeekBarPreference')
preferences << [ type: 'Int', key: node.getAttribute("android:key"), default: node.getAttribute("android:defaultValue") ]
preferences << [ type: 'Int', key: node.getAttribute("app:key"), default: node.getAttribute("app:defaultValue") ]
else if (node.nodeName == 'ListPreference') {
def entries = node.getAttribute("android:entries")
def values = node.getAttribute("android:entryValues")
preferences << [type: 'List', key: node.getAttribute("android:key"), default: node.getAttribute("android:defaultValue"),
def entries = node.getAttribute("app:entries")
def values = node.getAttribute("app:entryValues")
preferences << [type: 'List', key: node.getAttribute("app:key"), default: node.getAttribute("app:defaultValue"),
entries: entries.substring(7, entries.length()), values: values.substring(7, values.length())]
}
else if (node.nodeName == 'SwitchPreferenceCompat')
preferences << [ type: 'Boolean', key: node.getAttribute("android:key"), default: node.getAttribute("android:defaultValue") ]
preferences << [ type: 'Boolean', key: node.getAttribute("app:key"), default: node.getAttribute("app:defaultValue") ]
}

def out = file('build/generated/java/com/termux/x11/Prefs.java')
Expand Down
375 changes: 208 additions & 167 deletions app/src/main/java/com/termux/x11/LoriePreferences.java

Large diffs are not rendered by default.

67 changes: 28 additions & 39 deletions app/src/main/java/com/termux/x11/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Canvas;
Expand Down Expand Up @@ -75,7 +75,8 @@
@SuppressLint("ApplySharedPref")
@SuppressWarnings({"deprecation", "unused"})
public class MainActivity extends AppCompatActivity implements View.OnApplyWindowInsetsListener {
static final String ACTION_STOP = "com.termux.x11.ACTION_STOP";
public static final String ACTION_STOP = "com.termux.x11.ACTION_STOP";
public static final String ACTION_CUSTOM = "com.termux.x11.ACTION_CUSTOM";

public static Handler handler = new Handler();
FrameLayout frm;
Expand All @@ -97,6 +98,7 @@ public class MainActivity extends AppCompatActivity implements View.OnApplyWindo

private static boolean oldFullscreen = false;
private static boolean oldHideCutout = false;
private final SharedPreferences.OnSharedPreferenceChangeListener preferencesChangedListener = (__, key) -> onPreferencesChanged(key);

private final BroadcastReceiver receiver = new BroadcastReceiver() {
@SuppressLint("UnspecifiedRegisterReceiverFlag")
Expand Down Expand Up @@ -126,6 +128,9 @@ public void onReceive(Context context, Intent intent) {
Log.d("MainActivity", "preference: " + intent.getStringExtra("key"));
if (!"additionalKbdVisible".equals(intent.getStringExtra("key")))
onPreferencesChanged("");
} else if (ACTION_CUSTOM.equals(intent.getAction())) {
android.util.Log.d("ACTION_CUSTOM", "action " + intent.getStringExtra("what"));
mInputHandler.extractUserActionFromPreferences(prefs, intent.getStringExtra("what")).accept(true);
}
}
};
Expand Down Expand Up @@ -158,7 +163,7 @@ protected void onCreate(Bundle savedInstanceState) {
oldFullscreen = prefs.fullscreen.get();
oldHideCutout = prefs.hideCutout.get();

prefs.get().registerOnSharedPreferenceChangeListener((sharedPreferences, key) -> onPreferencesChanged(key));
prefs.get().registerOnSharedPreferenceChangeListener(preferencesChangedListener);

getWindow().setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS | FLAG_KEEP_SCREEN_ON | FLAG_TRANSLUCENT_STATUS, 0);
requestWindowFeature(Window.FEATURE_NO_TITLE);
Expand Down Expand Up @@ -217,6 +222,7 @@ else if (SamsungDexUtils.checkDeXEnabled(this))
registerReceiver(receiver, new IntentFilter(ACTION_START) {{
addAction(ACTION_PREFERENCES_CHANGED);
addAction(ACTION_STOP);
addAction(ACTION_CUSTOM);
}}, SDK_INT >= VERSION_CODES.TIRAMISU ? RECEIVER_EXPORTED : 0);

inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
Expand Down Expand Up @@ -538,12 +544,19 @@ else if (checkSelfPermission(WRITE_SECURE_SETTINGS) == PERMISSION_GRANTED)

lorieView.requestLayout();
lorieView.invalidate();

for (StatusBarNotification notification: mNotificationManager.getActiveNotifications())
if (notification.getId() == mNotificationId) {
mNotification = buildNotification();
mNotificationManager.notify(mNotificationId, mNotification);
}
}

@Override
public void onResume() {
super.onResume();

mNotification = buildNotification();
mNotificationManager.notify(mNotificationId, mNotification);

setTerminalToolbarView();
Expand All @@ -552,16 +565,12 @@ public void onResume() {

@Override
public void onPause() {
View view = getCurrentFocus();
if (view == null) {
view = getLorieView();
view.requestFocus();
}
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
inputMethodManager.hideSoftInputFromWindow(getWindow().getDecorView().getRootView().getWindowToken(), 0);

for (StatusBarNotification notification: mNotificationManager.getActiveNotifications())
if (notification.getId() == mNotificationId)
mNotificationManager.cancel(mNotificationId);

super.onPause();
}

Expand Down Expand Up @@ -623,38 +632,24 @@ public boolean handleKey(KeyEvent e) {

@SuppressLint("ObsoleteSdkInt")
Notification buildNotification() {
Intent preferencesIntent = new Intent(this, LoriePreferences.class);
preferencesIntent.putExtra("key", "value");
preferencesIntent.setAction(Intent.ACTION_MAIN);

Intent exitIntent = new Intent(ACTION_STOP);
exitIntent.setPackage(getPackageName());

PendingIntent pIntent = PendingIntent.getActivity(this, 0, preferencesIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
PendingIntent pExitIntent = PendingIntent.getBroadcast(this, 0, exitIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);

NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
return new NotificationCompat.Builder(this, getNotificationChannel(notificationManager))
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, getNotificationChannel(mNotificationManager))
.setContentTitle("Termux:X11")
.setSmallIcon(R.drawable.ic_x11_icon)
.setContentText("Pull down to show options")
.setContentIntent(pIntent)
.setOngoing(true)
.setPriority(Notification.PRIORITY_MAX)
.setSilent(true)
.setShowWhen(false)
.setColor(0xFF607D8B)
.addAction(0, "Exit", pExitIntent)
.addAction(0, "Preferences", pIntent)
.build();
.setColor(0xFF607D8B);
return mInputHandler.setupNotification(prefs, builder).build();
}

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_DEFAULT);
channel.setImportance(NotificationManager.IMPORTANCE_DEFAULT);
channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
channel.setImportance(NotificationManager.IMPORTANCE_HIGH);
channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
if (SDK_INT >= VERSION_CODES.Q)
channel.setAllowBubbles(false);
notificationManager.createNotificationChannel(channel);
Expand All @@ -667,14 +662,8 @@ private String getNotificationChannel(NotificationManager notificationManager){
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);

if (newConfig.orientation != orientation) {
View view = getCurrentFocus();
if (view == null) {
view = getLorieView();
view.requestFocus();
}
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
if (newConfig.orientation != orientation)
inputMethodManager.hideSoftInputFromWindow(getWindow().getDecorView().getRootView().getWindowToken(), 0);

orientation = newConfig.orientation;
setTerminalToolbarView();
Expand Down Expand Up @@ -803,7 +792,7 @@ public static void toggleKeyboardVisibility(Context context) {
if (!externalKeyboardConnected || showIMEWhileExternalConnected)
inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
else
inputMethodManager.hideSoftInputFromWindow(getInstance().getLorieView().getWindowToken(), 0);
inputMethodManager.hideSoftInputFromWindow(getInstance().getWindow().getDecorView().getRootView().getWindowToken(), 0);

getInstance().getLorieView().requestFocus();
}
Expand Down Expand Up @@ -879,7 +868,7 @@ public void setExternalKeyboardConnected(boolean connected) {
if (textInput != null)
textInput.setShowSoftInputOnFocus(!connected || showIMEWhileExternalConnected);
if (connected && !showIMEWhileExternalConnected)
inputMethodManager.hideSoftInputFromWindow(getLorieView().getWindowToken(), 0);
inputMethodManager.hideSoftInputFromWindow(getWindow().getDecorView().getRootView().getWindowToken(), 0);
getLorieView().requestFocus();
}
}
63 changes: 60 additions & 3 deletions app/src/main/java/com/termux/x11/input/TouchInputHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import static android.view.KeyEvent.KEYCODE_VOLUME_UP;

import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.PointF;
Expand All @@ -27,6 +28,7 @@
import android.view.ViewConfiguration;

import androidx.annotation.IntDef;
import androidx.core.app.NotificationCompat;
import androidx.core.math.MathUtils;

import com.termux.x11.LoriePreferences;
Expand Down Expand Up @@ -470,7 +472,7 @@ public void reloadPreferences(Prefs p) {
mTouchpadHandler.reloadPreferences(p);
}

private Consumer<Boolean> extractUserActionFromPreferences(Prefs p, String name) {
public Consumer<Boolean> extractUserActionFromPreferences(Prefs p, String name) {
LoriePreferences.PrefsProto.Preference pref = p.keys.get(name + "Action");
if (pref == null)
return noAction;
Expand All @@ -480,12 +482,67 @@ private Consumer<Boolean> extractUserActionFromPreferences(Prefs p, String name)
case "toggle additional key bar": return (down) -> { if (down) mActivity.toggleExtraKeys(); };
case "open preferences": return (down) -> { if (down) mActivity.startActivity(new Intent(mActivity, LoriePreferences.class) {{ setAction(Intent.ACTION_MAIN); }}); };
case "release pointer and keyboard capture": return (down) -> { if (down) setCapturingEnabled(false); };
case "send volume up": return (down) -> MainActivity.getInstance().getLorieView().sendKeyEvent(0, KEYCODE_VOLUME_UP, down);
case "send volume down": return (down) -> MainActivity.getInstance().getLorieView().sendKeyEvent(0, KEYCODE_VOLUME_DOWN, down);
case "exit": return (down) -> { if (down) mActivity.finish(); };
case "send volume up": return (down) -> mActivity.getLorieView().sendKeyEvent(0, KEYCODE_VOLUME_UP, down);
case "send volume down": return (down) -> mActivity.getLorieView().sendKeyEvent(0, KEYCODE_VOLUME_DOWN, down);
default: return noAction;
}
}

public PendingIntent extractIntentFromPreferences(Prefs p, String name, int requestCode) {
LoriePreferences.PrefsProto.Preference pref = p.keys.get(name + "Action");
if (pref == null)
return null;

switch(pref.asList().get()) {
case "open preferences":
return PendingIntent.getActivity(mActivity, requestCode, new Intent(mActivity, LoriePreferences.class) {{
putExtra("key", "value");
setPackage(mActivity.getPackageName());
setAction(Intent.ACTION_MAIN);
}}, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
case "exit":
case "toggle soft keyboard":
case "toggle additional key bar":
case "release pointer and keyboard capture":
return PendingIntent.getBroadcast(mActivity, requestCode, new Intent(MainActivity.ACTION_CUSTOM) {{
putExtra("what", name);
setPackage(mActivity.getPackageName());
}}, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
default: return null;
}
}

public String extractTitleFromPreferences(Prefs p, String name) {
LoriePreferences.PrefsProto.Preference pref = p.keys.get(name + "Action");
if (pref == null)
return null;

switch(pref.asList().get()) {
case "open preferences": return "Preferences";
case "exit": return "Exit";
case "toggle soft keyboard": return "Toggle IME";
case "toggle additional key bar": return "Toggle additional keyboard";
case "release pointer and keyboard capture": return "Release captures";
default: return null;
}
}

public NotificationCompat.Builder setupNotification(Prefs prefs, NotificationCompat.Builder builder) {
PendingIntent i;

if ((i = extractIntentFromPreferences(prefs, "notificationTap", 0)) != null)
builder.setContentIntent(i);

if ((i = extractIntentFromPreferences(prefs, "notificationButton0", 1)) != null)
builder.addAction(0, extractTitleFromPreferences(prefs, "notificationButton0"), i);

if ((i = extractIntentFromPreferences(prefs, "notificationButton1", 2)) != null)
builder.addAction(0, extractTitleFromPreferences(prefs, "notificationButton1"), i);

return builder;
}

public boolean shouldInterceptKeys() {
return !mInjector.pauseKeyInterceptingWithEsc || keyIntercepting;
}
Expand Down
26 changes: 26 additions & 0 deletions app/src/main/res/anim/slide_in_left.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/* //device/apps/common/res/anim/slide_in_left.xml
**
** Copyright 2007, 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.
*/
-->

<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="-50%p" android:toXDelta="0"
android:duration="@android:integer/config_mediumAnimTime"/>
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="@android:integer/config_mediumAnimTime" />
</set>
26 changes: 26 additions & 0 deletions app/src/main/res/anim/slide_in_right.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/* //device/apps/common/res/anim/slide_in_right.xml
**
** Copyright 2007, 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.
*/
-->

<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="50%p" android:toXDelta="0"
android:duration="@android:integer/config_mediumAnimTime"/>
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="@android:integer/config_mediumAnimTime" />
</set>
26 changes: 26 additions & 0 deletions app/src/main/res/anim/slide_out_left.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/* //device/apps/common/res/anim/slide_out_left.xml
**
** Copyright 2007, 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.
*/
-->

<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="0" android:toXDelta="-50%p"
android:duration="@android:integer/config_mediumAnimTime"/>
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
android:duration="@android:integer/config_mediumAnimTime" />
</set>
Loading

0 comments on commit 033ee16

Please sign in to comment.