mirror of
https://github.com/fankes/termux-app.git
synced 2025-09-06 02:35:19 +08:00
Implement GUI based Termux settings manager and a centralized logging framework
The settings activity can be accessed by long pressing on terminal view and selecting "Settings" from the popup shown. It uses the Android's Preference framework. Currently only debugging preferences to set log level and enabling terminal view key logging are provided. The Preference framework by default uses the keys set in `app:key` attribute in the respective preferences XML file to store the values in the default `SharedPreferences` file of the app. However, since we rely on `TermuxPreferenceConstants` and `TermuxPropertyConstants` classes to define key names so that they can be easily shared between termux and its plugin apps, we provide our own `PreferenceDataStore` for storing key/value pairs. The key name in the XML file can optionally be the same. Check `DebuggingPreferencesFragment` class for a sample. Each new preference category fragment should be added to `app/settings/` with its data store. This commit may allow support to be added for modifying `termux.properties` file directly from the UI but that requires more work, since writing to property files with comments require in-place modification. The `Logger` class provides various static functions for logging that should be used from now on instead of directly calling android `Log.*` functions. The log level is automatically loaded from shared preferences at application startup via `TermuxApplication` and set in the static `Logger.CURRENT_LOG_LEVEL` variable. Changing the log level through the settings activity also changes the log level immediately. The 4 supported log levels are: - LOG_LEVEL_OFF which will log nothing. - LOG_LEVEL_NORMAL which will start logging error, warn and info messages and stacktraces. - LOG_LEVEL_DEBUG which will start logging debug messages. - LOG_LEVEL_VERBOSE which will start logging verbose messages. The default log level is `LOG_LEVEL_NORMAL` which will not log debug or verbose messages. Contributors can add useful log entries at those levels where ever they feel is appropriate so that it allows users and devs to more easily help solve issues or find bugs, specially without having to recompile termux after having to manually add general log entries to the source. DO NOT log data that may have private info of users like command arguments at log levels below debug, like `BackgroundJob` was doing previously. Logging to file support may be added later, will require log file rotation support and storage permissions.
This commit is contained in:
@@ -87,6 +87,8 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||||
|
implementation 'androidx.preference:preference:1.1.1'
|
||||||
testImplementation 'junit:junit:4.13.1'
|
testImplementation 'junit:junit:4.13.1'
|
||||||
testImplementation 'org.robolectric:robolectric:4.4'
|
testImplementation 'org.robolectric:robolectric:4.4'
|
||||||
}
|
}
|
||||||
|
@@ -1,17 +1,23 @@
|
|||||||
|
<?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"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="com.termux"
|
package="com.termux"
|
||||||
android:installLocation="internalOnly"
|
android:installLocation="internalOnly"
|
||||||
android:sharedUserId="${TERMUX_PACKAGE_NAME}"
|
android:sharedUserId="${TERMUX_PACKAGE_NAME}"
|
||||||
android:sharedUserLabel="@string/shared_user_label" >
|
android:sharedUserLabel="@string/shared_user_label">
|
||||||
|
|
||||||
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
<uses-feature
|
||||||
<uses-feature android:name="android.software.leanback" android:required="false" />
|
android:name="android.hardware.touchscreen"
|
||||||
|
android:required="false" />
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.software.leanback"
|
||||||
|
android:required="false" />
|
||||||
|
|
||||||
<permission android:name="${TERMUX_PACKAGE_NAME}.permission.RUN_COMMAND"
|
<permission
|
||||||
android:label="@string/run_command_permission_label"
|
android:name="${TERMUX_PACKAGE_NAME}.permission.RUN_COMMAND"
|
||||||
android:description="@string/run_command_permission_description"
|
android:description="@string/run_command_permission_description"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/run_command_permission_label"
|
||||||
android:protectionLevel="dangerous" />
|
android:protectionLevel="dangerous" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
@@ -20,65 +26,95 @@
|
|||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||||
<uses-permission android:name="android.permission.READ_LOGS" />
|
<uses-permission android:name="android.permission.READ_LOGS" />
|
||||||
<uses-permission android:name="android.permission.DUMP" />
|
<uses-permission android:name="android.permission.DUMP" />
|
||||||
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
|
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="@string/application_name"
|
android:name=".app.TermuxApplication"
|
||||||
android:icon="@mipmap/ic_launcher"
|
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
|
||||||
android:banner="@drawable/banner"
|
|
||||||
android:theme="@style/Theme.Termux"
|
|
||||||
|
|
||||||
android:extractNativeLibs="true"
|
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:supportsRtl="false" >
|
android:banner="@drawable/banner"
|
||||||
|
android:extractNativeLibs="true"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/application_name"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:supportsRtl="false"
|
||||||
|
android:theme="@style/Theme.Termux">
|
||||||
|
|
||||||
<!-- This (or rather, value 2.1 or higher) is needed to make the Samsung Galaxy S8
|
<!--
|
||||||
mark the app with "This app is optimized to run in full screen." -->
|
This (or rather, value 2.1 or higher) is needed to make the Samsung Galaxy S8
|
||||||
<meta-data android:name="android.max_aspect" android:value="10.0" />
|
mark the app with "This app is optimized to run in full screen."
|
||||||
|
-->
|
||||||
|
<meta-data
|
||||||
|
android:name="android.max_aspect"
|
||||||
|
android:value="10.0" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".app.TermuxActivity"
|
android:name=".app.TermuxActivity"
|
||||||
android:label="@string/application_name"
|
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|uiMode|keyboard|keyboardHidden|navigation"
|
||||||
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|uiMode|keyboard|keyboardHidden|navigation"
|
android:label="@string/application_name"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
android:resizeableActivity="true"
|
android:resizeableActivity="true"
|
||||||
android:windowSoftInputMode="adjustResize|stateAlwaysVisible" >
|
android:windowSoftInputMode="adjustResize|stateAlwaysVisible">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts" />
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.app.shortcuts"
|
||||||
|
android:resource="@xml/shortcuts" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity-alias
|
||||||
|
android:name=".HomeActivity"
|
||||||
|
android:targetActivity=".app.TermuxActivity">
|
||||||
|
|
||||||
|
<!-- Launch activity automatically on boot on Android Things devices -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.IOT_LAUNCHER" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity-alias>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".app.TermuxHelpActivity"
|
android:name=".app.TermuxHelpActivity"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:theme="@android:style/Theme.Material.Light.DarkActionBar"
|
android:label="@string/application_name"
|
||||||
android:parentActivityName=".app.TermuxActivity"
|
android:parentActivityName=".app.TermuxActivity"
|
||||||
android:resizeableActivity="true"
|
android:resizeableActivity="true"
|
||||||
android:label="@string/application_name" />
|
android:theme="@android:style/Theme.Material.Light.DarkActionBar" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".app.TermuxSettingsActivity"
|
||||||
|
android:label="@string/title_activity_termux_settings"
|
||||||
|
android:theme="@style/Theme.AppCompat.Light.DarkActionBar" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".filepicker.TermuxFileReceiverActivity"
|
android:name=".filepicker.TermuxFileReceiverActivity"
|
||||||
android:label="@string/application_name"
|
|
||||||
android:taskAffinity="${TERMUX_PACKAGE_NAME}.filereceiver"
|
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
|
android:label="@string/application_name"
|
||||||
|
android:noHistory="true"
|
||||||
android:resizeableActivity="true"
|
android:resizeableActivity="true"
|
||||||
android:noHistory="true">
|
android:taskAffinity="${TERMUX_PACKAGE_NAME}.filereceiver">
|
||||||
|
|
||||||
<!-- Accept multiple file types when sending. -->
|
<!-- Accept multiple file types when sending. -->
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND"/>
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
<data android:mimeType="application/*" />
|
<data android:mimeType="application/*" />
|
||||||
<data android:mimeType="audio/*" />
|
<data android:mimeType="audio/*" />
|
||||||
<data android:mimeType="image/*" />
|
<data android:mimeType="image/*" />
|
||||||
@@ -89,8 +125,10 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
<!-- Accept multiple file types to let Termux be usable as generic file viewer. -->
|
<!-- Accept multiple file types to let Termux be usable as generic file viewer. -->
|
||||||
<intent-filter tools:ignore="AppLinkUrlError">
|
<intent-filter tools:ignore="AppLinkUrlError">
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
<data android:mimeType="application/*" />
|
<data android:mimeType="application/*" />
|
||||||
<data android:mimeType="audio/*" />
|
<data android:mimeType="audio/*" />
|
||||||
<data android:mimeType="image/*" />
|
<data android:mimeType="image/*" />
|
||||||
@@ -99,23 +137,11 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity-alias
|
|
||||||
android:name=".HomeActivity"
|
|
||||||
android:targetActivity=".app.TermuxActivity">
|
|
||||||
|
|
||||||
<!-- Launch activity automatically on boot on Android Things devices -->
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
|
||||||
<category android:name="android.intent.category.IOT_LAUNCHER"/>
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
|
||||||
</intent-filter>
|
|
||||||
</activity-alias>
|
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name=".filepicker.TermuxDocumentsProvider"
|
android:name=".filepicker.TermuxDocumentsProvider"
|
||||||
android:authorities="${TERMUX_PACKAGE_NAME}.documents"
|
android:authorities="${TERMUX_PACKAGE_NAME}.documents"
|
||||||
android:grantUriPermissions="true"
|
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
android:grantUriPermissions="true"
|
||||||
android:permission="android.permission.MANAGE_DOCUMENTS">
|
android:permission="android.permission.MANAGE_DOCUMENTS">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.content.action.DOCUMENTS_PROVIDER" />
|
<action android:name="android.content.action.DOCUMENTS_PROVIDER" />
|
||||||
@@ -125,11 +151,10 @@
|
|||||||
<service
|
<service
|
||||||
android:name=".app.TermuxService"
|
android:name=".app.TermuxService"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".app.RunCommandService"
|
android:name=".app.RunCommandService"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:permission="${TERMUX_PACKAGE_NAME}.permission.RUN_COMMAND" >
|
android:permission="${TERMUX_PACKAGE_NAME}.permission.RUN_COMMAND">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="${TERMUX_PACKAGE_NAME}.RUN_COMMAND" />
|
<action android:name="${TERMUX_PACKAGE_NAME}.RUN_COMMAND" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@@ -137,13 +162,19 @@
|
|||||||
|
|
||||||
<receiver android:name=".app.TermuxOpenReceiver" />
|
<receiver android:name=".app.TermuxOpenReceiver" />
|
||||||
|
|
||||||
<provider android:authorities="${TERMUX_PACKAGE_NAME}.files"
|
<provider
|
||||||
android:readPermission="android.permission.permRead"
|
android:name=".app.TermuxOpenReceiver$ContentProvider"
|
||||||
android:exported="true"
|
android:authorities="${TERMUX_PACKAGE_NAME}.files"
|
||||||
android:grantUriPermissions="true"
|
android:exported="true"
|
||||||
android:name=".app.TermuxOpenReceiver$ContentProvider" />
|
android:grantUriPermissions="true"
|
||||||
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
|
android:readPermission="android.permission.permRead" />
|
||||||
<meta-data android:name="com.samsung.android.multidisplay.keep_process_alive" android:value="true"/>
|
|
||||||
|
<meta-data
|
||||||
|
android:name="com.sec.android.support.multiwindow"
|
||||||
|
android:value="true" />
|
||||||
|
<meta-data
|
||||||
|
android:name="com.samsung.android.multidisplay.keep_process_alive"
|
||||||
|
android:value="true" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@@ -4,9 +4,9 @@ import android.app.Activity;
|
|||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.termux.BuildConfig;
|
import com.termux.BuildConfig;
|
||||||
|
import com.termux.app.utils.Logger;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -26,10 +26,10 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public final class BackgroundJob {
|
public final class BackgroundJob {
|
||||||
|
|
||||||
private static final String LOG_TAG = "termux-task";
|
|
||||||
|
|
||||||
final Process mProcess;
|
final Process mProcess;
|
||||||
|
|
||||||
|
private static final String LOG_TAG = "BackgroundJob";
|
||||||
|
|
||||||
public BackgroundJob(String cwd, String fileToExecute, final String[] args, final TermuxService service){
|
public BackgroundJob(String cwd, String fileToExecute, final String[] args, final TermuxService service){
|
||||||
this(cwd, fileToExecute, args, service, null);
|
this(cwd, fileToExecute, args, service, null);
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ public final class BackgroundJob {
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
mProcess = null;
|
mProcess = null;
|
||||||
// TODO: Visible error message?
|
// TODO: Visible error message?
|
||||||
Log.e(LOG_TAG, "Failed running background job: " + processDescription, e);
|
Logger.logStackTraceWithMessage(LOG_TAG, "Failed running background job: " + processDescription, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ public final class BackgroundJob {
|
|||||||
// FIXME: Long lines.
|
// FIXME: Long lines.
|
||||||
while ((line = reader.readLine()) != null) {
|
while ((line = reader.readLine()) != null) {
|
||||||
errResult.append(line).append('\n');
|
errResult.append(line).append('\n');
|
||||||
Log.i(LOG_TAG, "[" + pid + "] stderr: " + line);
|
Logger.logDebug(LOG_TAG, "[" + pid + "] stderr: " + line);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// Ignore.
|
// Ignore.
|
||||||
@@ -79,7 +79,7 @@ public final class BackgroundJob {
|
|||||||
new Thread() {
|
new Thread() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Log.i(LOG_TAG, "[" + pid + "] starting: " + processDescription);
|
Logger.logDebug(LOG_TAG, "[" + pid + "] starting: " + processDescription);
|
||||||
InputStream stdout = mProcess.getInputStream();
|
InputStream stdout = mProcess.getInputStream();
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(stdout, StandardCharsets.UTF_8));
|
BufferedReader reader = new BufferedReader(new InputStreamReader(stdout, StandardCharsets.UTF_8));
|
||||||
|
|
||||||
@@ -87,20 +87,20 @@ public final class BackgroundJob {
|
|||||||
try {
|
try {
|
||||||
// FIXME: Long lines.
|
// FIXME: Long lines.
|
||||||
while ((line = reader.readLine()) != null) {
|
while ((line = reader.readLine()) != null) {
|
||||||
Log.i(LOG_TAG, "[" + pid + "] stdout: " + line);
|
Logger.logDebug(LOG_TAG, "[" + pid + "] stdout: " + line);
|
||||||
outResult.append(line).append('\n');
|
outResult.append(line).append('\n');
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(LOG_TAG, "Error reading output", e);
|
Logger.logStackTraceWithMessage(LOG_TAG, "Error reading output", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
int exitCode = mProcess.waitFor();
|
int exitCode = mProcess.waitFor();
|
||||||
service.onBackgroundJobExited(BackgroundJob.this);
|
service.onBackgroundJobExited(BackgroundJob.this);
|
||||||
if (exitCode == 0) {
|
if (exitCode == 0) {
|
||||||
Log.i(LOG_TAG, "[" + pid + "] exited normally");
|
Logger.logDebug(LOG_TAG, "[" + pid + "] exited normally");
|
||||||
} else {
|
} else {
|
||||||
Log.w(LOG_TAG, "[" + pid + "] exited with code: " + exitCode);
|
Logger.logDebug(LOG_TAG, "[" + pid + "] exited with code: " + exitCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
result.putString("stdout", outResult.toString());
|
result.putString("stdout", outResult.toString());
|
||||||
|
@@ -10,13 +10,13 @@ import android.net.Uri;
|
|||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.termux.R;
|
import com.termux.R;
|
||||||
import com.termux.app.TermuxConstants.TERMUX_APP.RUN_COMMAND_SERVICE;
|
import com.termux.app.TermuxConstants.TERMUX_APP.RUN_COMMAND_SERVICE;
|
||||||
import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_SERVICE;
|
import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_SERVICE;
|
||||||
import com.termux.app.settings.properties.TermuxPropertyConstants;
|
import com.termux.app.settings.properties.TermuxPropertyConstants;
|
||||||
import com.termux.app.settings.properties.TermuxSharedProperties;
|
import com.termux.app.settings.properties.TermuxSharedProperties;
|
||||||
|
import com.termux.app.utils.Logger;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
@@ -84,6 +84,8 @@ public class RunCommandService extends Service {
|
|||||||
private static final String NOTIFICATION_CHANNEL_ID = "termux_run_command_notification_channel";
|
private static final String NOTIFICATION_CHANNEL_ID = "termux_run_command_notification_channel";
|
||||||
private static final int NOTIFICATION_ID = 1338;
|
private static final int NOTIFICATION_ID = 1338;
|
||||||
|
|
||||||
|
private static final String LOG_TAG = "RunCommandService";
|
||||||
|
|
||||||
class LocalBinder extends Binder {
|
class LocalBinder extends Binder {
|
||||||
public final RunCommandService service = RunCommandService.this;
|
public final RunCommandService service = RunCommandService.this;
|
||||||
}
|
}
|
||||||
@@ -107,13 +109,13 @@ public class RunCommandService extends Service {
|
|||||||
|
|
||||||
// If wrong action passed, then just return
|
// If wrong action passed, then just return
|
||||||
if (!RUN_COMMAND_SERVICE.ACTION_RUN_COMMAND.equals(intent.getAction())) {
|
if (!RUN_COMMAND_SERVICE.ACTION_RUN_COMMAND.equals(intent.getAction())) {
|
||||||
Log.e("termux", "Unexpected intent action to RunCommandService: " + intent.getAction());
|
Logger.logError(LOG_TAG, "Unexpected intent action to RunCommandService: " + intent.getAction());
|
||||||
return Service.START_NOT_STICKY;
|
return Service.START_NOT_STICKY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If allow-external-apps property is not set to "true"
|
// If allow-external-apps property is not set to "true"
|
||||||
if (!TermuxSharedProperties.isPropertyValueTrue(this, TermuxPropertyConstants.getTermuxPropertiesFile(), TermuxConstants.PROP_ALLOW_EXTERNAL_APPS)) {
|
if (!TermuxSharedProperties.isPropertyValueTrue(this, TermuxPropertyConstants.getTermuxPropertiesFile(), TermuxConstants.PROP_ALLOW_EXTERNAL_APPS)) {
|
||||||
Log.e("termux", "RunCommandService requires allow-external-apps property to be set to \"true\" in \"" + TermuxConstants.TERMUX_PROPERTIES_PRIMARY_FILE_PATH + "\" file");
|
Logger.logError(LOG_TAG, "RunCommandService requires allow-external-apps property to be set to \"true\" in \"" + TermuxConstants.TERMUX_PROPERTIES_PRIMARY_FILE_PATH + "\" file");
|
||||||
return Service.START_NOT_STICKY;
|
return Service.START_NOT_STICKY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,7 +157,7 @@ public class RunCommandService extends Service {
|
|||||||
|
|
||||||
private Notification buildNotification() {
|
private Notification buildNotification() {
|
||||||
Notification.Builder builder = new Notification.Builder(this);
|
Notification.Builder builder = new Notification.Builder(this);
|
||||||
builder.setContentTitle(getText(R.string.application_name) + " Run Command");
|
builder.setContentTitle(TermuxConstants.TERMUX_APP_NAME + " Run Command");
|
||||||
builder.setSmallIcon(R.drawable.ic_service_notification);
|
builder.setSmallIcon(R.drawable.ic_service_notification);
|
||||||
|
|
||||||
// Use a low priority:
|
// Use a low priority:
|
||||||
@@ -177,7 +179,7 @@ public class RunCommandService extends Service {
|
|||||||
private void setupNotificationChannel() {
|
private void setupNotificationChannel() {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
|
||||||
|
|
||||||
String channelName = "Termux Run Command";
|
String channelName = TermuxConstants.TERMUX_APP_NAME + " Run Command";
|
||||||
int importance = NotificationManager.IMPORTANCE_LOW;
|
int importance = NotificationManager.IMPORTANCE_LOW;
|
||||||
|
|
||||||
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, importance);
|
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, importance);
|
||||||
|
@@ -28,7 +28,6 @@ import android.text.SpannableString;
|
|||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.style.StyleSpan;
|
import android.text.style.StyleSpan;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.ContextMenu;
|
import android.view.ContextMenu;
|
||||||
import android.view.ContextMenu.ContextMenuInfo;
|
import android.view.ContextMenu.ContextMenuInfo;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
@@ -55,7 +54,7 @@ import com.termux.app.terminal.extrakeys.ExtraKeysView;
|
|||||||
import com.termux.app.terminal.FullScreenWorkAround;
|
import com.termux.app.terminal.FullScreenWorkAround;
|
||||||
import com.termux.app.settings.properties.TermuxPropertyConstants;
|
import com.termux.app.settings.properties.TermuxPropertyConstants;
|
||||||
import com.termux.app.settings.properties.TermuxSharedProperties;
|
import com.termux.app.settings.properties.TermuxSharedProperties;
|
||||||
import com.termux.terminal.EmulatorDebug;
|
import com.termux.app.utils.Logger;
|
||||||
import com.termux.terminal.TerminalColors;
|
import com.termux.terminal.TerminalColors;
|
||||||
import com.termux.terminal.TerminalSession;
|
import com.termux.terminal.TerminalSession;
|
||||||
import com.termux.terminal.TerminalSession.SessionChangedCallback;
|
import com.termux.terminal.TerminalSession.SessionChangedCallback;
|
||||||
@@ -100,6 +99,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
private static final int CONTEXTMENU_HELP_ID = 8;
|
private static final int CONTEXTMENU_HELP_ID = 8;
|
||||||
private static final int CONTEXTMENU_TOGGLE_KEEP_SCREEN_ON = 9;
|
private static final int CONTEXTMENU_TOGGLE_KEEP_SCREEN_ON = 9;
|
||||||
private static final int CONTEXTMENU_AUTOFILL_ID = 10;
|
private static final int CONTEXTMENU_AUTOFILL_ID = 10;
|
||||||
|
private static final int CONTEXTMENU_SETTINGS_ID = 11;
|
||||||
|
|
||||||
private static final int MAX_SESSIONS = 8;
|
private static final int MAX_SESSIONS = 8;
|
||||||
|
|
||||||
@@ -145,12 +145,14 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION).build()).build();
|
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION).build()).build();
|
||||||
int mBellSoundId;
|
int mBellSoundId;
|
||||||
|
|
||||||
|
private static final String LOG_TAG = "TermuxActivity";
|
||||||
|
|
||||||
private final BroadcastReceiver mBroadcastReceiever = new BroadcastReceiver() {
|
private final BroadcastReceiver mBroadcastReceiever = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
if (mIsVisible) {
|
if (mIsVisible) {
|
||||||
String whatToReload = intent.getStringExtra(TERMUX_ACTIVITY.EXTRA_RELOAD_STYLE);
|
String whatToReload = intent.getStringExtra(TERMUX_ACTIVITY.EXTRA_RELOAD_STYLE);
|
||||||
Log.d("termux", "Reloading termux style for: " + whatToReload);
|
Logger.logDebug(LOG_TAG, "Reloading termux style for: " + whatToReload);
|
||||||
if ("storage".equals(whatToReload)) {
|
if ("storage".equals(whatToReload)) {
|
||||||
if (ensureStoragePermissionGranted())
|
if (ensureStoragePermissionGranted())
|
||||||
TermuxInstaller.setupStorageSymlinks(TermuxActivity.this);
|
TermuxInstaller.setupStorageSymlinks(TermuxActivity.this);
|
||||||
@@ -190,7 +192,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
final Typeface newTypeface = (fontFile.exists() && fontFile.length() > 0) ? Typeface.createFromFile(fontFile) : Typeface.MONOSPACE;
|
final Typeface newTypeface = (fontFile.exists() && fontFile.length() > 0) ? Typeface.createFromFile(fontFile) : Typeface.MONOSPACE;
|
||||||
mTerminalView.setTypeface(newTypeface);
|
mTerminalView.setTypeface(newTypeface);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(EmulatorDebug.LOG_TAG, "Error in checkForFontAndColors()", e);
|
Logger.logStackTraceWithMessage(LOG_TAG, "Error in checkForFontAndColors()", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,10 +247,12 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
}
|
}
|
||||||
|
|
||||||
mTerminalView = findViewById(R.id.terminal_view);
|
mTerminalView = findViewById(R.id.terminal_view);
|
||||||
mTerminalView.setOnKeyListener(new TermuxViewClient(this));
|
|
||||||
|
mTerminalView.setTerminalViewClient(new TermuxViewClient(this));
|
||||||
|
|
||||||
mTerminalView.setTextSize(mPreferences.getFontSize());
|
mTerminalView.setTextSize(mPreferences.getFontSize());
|
||||||
mTerminalView.setKeepScreenOn(mPreferences.getKeepScreenOn());
|
mTerminalView.setKeepScreenOn(mPreferences.getKeepScreenOn());
|
||||||
|
mTerminalView.setIsTerminalViewKeyLoggingEnabled(mPreferences.getTerminalViewKeyLoggingEnabled());
|
||||||
mTerminalView.requestFocus();
|
mTerminalView.requestFocus();
|
||||||
|
|
||||||
final ViewPager viewPager = findViewById(R.id.viewpager);
|
final ViewPager viewPager = findViewById(R.id.viewpager);
|
||||||
@@ -627,6 +631,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
|
|
||||||
registerReceiver(mBroadcastReceiever, new IntentFilter(TERMUX_ACTIVITY.ACTION_RELOAD_STYLE));
|
registerReceiver(mBroadcastReceiever, new IntentFilter(TERMUX_ACTIVITY.ACTION_RELOAD_STYLE));
|
||||||
|
|
||||||
|
mTerminalView.setIsTerminalViewKeyLoggingEnabled(mPreferences.getTerminalViewKeyLoggingEnabled());
|
||||||
|
|
||||||
// The current terminal session may have changed while being away, force
|
// The current terminal session may have changed while being away, force
|
||||||
// a refresh of the displayed terminal:
|
// a refresh of the displayed terminal:
|
||||||
mTerminalView.onScreenUpdated();
|
mTerminalView.onScreenUpdated();
|
||||||
@@ -742,6 +748,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
menu.add(Menu.NONE, CONTEXTMENU_STYLING_ID, Menu.NONE, R.string.style_terminal);
|
menu.add(Menu.NONE, CONTEXTMENU_STYLING_ID, Menu.NONE, R.string.style_terminal);
|
||||||
menu.add(Menu.NONE, CONTEXTMENU_TOGGLE_KEEP_SCREEN_ON, Menu.NONE, R.string.toggle_keep_screen_on).setCheckable(true).setChecked(mPreferences.getKeepScreenOn());
|
menu.add(Menu.NONE, CONTEXTMENU_TOGGLE_KEEP_SCREEN_ON, Menu.NONE, R.string.toggle_keep_screen_on).setCheckable(true).setChecked(mPreferences.getKeepScreenOn());
|
||||||
menu.add(Menu.NONE, CONTEXTMENU_HELP_ID, Menu.NONE, R.string.help);
|
menu.add(Menu.NONE, CONTEXTMENU_HELP_ID, Menu.NONE, R.string.help);
|
||||||
|
menu.add(Menu.NONE, CONTEXTMENU_SETTINGS_ID, Menu.NONE, R.string.settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Hook system menu to show context menu instead. */
|
/** Hook system menu to show context menu instead. */
|
||||||
@@ -948,6 +955,9 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
case CONTEXTMENU_HELP_ID:
|
case CONTEXTMENU_HELP_ID:
|
||||||
startActivity(new Intent(this, TermuxHelpActivity.class));
|
startActivity(new Intent(this, TermuxHelpActivity.class));
|
||||||
return true;
|
return true;
|
||||||
|
case CONTEXTMENU_SETTINGS_ID:
|
||||||
|
startActivity(new Intent(this, TermuxSettingsActivity.class));
|
||||||
|
return true;
|
||||||
case CONTEXTMENU_TOGGLE_KEEP_SCREEN_ON: {
|
case CONTEXTMENU_TOGGLE_KEEP_SCREEN_ON: {
|
||||||
if(mTerminalView.getKeepScreenOn()) {
|
if(mTerminalView.getKeepScreenOn()) {
|
||||||
mTerminalView.setKeepScreenOn(false);
|
mTerminalView.setKeepScreenOn(false);
|
||||||
@@ -1036,6 +1046,10 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isVisible() {
|
||||||
|
return mIsVisible;
|
||||||
|
}
|
||||||
|
|
||||||
public TermuxService getTermService() {
|
public TermuxService getTermService() {
|
||||||
return mTermService;
|
return mTermService;
|
||||||
}
|
}
|
||||||
@@ -1047,8 +1061,13 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
public ExtraKeysView getExtraKeysView() {
|
public ExtraKeysView getExtraKeysView() {
|
||||||
return mExtraKeysView;
|
return mExtraKeysView;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TermuxSharedProperties getProperties() {
|
public TermuxSharedProperties getProperties() {
|
||||||
return mProperties;
|
return mProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TermuxSharedPreferences getPreferences() {
|
||||||
|
return mPreferences;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
23
app/src/main/java/com/termux/app/TermuxApplication.java
Normal file
23
app/src/main/java/com/termux/app/TermuxApplication.java
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package com.termux.app;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
|
||||||
|
import com.termux.app.settings.preferences.TermuxSharedPreferences;
|
||||||
|
import com.termux.app.utils.Logger;
|
||||||
|
|
||||||
|
|
||||||
|
public class TermuxApplication extends Application {
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
|
||||||
|
updateLogLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateLogLevel() {
|
||||||
|
// Load the log level from shared preferences and set it to the {@link Loggger.CURRENT_LOG_LEVEL}
|
||||||
|
TermuxSharedPreferences preferences = new TermuxSharedPreferences(getApplicationContext());
|
||||||
|
preferences.setLogLevel(null, preferences.getLogLevel());
|
||||||
|
Logger.logDebug("Starting Application");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@@ -7,12 +7,11 @@ import android.content.Context;
|
|||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.system.Os;
|
import android.system.Os;
|
||||||
import android.util.Log;
|
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
|
||||||
import com.termux.R;
|
import com.termux.R;
|
||||||
import com.termux.terminal.EmulatorDebug;
|
import com.termux.app.utils.Logger;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
@@ -46,6 +45,8 @@ import java.util.zip.ZipInputStream;
|
|||||||
*/
|
*/
|
||||||
final class TermuxInstaller {
|
final class TermuxInstaller {
|
||||||
|
|
||||||
|
private static final String LOG_TAG = "TermuxInstaller";
|
||||||
|
|
||||||
/** Performs setup if necessary. */
|
/** Performs setup if necessary. */
|
||||||
static void setupIfNeeded(final Activity activity, final Runnable whenDone) {
|
static void setupIfNeeded(final Activity activity, final Runnable whenDone) {
|
||||||
// Termux can only be run as the primary user (device owner) since only that
|
// Termux can only be run as the primary user (device owner) since only that
|
||||||
@@ -130,7 +131,7 @@ final class TermuxInstaller {
|
|||||||
|
|
||||||
activity.runOnUiThread(whenDone);
|
activity.runOnUiThread(whenDone);
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
Log.e(EmulatorDebug.LOG_TAG, "Bootstrap error", e);
|
Logger.logStackTraceWithMessage(LOG_TAG, "Bootstrap error", e);
|
||||||
activity.runOnUiThread(() -> {
|
activity.runOnUiThread(() -> {
|
||||||
try {
|
try {
|
||||||
new AlertDialog.Builder(activity).setTitle(R.string.bootstrap_error_title).setMessage(R.string.bootstrap_error_body)
|
new AlertDialog.Builder(activity).setTitle(R.string.bootstrap_error_title).setMessage(R.string.bootstrap_error_body)
|
||||||
@@ -200,13 +201,13 @@ final class TermuxInstaller {
|
|||||||
try {
|
try {
|
||||||
deleteFolder(storageDir);
|
deleteFolder(storageDir);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(LOG_TAG, "Could not delete old $HOME/storage, " + e.getMessage());
|
Logger.logError(LOG_TAG, "Could not delete old $HOME/storage, " + e.getMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!storageDir.mkdirs()) {
|
if (!storageDir.mkdirs()) {
|
||||||
Log.e(LOG_TAG, "Unable to mkdirs() for $HOME/storage");
|
Logger.logError(LOG_TAG, "Unable to mkdirs() for $HOME/storage");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,7 +239,7 @@ final class TermuxInstaller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(LOG_TAG, "Error setting up link", e);
|
Logger.logStackTraceWithMessage(LOG_TAG, "Error setting up link", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.start();
|
}.start();
|
||||||
|
@@ -11,10 +11,9 @@ import android.net.Uri;
|
|||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
import android.util.Log;
|
|
||||||
import android.webkit.MimeTypeMap;
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
import com.termux.terminal.EmulatorDebug;
|
import com.termux.app.utils.Logger;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
@@ -24,11 +23,13 @@ import androidx.annotation.NonNull;
|
|||||||
|
|
||||||
public class TermuxOpenReceiver extends BroadcastReceiver {
|
public class TermuxOpenReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
|
private static final String LOG_TAG = "TermuxOpenReceiver";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
final Uri data = intent.getData();
|
final Uri data = intent.getData();
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
Log.e(EmulatorDebug.LOG_TAG, "termux-open: Called without intent data");
|
Logger.logError(LOG_TAG, "termux-open: Called without intent data");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ public class TermuxOpenReceiver extends BroadcastReceiver {
|
|||||||
// Ok.
|
// Ok.
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Log.e(EmulatorDebug.LOG_TAG, "Invalid action '" + intentAction + "', using 'view'");
|
Logger.logError(LOG_TAG, "Invalid action '" + intentAction + "', using 'view'");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,14 +60,14 @@ public class TermuxOpenReceiver extends BroadcastReceiver {
|
|||||||
try {
|
try {
|
||||||
context.startActivity(urlIntent);
|
context.startActivity(urlIntent);
|
||||||
} catch (ActivityNotFoundException e) {
|
} catch (ActivityNotFoundException e) {
|
||||||
Log.e(EmulatorDebug.LOG_TAG, "termux-open: No app handles the url " + data);
|
Logger.logError(LOG_TAG, "termux-open: No app handles the url " + data);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final File fileToShare = new File(filePath);
|
final File fileToShare = new File(filePath);
|
||||||
if (!(fileToShare.isFile() && fileToShare.canRead())) {
|
if (!(fileToShare.isFile() && fileToShare.canRead())) {
|
||||||
Log.e(EmulatorDebug.LOG_TAG, "termux-open: Not a readable file: '" + fileToShare.getAbsolutePath() + "'");
|
Logger.logError(LOG_TAG, "termux-open: Not a readable file: '" + fileToShare.getAbsolutePath() + "'");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +104,7 @@ public class TermuxOpenReceiver extends BroadcastReceiver {
|
|||||||
try {
|
try {
|
||||||
context.startActivity(sendIntent);
|
context.startActivity(sendIntent);
|
||||||
} catch (ActivityNotFoundException e) {
|
} catch (ActivityNotFoundException e) {
|
||||||
Log.e(EmulatorDebug.LOG_TAG, "termux-open: No app handles the url " + data);
|
Logger.logError(LOG_TAG, "termux-open: No app handles the url " + data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -18,14 +18,13 @@ import android.os.Handler;
|
|||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.util.Log;
|
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
|
|
||||||
import com.termux.R;
|
import com.termux.R;
|
||||||
import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_ACTIVITY;
|
import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_ACTIVITY;
|
||||||
import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_SERVICE;
|
import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_SERVICE;
|
||||||
import com.termux.app.settings.preferences.TermuxSharedPreferences;
|
import com.termux.app.settings.preferences.TermuxSharedPreferences;
|
||||||
import com.termux.terminal.EmulatorDebug;
|
import com.termux.app.utils.Logger;
|
||||||
import com.termux.terminal.TerminalSession;
|
import com.termux.terminal.TerminalSession;
|
||||||
import com.termux.terminal.TerminalSession.SessionChangedCallback;
|
import com.termux.terminal.TerminalSession.SessionChangedCallback;
|
||||||
|
|
||||||
@@ -50,6 +49,8 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
|||||||
private static final String NOTIFICATION_CHANNEL_ID = "termux_notification_channel";
|
private static final String NOTIFICATION_CHANNEL_ID = "termux_notification_channel";
|
||||||
private static final int NOTIFICATION_ID = 1337;
|
private static final int NOTIFICATION_ID = 1337;
|
||||||
|
|
||||||
|
private static final String LOG_TAG = "TermuxService";
|
||||||
|
|
||||||
/** This service is only bound from inside the same process and never uses IPC. */
|
/** This service is only bound from inside the same process and never uses IPC. */
|
||||||
class LocalBinder extends Binder {
|
class LocalBinder extends Binder {
|
||||||
public final TermuxService service = TermuxService.this;
|
public final TermuxService service = TermuxService.this;
|
||||||
@@ -91,12 +92,12 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
|||||||
} else if (TERMUX_SERVICE.ACTION_WAKE_LOCK.equals(action)) {
|
} else if (TERMUX_SERVICE.ACTION_WAKE_LOCK.equals(action)) {
|
||||||
if (mWakeLock == null) {
|
if (mWakeLock == null) {
|
||||||
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||||
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, EmulatorDebug.LOG_TAG + ":service-wakelock");
|
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TermuxConstants.TERMUX_APP_NAME.toLowerCase() + ":service-wakelock");
|
||||||
mWakeLock.acquire();
|
mWakeLock.acquire();
|
||||||
|
|
||||||
// http://tools.android.com/tech-docs/lint-in-studio-2-3#TOC-WifiManager-Leak
|
// http://tools.android.com/tech-docs/lint-in-studio-2-3#TOC-WifiManager-Leak
|
||||||
WifiManager wm = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
|
WifiManager wm = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
|
||||||
mWifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, EmulatorDebug.LOG_TAG);
|
mWifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, TermuxConstants.TERMUX_APP_NAME.toLowerCase());
|
||||||
mWifiLock.acquire();
|
mWifiLock.acquire();
|
||||||
|
|
||||||
String packageName = getPackageName();
|
String packageName = getPackageName();
|
||||||
@@ -109,7 +110,7 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
|||||||
try {
|
try {
|
||||||
startActivity(whitelist);
|
startActivity(whitelist);
|
||||||
} catch (ActivityNotFoundException e) {
|
} catch (ActivityNotFoundException e) {
|
||||||
Log.e(EmulatorDebug.LOG_TAG, "Failed to call ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS", e);
|
Logger.logStackTraceWithMessage(LOG_TAG, "Failed to call ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +157,7 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
|||||||
startActivity(new Intent(this, TermuxActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
startActivity(new Intent(this, TermuxActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||||
}
|
}
|
||||||
} else if (action != null) {
|
} else if (action != null) {
|
||||||
Log.e(EmulatorDebug.LOG_TAG, "Unknown TermuxService action: '" + action + "'");
|
Logger.logError(LOG_TAG, "Unknown TermuxService action: '" + action + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this service really do get killed, there is no point restarting it automatically - let the user do on next
|
// If this service really do get killed, there is no point restarting it automatically - let the user do on next
|
||||||
@@ -246,7 +247,7 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
|||||||
try {
|
try {
|
||||||
TermuxInstaller.deleteFolder(termuxTmpDir.getCanonicalFile());
|
TermuxInstaller.deleteFolder(termuxTmpDir.getCanonicalFile());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(EmulatorDebug.LOG_TAG, "Error while removing file at " + termuxTmpDir.getAbsolutePath(), e);
|
Logger.logStackTraceWithMessage(LOG_TAG, "Error while removing file at " + termuxTmpDir.getAbsolutePath(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
termuxTmpDir.mkdirs();
|
termuxTmpDir.mkdirs();
|
||||||
@@ -367,8 +368,8 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
|||||||
private void setupNotificationChannel() {
|
private void setupNotificationChannel() {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
|
||||||
|
|
||||||
String channelName = "Termux";
|
String channelName = TermuxConstants.TERMUX_APP_NAME;
|
||||||
String channelDescription = "Notifications from Termux";
|
String channelDescription = "Notifications from " + TermuxConstants.TERMUX_APP_NAME;
|
||||||
int importance = NotificationManager.IMPORTANCE_LOW;
|
int importance = NotificationManager.IMPORTANCE_LOW;
|
||||||
|
|
||||||
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName,importance);
|
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName,importance);
|
||||||
@@ -376,4 +377,8 @@ public final class TermuxService extends Service implements SessionChangedCallba
|
|||||||
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
manager.createNotificationChannel(channel);
|
manager.createNotificationChannel(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean wantsToStop() {
|
||||||
|
return mWantsToStop;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
43
app/src/main/java/com/termux/app/TermuxSettingsActivity.java
Normal file
43
app/src/main/java/com/termux/app/TermuxSettingsActivity.java
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package com.termux.app;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.preference.PreferenceFragmentCompat;
|
||||||
|
|
||||||
|
import com.termux.R;
|
||||||
|
|
||||||
|
public class TermuxSettingsActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.settings_activity);
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
getSupportFragmentManager()
|
||||||
|
.beginTransaction()
|
||||||
|
.replace(R.id.settings, new RootPreferencesFragment())
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
if (actionBar != null) {
|
||||||
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
|
actionBar.setDisplayShowHomeEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onSupportNavigateUp() {
|
||||||
|
onBackPressed();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class RootPreferencesFragment extends PreferenceFragmentCompat {
|
||||||
|
@Override
|
||||||
|
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||||
|
setPreferencesFromResource(R.xml.root_preferences, rootKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,122 @@
|
|||||||
|
package com.termux.app.settings;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.preference.ListPreference;
|
||||||
|
import androidx.preference.PreferenceCategory;
|
||||||
|
import androidx.preference.PreferenceDataStore;
|
||||||
|
import androidx.preference.PreferenceFragmentCompat;
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import com.termux.R;
|
||||||
|
import com.termux.app.settings.preferences.TermuxPreferenceConstants;
|
||||||
|
import com.termux.app.settings.preferences.TermuxSharedPreferences;
|
||||||
|
import com.termux.app.utils.Logger;
|
||||||
|
|
||||||
|
public class DebuggingPreferencesFragment extends PreferenceFragmentCompat {
|
||||||
|
@Override
|
||||||
|
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||||
|
PreferenceManager preferenceManager = getPreferenceManager();
|
||||||
|
preferenceManager.setPreferenceDataStore(DebuggingPreferencesDataStore.getInstance(getContext()));
|
||||||
|
|
||||||
|
setPreferencesFromResource(R.xml.debugging_preferences, rootKey);
|
||||||
|
|
||||||
|
PreferenceCategory loggingCategory = findPreference("logging");
|
||||||
|
|
||||||
|
if (loggingCategory != null) {
|
||||||
|
final ListPreference logLevelListPreference = setLogLevelListPreferenceData(findPreference("log_level"), getActivity());
|
||||||
|
loggingCategory.addPreference(logLevelListPreference);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ListPreference setLogLevelListPreferenceData(ListPreference logLevelListPreference, Context context) {
|
||||||
|
if(logLevelListPreference == null)
|
||||||
|
logLevelListPreference = new ListPreference(context);
|
||||||
|
|
||||||
|
CharSequence[] logLevels = Logger.getLogLevelsArray();
|
||||||
|
CharSequence[] logLevelLabels = Logger.getLogLevelLabelsArray(context, logLevels, true);
|
||||||
|
|
||||||
|
logLevelListPreference.setEntryValues(logLevels);
|
||||||
|
logLevelListPreference.setEntries(logLevelLabels);
|
||||||
|
|
||||||
|
logLevelListPreference.setKey(TermuxPreferenceConstants.KEY_LOG_LEVEL);
|
||||||
|
logLevelListPreference.setValue(String.valueOf(Logger.getLogLevel()));
|
||||||
|
logLevelListPreference.setDefaultValue(Logger.getLogLevel());
|
||||||
|
|
||||||
|
return logLevelListPreference;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class DebuggingPreferencesDataStore extends PreferenceDataStore {
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
|
private final TermuxSharedPreferences mPreferences;
|
||||||
|
|
||||||
|
private static DebuggingPreferencesDataStore mInstance;
|
||||||
|
|
||||||
|
private DebuggingPreferencesDataStore(Context context) {
|
||||||
|
mContext = context;
|
||||||
|
mPreferences = new TermuxSharedPreferences(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized DebuggingPreferencesDataStore getInstance(Context context) {
|
||||||
|
if (mInstance == null) {
|
||||||
|
mInstance = new DebuggingPreferencesDataStore(context.getApplicationContext());
|
||||||
|
}
|
||||||
|
return mInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public String getString(String key, @Nullable String defValue) {
|
||||||
|
if(key == null) return null;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case "log_level":
|
||||||
|
return String.valueOf(mPreferences.getLogLevel());
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putString(String key, @Nullable String value) {
|
||||||
|
if(key == null) return;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case "log_level":
|
||||||
|
if (value != null) {
|
||||||
|
mPreferences.setLogLevel(mContext, Integer.parseInt(value));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putBoolean(String key, boolean value) {
|
||||||
|
if(key == null) return;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case "terminal_view_key_logging_enabled":
|
||||||
|
mPreferences.setTerminalViewKeyLoggingEnabled(value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getBoolean(String key, boolean defValue) {
|
||||||
|
switch (key) {
|
||||||
|
case "terminal_view_key_logging_enabled":
|
||||||
|
return mPreferences.getTerminalViewKeyLoggingEnabled();
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -3,13 +3,14 @@ package com.termux.app.settings.preferences;
|
|||||||
import com.termux.app.TermuxConstants;
|
import com.termux.app.TermuxConstants;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Version: v0.1.0
|
* Version: v0.2.0
|
||||||
*
|
*
|
||||||
* Changelog
|
* Changelog
|
||||||
*
|
*
|
||||||
* - 0.1.0 (2021-03-12)
|
* - 0.1.0 (2021-03-12)
|
||||||
* - Initial Release.
|
* - Initial Release.
|
||||||
*
|
* - 0.2.0 (2021-03-13)
|
||||||
|
* - Added `KEY_LOG_LEVEL` and `KEY_TERMINAL_VIEW_LOGGING_ENABLED`
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,4 +48,13 @@ public final class TermuxPreferenceConstants {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Defines the key for current termux log level */
|
||||||
|
public static final String KEY_LOG_LEVEL = "log_level";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Defines the key for whether termux terminal view key logging is enabled or not */
|
||||||
|
public static final String KEY_TERMINAL_VIEW_KEY_LOGGING_ENABLED = "terminal_view_key_logging_enabled";
|
||||||
|
public static final boolean DEFAULT_VALUE_TERMINAL_VIEW_KEY_LOGGING_ENABLED = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -2,11 +2,11 @@ package com.termux.app.settings.preferences;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.util.Log;
|
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
|
|
||||||
import com.termux.app.TermuxConstants;
|
import com.termux.app.TermuxConstants;
|
||||||
import com.termux.terminal.EmulatorDebug;
|
import com.termux.app.utils.Logger;
|
||||||
|
import com.termux.app.utils.TermuxUtils;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
@@ -21,17 +21,7 @@ public class TermuxSharedPreferences {
|
|||||||
private int DEFAULT_FONTSIZE;
|
private int DEFAULT_FONTSIZE;
|
||||||
|
|
||||||
public TermuxSharedPreferences(@Nonnull Context context) {
|
public TermuxSharedPreferences(@Nonnull Context context) {
|
||||||
Context mTempContext;
|
mContext = TermuxUtils.getTermuxPackageContext(context);
|
||||||
|
|
||||||
try {
|
|
||||||
mTempContext = context.createPackageContext(TermuxConstants.TERMUX_PACKAGE_NAME, Context.CONTEXT_RESTRICTED);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(EmulatorDebug.LOG_TAG, "Failed to get \"" + TermuxConstants.TERMUX_PACKAGE_NAME + "\" package context", e);
|
|
||||||
Log.e(EmulatorDebug.LOG_TAG, "Force using current context");
|
|
||||||
mTempContext = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
mContext = mTempContext;
|
|
||||||
mSharedPreferences = getSharedPreferences(mContext);
|
mSharedPreferences = getSharedPreferences(mContext);
|
||||||
|
|
||||||
setFontVariables(context);
|
setFontVariables(context);
|
||||||
@@ -135,4 +125,31 @@ public class TermuxSharedPreferences {
|
|||||||
mSharedPreferences.edit().putString(TermuxPreferenceConstants.KEY_CURRENT_SESSION, value).apply();
|
mSharedPreferences.edit().putString(TermuxPreferenceConstants.KEY_CURRENT_SESSION, value).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public int getLogLevel() {
|
||||||
|
try {
|
||||||
|
return mSharedPreferences.getInt(TermuxPreferenceConstants.KEY_LOG_LEVEL, Logger.DEFAULT_LOG_LEVEL);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
Logger.logStackTraceWithMessage("Error getting \"" + TermuxPreferenceConstants.KEY_LOG_LEVEL + "\" from shared preferences", e);
|
||||||
|
return Logger.DEFAULT_LOG_LEVEL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogLevel(Context context, int logLevel) {
|
||||||
|
logLevel = Logger.setLogLevel(context, logLevel);
|
||||||
|
mSharedPreferences.edit().putInt(TermuxPreferenceConstants.KEY_LOG_LEVEL, logLevel).apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public boolean getTerminalViewKeyLoggingEnabled() {
|
||||||
|
return mSharedPreferences.getBoolean(TermuxPreferenceConstants.KEY_TERMINAL_VIEW_KEY_LOGGING_ENABLED, TermuxPreferenceConstants.DEFAULT_VALUE_TERMINAL_VIEW_KEY_LOGGING_ENABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTerminalViewKeyLoggingEnabled(boolean value) {
|
||||||
|
mSharedPreferences.edit().putBoolean(TermuxPreferenceConstants.KEY_TERMINAL_VIEW_KEY_LOGGING_ENABLED, value).apply();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
package com.termux.app.settings.properties;
|
package com.termux.app.settings.properties;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.Log;
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.google.common.primitives.Primitives;
|
import com.google.common.primitives.Primitives;
|
||||||
|
import com.termux.app.utils.Logger;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
@@ -57,6 +57,8 @@ public class SharedProperties {
|
|||||||
|
|
||||||
private final Object mLock = new Object();
|
private final Object mLock = new Object();
|
||||||
|
|
||||||
|
private static final String LOG_TAG = "SharedProperties";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for the SharedProperties class.
|
* Constructor for the SharedProperties class.
|
||||||
*
|
*
|
||||||
@@ -97,7 +99,7 @@ public class SharedProperties {
|
|||||||
Object internalValue;
|
Object internalValue;
|
||||||
for (String key : mPropertiesList) {
|
for (String key : mPropertiesList) {
|
||||||
value = properties.getProperty(key); // value will be null if key does not exist in propertiesFile
|
value = properties.getProperty(key); // value will be null if key does not exist in propertiesFile
|
||||||
Log.d("termux", key + " : " + value);
|
Logger.logDebug(LOG_TAG, key + " : " + value);
|
||||||
|
|
||||||
// Call the {@link SharedPropertiesParser#getInternalPropertyValueFromValue(Context,String,String)}
|
// Call the {@link SharedPropertiesParser#getInternalPropertyValueFromValue(Context,String,String)}
|
||||||
// interface method to get the internal value to store in the {@link #mMap}.
|
// interface method to get the internal value to store in the {@link #mMap}.
|
||||||
@@ -129,13 +131,13 @@ public class SharedProperties {
|
|||||||
public static boolean putToMap(HashMap<String, Object> map, String key, Object value) {
|
public static boolean putToMap(HashMap<String, Object> map, String key, Object value) {
|
||||||
|
|
||||||
if (map == null) {
|
if (map == null) {
|
||||||
Log.e("termux", "Map passed to SharedProperties.putToProperties() is null");
|
Logger.logError(LOG_TAG, "Map passed to SharedProperties.putToProperties() is null");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// null keys are not allowed to be stored in mMap
|
// null keys are not allowed to be stored in mMap
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
Log.e("termux", "Cannot put a null key into properties map");
|
Logger.logError(LOG_TAG, "Cannot put a null key into properties map");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +155,7 @@ public class SharedProperties {
|
|||||||
map.put(key, value);
|
map.put(key, value);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
Log.e("termux", "Cannot put a non-primitive value for the key \"" + key + "\" into properties map");
|
Logger.logError(LOG_TAG, "Cannot put a non-primitive value for the key \"" + key + "\" into properties map");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,13 +174,13 @@ public class SharedProperties {
|
|||||||
public static boolean putToProperties(Properties properties, String key, String value) {
|
public static boolean putToProperties(Properties properties, String key, String value) {
|
||||||
|
|
||||||
if (properties == null) {
|
if (properties == null) {
|
||||||
Log.e("termux", "Properties passed to SharedProperties.putToProperties() is null");
|
Logger.logError(LOG_TAG, "Properties passed to SharedProperties.putToProperties() is null");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// null keys are not allowed to be stored in mMap
|
// null keys are not allowed to be stored in mMap
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
Log.e("termux", "Cannot put a null key into properties");
|
Logger.logError(LOG_TAG, "Cannot put a null key into properties");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,19 +208,19 @@ public class SharedProperties {
|
|||||||
Properties properties = new Properties();
|
Properties properties = new Properties();
|
||||||
|
|
||||||
if (propertiesFile == null) {
|
if (propertiesFile == null) {
|
||||||
Log.e("termux", "Not loading properties since file is null");
|
Logger.logError(LOG_TAG, "Not loading properties since file is null");
|
||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
try (FileInputStream in = new FileInputStream(propertiesFile)) {
|
try (FileInputStream in = new FileInputStream(propertiesFile)) {
|
||||||
Log.v("termux", "Loading properties from \"" + propertiesFile.getAbsolutePath() + "\" file");
|
Logger.logVerbose(LOG_TAG, "Loading properties from \"" + propertiesFile.getAbsolutePath() + "\" file");
|
||||||
properties.load(new InputStreamReader(in, StandardCharsets.UTF_8));
|
properties.load(new InputStreamReader(in, StandardCharsets.UTF_8));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if(context != null)
|
if(context != null)
|
||||||
Toast.makeText(context, "Could not open properties file \"" + propertiesFile.getAbsolutePath() + "\": " + e.getMessage(), Toast.LENGTH_LONG).show();
|
Toast.makeText(context, "Could not open properties file \"" + propertiesFile.getAbsolutePath() + "\": " + e.getMessage(), Toast.LENGTH_LONG).show();
|
||||||
Log.e("termux", "Error loading properties file \"" + propertiesFile.getAbsolutePath() + "\"", e);
|
Logger.logStackTraceWithMessage(LOG_TAG, "Error loading properties file \"" + propertiesFile.getAbsolutePath() + "\"", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,9 +1,8 @@
|
|||||||
package com.termux.app.settings.properties;
|
package com.termux.app.settings.properties;
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableBiMap;
|
import com.google.common.collect.ImmutableBiMap;
|
||||||
import com.termux.app.TermuxConstants;
|
import com.termux.app.TermuxConstants;
|
||||||
|
import com.termux.app.utils.Logger;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -237,7 +236,7 @@ public final class TermuxPropertyConstants {
|
|||||||
if (propertiesFile.isFile() && propertiesFile.canRead()) {
|
if (propertiesFile.isFile() && propertiesFile.canRead()) {
|
||||||
return propertiesFile;
|
return propertiesFile;
|
||||||
} else {
|
} else {
|
||||||
Log.d("termux", "No readable termux.properties file found");
|
Logger.logDebug("No readable termux.properties file found");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,13 +2,12 @@ package com.termux.app.settings.properties;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.util.Log;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.termux.app.terminal.extrakeys.ExtraKeysInfo;
|
import com.termux.app.terminal.extrakeys.ExtraKeysInfo;
|
||||||
import com.termux.app.terminal.KeyboardShortcut;
|
import com.termux.app.terminal.KeyboardShortcut;
|
||||||
|
import com.termux.app.utils.Logger;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
|
|
||||||
@@ -30,6 +29,8 @@ public class TermuxSharedProperties implements SharedPropertiesParser {
|
|||||||
private ExtraKeysInfo mExtraKeysInfo;
|
private ExtraKeysInfo mExtraKeysInfo;
|
||||||
private final List<KeyboardShortcut> mSessionShortcuts = new ArrayList<>();
|
private final List<KeyboardShortcut> mSessionShortcuts = new ArrayList<>();
|
||||||
|
|
||||||
|
private static final String LOG_TAG = "TermuxSharedProperties";
|
||||||
|
|
||||||
public TermuxSharedProperties(@Nonnull Context context) {
|
public TermuxSharedProperties(@Nonnull Context context) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mPropertiesFile = TermuxPropertyConstants.getTermuxPropertiesFile();
|
mPropertiesFile = TermuxPropertyConstants.getTermuxPropertiesFile();
|
||||||
@@ -65,14 +66,14 @@ public class TermuxSharedProperties implements SharedPropertiesParser {
|
|||||||
String extraKeysStyle = (String) getInternalPropertyValue(TermuxPropertyConstants.KEY_EXTRA_KEYS_STYLE, true);
|
String extraKeysStyle = (String) getInternalPropertyValue(TermuxPropertyConstants.KEY_EXTRA_KEYS_STYLE, true);
|
||||||
mExtraKeysInfo = new ExtraKeysInfo(extrakeys, extraKeysStyle);
|
mExtraKeysInfo = new ExtraKeysInfo(extrakeys, extraKeysStyle);
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
Toast.makeText(mContext, "Could not load and set the \"" + TermuxPropertyConstants.KEY_EXTRA_KEYS + "\" property from the properties file: " + e.toString(), Toast.LENGTH_LONG).show();
|
Logger.showToast(mContext, "Could not load and set the \"" + TermuxPropertyConstants.KEY_EXTRA_KEYS + "\" property from the properties file: " + e.toString(), true);
|
||||||
Log.e("termux", "Could not load and set the \"" + TermuxPropertyConstants.KEY_EXTRA_KEYS + "\" property from the properties file: ", e);
|
Logger.logStackTraceWithMessage(LOG_TAG, "Could not load and set the \"" + TermuxPropertyConstants.KEY_EXTRA_KEYS + "\" property from the properties file: ", e);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mExtraKeysInfo = new ExtraKeysInfo(TermuxPropertyConstants.DEFAULT_IVALUE_EXTRA_KEYS, TermuxPropertyConstants.DEFAULT_IVALUE_EXTRA_KEYS_STYLE);
|
mExtraKeysInfo = new ExtraKeysInfo(TermuxPropertyConstants.DEFAULT_IVALUE_EXTRA_KEYS, TermuxPropertyConstants.DEFAULT_IVALUE_EXTRA_KEYS_STYLE);
|
||||||
} catch (JSONException e2) {
|
} catch (JSONException e2) {
|
||||||
Toast.makeText(mContext, "Can't create default extra keys", Toast.LENGTH_LONG).show();
|
Logger.showToast(mContext, "Can't create default extra keys",true);
|
||||||
Log.e("termux", "Could create default extra keys: ", e);
|
Logger.logStackTraceWithMessage(LOG_TAG, "Could create default extra keys: ", e);
|
||||||
mExtraKeysInfo = null;
|
mExtraKeysInfo = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -262,7 +263,7 @@ public class TermuxSharedProperties implements SharedPropertiesParser {
|
|||||||
// A null value can still be returned by
|
// A null value can still be returned by
|
||||||
// {@link #getInternalPropertyValueFromValue(Context,String,String)} for some keys
|
// {@link #getInternalPropertyValueFromValue(Context,String,String)} for some keys
|
||||||
value = getInternalPropertyValueFromValue(mContext, key, null);
|
value = getInternalPropertyValueFromValue(mContext, key, null);
|
||||||
Log.w("termux", "The value for \"" + key + "\" not found in SharedProperties cahce, force returning default value: `" + value + "`");
|
Logger.logWarn(LOG_TAG, "The value for \"" + key + "\" not found in SharedProperties cahce, force returning default value: `" + value + "`");
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -423,7 +424,7 @@ public class TermuxSharedProperties implements SharedPropertiesParser {
|
|||||||
String[] parts = value.toLowerCase().trim().split("\\+");
|
String[] parts = value.toLowerCase().trim().split("\\+");
|
||||||
String input = parts.length == 2 ? parts[1].trim() : null;
|
String input = parts.length == 2 ? parts[1].trim() : null;
|
||||||
if (!(parts.length == 2 && parts[0].trim().equals("ctrl")) || input.isEmpty() || input.length() > 2) {
|
if (!(parts.length == 2 && parts[0].trim().equals("ctrl")) || input.isEmpty() || input.length() > 2) {
|
||||||
Log.e("termux", "Keyboard shortcut '" + key + "' is not Ctrl+<something>");
|
Logger.logError(LOG_TAG, "Keyboard shortcut '" + key + "' is not Ctrl+<something>");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,7 +432,7 @@ public class TermuxSharedProperties implements SharedPropertiesParser {
|
|||||||
int codePoint = c;
|
int codePoint = c;
|
||||||
if (Character.isLowSurrogate(c)) {
|
if (Character.isLowSurrogate(c)) {
|
||||||
if (input.length() != 2 || Character.isHighSurrogate(input.charAt(1))) {
|
if (input.length() != 2 || Character.isHighSurrogate(input.charAt(1))) {
|
||||||
Log.e("termux", "Keyboard shortcut '" + key + "' is not Ctrl+<something>");
|
Logger.logError(LOG_TAG, "Keyboard shortcut '" + key + "' is not Ctrl+<something>");
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
codePoint = Character.toCodePoint(input.charAt(1), c);
|
codePoint = Character.toCodePoint(input.charAt(1), c);
|
||||||
@@ -556,7 +557,7 @@ public class TermuxSharedProperties implements SharedPropertiesParser {
|
|||||||
propertiesDump.append(" null");
|
propertiesDump.append(" null");
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d("termux", propertiesDump.toString());
|
Logger.logDebug(LOG_TAG, propertiesDump.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dumpInternalPropertiesToLog() {
|
public void dumpInternalPropertiesToLog() {
|
||||||
@@ -570,7 +571,7 @@ public class TermuxSharedProperties implements SharedPropertiesParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d("termux", internalPropertiesDump.toString());
|
Logger.logDebug(LOG_TAG, internalPropertiesDump.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -13,6 +13,7 @@ import com.termux.app.TermuxActivity;
|
|||||||
import com.termux.app.TermuxService;
|
import com.termux.app.TermuxService;
|
||||||
import com.termux.app.terminal.extrakeys.ExtraKeysView;
|
import com.termux.app.terminal.extrakeys.ExtraKeysView;
|
||||||
import com.termux.app.settings.properties.TermuxPropertyConstants;
|
import com.termux.app.settings.properties.TermuxPropertyConstants;
|
||||||
|
import com.termux.app.utils.Logger;
|
||||||
import com.termux.terminal.KeyHandler;
|
import com.termux.terminal.KeyHandler;
|
||||||
import com.termux.terminal.TerminalEmulator;
|
import com.termux.terminal.TerminalEmulator;
|
||||||
import com.termux.terminal.TerminalSession;
|
import com.termux.terminal.TerminalSession;
|
||||||
@@ -20,6 +21,7 @@ import com.termux.view.TerminalViewClient;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
import androidx.drawerlayout.widget.DrawerLayout;
|
import androidx.drawerlayout.widget.DrawerLayout;
|
||||||
|
|
||||||
public final class TermuxViewClient implements TerminalViewClient {
|
public final class TermuxViewClient implements TerminalViewClient {
|
||||||
@@ -43,6 +45,8 @@ public final class TermuxViewClient implements TerminalViewClient {
|
|||||||
return scale;
|
return scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSingleTapUp(MotionEvent e) {
|
public void onSingleTapUp(MotionEvent e) {
|
||||||
InputMethodManager mgr = (InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
|
InputMethodManager mgr = (InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
@@ -64,12 +68,16 @@ public final class TermuxViewClient implements TerminalViewClient {
|
|||||||
return mActivity.getProperties().isUsingCtrlSpaceWorkaround();
|
return mActivity.getProperties().isUsingCtrlSpaceWorkaround();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void copyModeChanged(boolean copyMode) {
|
public void copyModeChanged(boolean copyMode) {
|
||||||
// Disable drawer while copying.
|
// Disable drawer while copying.
|
||||||
mActivity.getDrawer().setDrawerLockMode(copyMode ? DrawerLayout.LOCK_MODE_LOCKED_CLOSED : DrawerLayout.LOCK_MODE_UNLOCKED);
|
mActivity.getDrawer().setDrawerLockMode(copyMode ? DrawerLayout.LOCK_MODE_LOCKED_CLOSED : DrawerLayout.LOCK_MODE_UNLOCKED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("RtlHardcoded")
|
@SuppressLint("RtlHardcoded")
|
||||||
@Override
|
@Override
|
||||||
public boolean onKeyDown(int keyCode, KeyEvent e, TerminalSession currentSession) {
|
public boolean onKeyDown(int keyCode, KeyEvent e, TerminalSession currentSession) {
|
||||||
@@ -127,6 +135,26 @@ public final class TermuxViewClient implements TerminalViewClient {
|
|||||||
return handleVirtualKeys(keyCode, e, false);
|
return handleVirtualKeys(keyCode, e, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Handle dedicated volume buttons as virtual keys if applicable. */
|
||||||
|
private boolean handleVirtualKeys(int keyCode, KeyEvent event, boolean down) {
|
||||||
|
InputDevice inputDevice = event.getDevice();
|
||||||
|
if (mActivity.getProperties().areVirtualVolumeKeysDisabled()) {
|
||||||
|
return false;
|
||||||
|
} else if (inputDevice != null && inputDevice.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {
|
||||||
|
// Do not steal dedicated buttons from a full external keyboard.
|
||||||
|
return false;
|
||||||
|
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
|
||||||
|
mVirtualControlKeyDown = down;
|
||||||
|
return true;
|
||||||
|
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
|
||||||
|
mVirtualFnKeyDown = down;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean readControlKey() {
|
public boolean readControlKey() {
|
||||||
return (mActivity.getExtraKeysView() != null && mActivity.getExtraKeysView().readSpecialButton(ExtraKeysView.SpecialButton.CTRL)) || mVirtualControlKeyDown;
|
return (mActivity.getExtraKeysView() != null && mActivity.getExtraKeysView().readSpecialButton(ExtraKeysView.SpecialButton.CTRL)) || mVirtualControlKeyDown;
|
||||||
@@ -137,6 +165,13 @@ public final class TermuxViewClient implements TerminalViewClient {
|
|||||||
return (mActivity.getExtraKeysView() != null && mActivity.getExtraKeysView().readSpecialButton(ExtraKeysView.SpecialButton.ALT));
|
return (mActivity.getExtraKeysView() != null && mActivity.getExtraKeysView().readSpecialButton(ExtraKeysView.SpecialButton.ALT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onLongPress(MotionEvent event) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCodePoint(final int codePoint, boolean ctrlDown, TerminalSession session) {
|
public boolean onCodePoint(final int codePoint, boolean ctrlDown, TerminalSession session) {
|
||||||
if (mVirtualFnKeyDown) {
|
if (mVirtualFnKeyDown) {
|
||||||
@@ -273,27 +308,41 @@ public final class TermuxViewClient implements TerminalViewClient {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onLongPress(MotionEvent event) {
|
public void logError(String tag, String message) {
|
||||||
return false;
|
Logger.logError(tag, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle dedicated volume buttons as virtual keys if applicable. */
|
@Override
|
||||||
private boolean handleVirtualKeys(int keyCode, KeyEvent event, boolean down) {
|
public void logWarn(String tag, String message) {
|
||||||
InputDevice inputDevice = event.getDevice();
|
Logger.logWarn(tag, message);
|
||||||
if (mActivity.getProperties().areVirtualVolumeKeysDisabled()) {
|
}
|
||||||
return false;
|
|
||||||
} else if (inputDevice != null && inputDevice.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {
|
@Override
|
||||||
// Do not steal dedicated buttons from a full external keyboard.
|
public void logInfo(String tag, String message) {
|
||||||
return false;
|
Logger.logInfo(tag, message);
|
||||||
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
|
}
|
||||||
mVirtualControlKeyDown = down;
|
|
||||||
return true;
|
@Override
|
||||||
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
|
public void logDebug(String tag, String message) {
|
||||||
mVirtualFnKeyDown = down;
|
Logger.logDebug(tag, message);
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
return false;
|
@Override
|
||||||
|
public void logVerbose(String tag, String message) {
|
||||||
|
Logger.logVerbose(tag, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logStackTraceWithMessage(String tag, String message, Exception e) {
|
||||||
|
Logger.logStackTraceWithMessage(tag, message, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logStackTrace(String tag, Exception e) {
|
||||||
|
Logger.logStackTrace(tag, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
223
app/src/main/java/com/termux/app/utils/Logger.java
Normal file
223
app/src/main/java/com/termux/app/utils/Logger.java
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
package com.termux.app.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
|
||||||
|
import com.termux.R;
|
||||||
|
import com.termux.app.TermuxConstants;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
|
||||||
|
public class Logger {
|
||||||
|
|
||||||
|
public static final String DEFAULT_LOG_TAG = TermuxConstants.TERMUX_APP_NAME;
|
||||||
|
|
||||||
|
public static final int LOG_LEVEL_OFF = 0; // log nothing
|
||||||
|
public static final int LOG_LEVEL_NORMAL = 1; // start logging error, warn and info messages and stacktraces
|
||||||
|
public static final int LOG_LEVEL_DEBUG = 2; // start logging debug messages
|
||||||
|
public static final int LOG_LEVEL_VERBOSE = 3; // start logging verbose messages
|
||||||
|
|
||||||
|
public static final int DEFAULT_LOG_LEVEL = LOG_LEVEL_NORMAL;
|
||||||
|
|
||||||
|
private static int CURRENT_LOG_LEVEL = DEFAULT_LOG_LEVEL;
|
||||||
|
|
||||||
|
static public void logMesssage(int logLevel, String tag, String message) {
|
||||||
|
if(logLevel == Log.ERROR && CURRENT_LOG_LEVEL >= LOG_LEVEL_NORMAL)
|
||||||
|
Log.e(tag, message);
|
||||||
|
else if(logLevel == Log.WARN && CURRENT_LOG_LEVEL >= LOG_LEVEL_NORMAL)
|
||||||
|
Log.w(tag, message);
|
||||||
|
else if(logLevel == Log.INFO && CURRENT_LOG_LEVEL >= LOG_LEVEL_NORMAL)
|
||||||
|
Log.i(tag, message);
|
||||||
|
else if(logLevel == Log.DEBUG && CURRENT_LOG_LEVEL >= LOG_LEVEL_DEBUG)
|
||||||
|
Log.d(tag, message);
|
||||||
|
else if(logLevel == Log.VERBOSE && CURRENT_LOG_LEVEL >= LOG_LEVEL_VERBOSE)
|
||||||
|
Log.v(tag, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static public void logError(String tag, String message) {
|
||||||
|
logMesssage(Log.ERROR, tag, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void logError(String message) {
|
||||||
|
logMesssage(Log.ERROR, DEFAULT_LOG_TAG, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static public void logWarn(String tag, String message) {
|
||||||
|
logMesssage(Log.WARN, tag, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void logWarn(String message) {
|
||||||
|
logMesssage(Log.WARN, DEFAULT_LOG_TAG, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static public void logInfo(String tag, String message) {
|
||||||
|
logMesssage(Log.INFO, tag, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void logInfo(String message) {
|
||||||
|
logMesssage(Log.INFO, DEFAULT_LOG_TAG, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static public void logDebug(String tag, String message) {
|
||||||
|
logMesssage(Log.DEBUG, tag, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void logDebug(String message) {
|
||||||
|
logMesssage(Log.DEBUG, DEFAULT_LOG_TAG, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static public void logVerbose(String tag, String message) {
|
||||||
|
logMesssage(Log.VERBOSE, tag, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void logVerbose(String message) {
|
||||||
|
logMesssage(Log.VERBOSE, DEFAULT_LOG_TAG, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static public void logErrorAndShowToast(Context context, String tag, String message) {
|
||||||
|
if (context == null) return;
|
||||||
|
|
||||||
|
if(CURRENT_LOG_LEVEL >= LOG_LEVEL_NORMAL) {
|
||||||
|
logError(tag, message);
|
||||||
|
showToast(context, message, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void logErrorAndShowToast(Context context, String message) {
|
||||||
|
logErrorAndShowToast(context, DEFAULT_LOG_TAG, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static public void logDebugAndShowToast(Context context, String tag, String message) {
|
||||||
|
if (context == null) return;
|
||||||
|
|
||||||
|
if(CURRENT_LOG_LEVEL >= LOG_LEVEL_DEBUG) {
|
||||||
|
logDebug(tag, message);
|
||||||
|
showToast(context, message, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void logDebugAndShowToast(Context context, String message) {
|
||||||
|
logDebugAndShowToast(context, DEFAULT_LOG_TAG, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static public void logStackTraceWithMessage(String tag, String message, Exception e) {
|
||||||
|
|
||||||
|
if(CURRENT_LOG_LEVEL >= LOG_LEVEL_NORMAL)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
StringWriter errors = new StringWriter();
|
||||||
|
PrintWriter pw = new PrintWriter(errors);
|
||||||
|
e.printStackTrace(pw);
|
||||||
|
pw.close();
|
||||||
|
if(message != null)
|
||||||
|
Log.e(tag, message + ":\n" + errors.toString());
|
||||||
|
else
|
||||||
|
Log.e(tag, errors.toString());
|
||||||
|
errors.close();
|
||||||
|
} catch (IOException e1) {
|
||||||
|
e1.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void logStackTraceWithMessage(String message, Exception e) {
|
||||||
|
logStackTraceWithMessage(DEFAULT_LOG_TAG, message, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void logStackTrace(String tag, Exception e) {
|
||||||
|
logStackTraceWithMessage(tag, null, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void logStackTrace(Exception e) {
|
||||||
|
logStackTraceWithMessage(DEFAULT_LOG_TAG, null, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static public void showToast(final Context context, final String toastText, boolean longDuration) {
|
||||||
|
if (context == null) return;
|
||||||
|
|
||||||
|
new Handler(Looper.getMainLooper()).post(() -> Toast.makeText(context, toastText, longDuration ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT).show());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static CharSequence[] getLogLevelsArray() {
|
||||||
|
return new CharSequence[]{
|
||||||
|
String.valueOf(LOG_LEVEL_OFF),
|
||||||
|
String.valueOf(LOG_LEVEL_NORMAL),
|
||||||
|
String.valueOf(LOG_LEVEL_DEBUG),
|
||||||
|
String.valueOf(LOG_LEVEL_VERBOSE)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CharSequence[] getLogLevelLabelsArray(Context context, CharSequence[] logLevels, boolean addDefaultTag) {
|
||||||
|
if (logLevels == null) return null;
|
||||||
|
|
||||||
|
CharSequence[] logLevelLabels = new CharSequence[logLevels.length];
|
||||||
|
|
||||||
|
for(int i=0; i<logLevels.length; i++) {
|
||||||
|
logLevelLabels[i] = getLogLevelLabel(context, Integer.parseInt(logLevels[i].toString()), addDefaultTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
return logLevelLabels;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getLogLevelLabel(final Context context, final int logLevel, final boolean addDefaultTag) {
|
||||||
|
String logLabel;
|
||||||
|
switch (logLevel) {
|
||||||
|
case LOG_LEVEL_OFF: logLabel = context.getString(R.string.log_level_off); break;
|
||||||
|
case LOG_LEVEL_NORMAL: logLabel = context.getString(R.string.log_level_normal); break;
|
||||||
|
case LOG_LEVEL_DEBUG: logLabel = context.getString(R.string.log_level_debug); break;
|
||||||
|
case LOG_LEVEL_VERBOSE: logLabel = context.getString(R.string.log_level_verbose); break;
|
||||||
|
default: logLabel = context.getString(R.string.log_level_unknown); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addDefaultTag && logLevel == DEFAULT_LOG_LEVEL)
|
||||||
|
return logLabel + " (default)";
|
||||||
|
else
|
||||||
|
return logLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static int getLogLevel() {
|
||||||
|
return CURRENT_LOG_LEVEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int setLogLevel(Context context, int logLevel) {
|
||||||
|
if(logLevel >= LOG_LEVEL_OFF && logLevel <= LOG_LEVEL_VERBOSE)
|
||||||
|
CURRENT_LOG_LEVEL = logLevel;
|
||||||
|
else
|
||||||
|
CURRENT_LOG_LEVEL = DEFAULT_LOG_LEVEL;
|
||||||
|
|
||||||
|
if(context != null)
|
||||||
|
showToast(context, context.getString(R.string.log_level_value, getLogLevelLabel(context, CURRENT_LOG_LEVEL, false)),true);
|
||||||
|
|
||||||
|
return CURRENT_LOG_LEVEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
18
app/src/main/java/com/termux/app/utils/TermuxUtils.java
Normal file
18
app/src/main/java/com/termux/app/utils/TermuxUtils.java
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package com.termux.app.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.termux.app.TermuxConstants;
|
||||||
|
|
||||||
|
public class TermuxUtils {
|
||||||
|
|
||||||
|
public static Context getTermuxPackageContext(Context context) {
|
||||||
|
try {
|
||||||
|
return context.createPackageContext(TermuxConstants.TERMUX_PACKAGE_NAME, Context.CONTEXT_RESTRICTED);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.logStackTraceWithMessage("Failed to get \"" + TermuxConstants.TERMUX_PACKAGE_NAME + "\" package context. Force using current context.", e);
|
||||||
|
Logger.logError("Force using current context");
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -6,7 +6,6 @@ import android.content.Intent;
|
|||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.provider.OpenableColumns;
|
import android.provider.OpenableColumns;
|
||||||
import android.util.Log;
|
|
||||||
import android.util.Patterns;
|
import android.util.Patterns;
|
||||||
|
|
||||||
import com.termux.R;
|
import com.termux.R;
|
||||||
@@ -14,6 +13,7 @@ import com.termux.app.DialogUtils;
|
|||||||
import com.termux.app.TermuxConstants;
|
import com.termux.app.TermuxConstants;
|
||||||
import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_SERVICE;
|
import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_SERVICE;
|
||||||
import com.termux.app.TermuxService;
|
import com.termux.app.TermuxService;
|
||||||
|
import com.termux.app.utils.Logger;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -39,6 +39,8 @@ public class TermuxFileReceiverActivity extends Activity {
|
|||||||
*/
|
*/
|
||||||
boolean mFinishOnDismissNameDialog = true;
|
boolean mFinishOnDismissNameDialog = true;
|
||||||
|
|
||||||
|
private static final String LOG_TAG = "TermuxFileReceiverActivity";
|
||||||
|
|
||||||
static boolean isSharedTextAnUrl(String sharedText) {
|
static boolean isSharedTextAnUrl(String sharedText) {
|
||||||
return Patterns.WEB_URL.matcher(sharedText).matches()
|
return Patterns.WEB_URL.matcher(sharedText).matches()
|
||||||
|| Pattern.matches("magnet:\\?xt=urn:btih:.*?", sharedText);
|
|| Pattern.matches("magnet:\\?xt=urn:btih:.*?", sharedText);
|
||||||
@@ -111,7 +113,7 @@ public class TermuxFileReceiverActivity extends Activity {
|
|||||||
promptNameAndSave(in, attachmentFileName);
|
promptNameAndSave(in, attachmentFileName);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
showErrorDialogAndQuit("Unable to handle shared content:\n\n" + e.getMessage());
|
showErrorDialogAndQuit("Unable to handle shared content:\n\n" + e.getMessage());
|
||||||
Log.e("termux", "handleContentUri(uri=" + uri + ") failed", e);
|
Logger.logStackTraceWithMessage(LOG_TAG, "handleContentUri(uri=" + uri + ") failed", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +173,7 @@ public class TermuxFileReceiverActivity extends Activity {
|
|||||||
return outFile;
|
return outFile;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
showErrorDialogAndQuit("Error saving file:\n\n" + e);
|
showErrorDialogAndQuit("Error saving file:\n\n" + e);
|
||||||
Log.e("termux", "Error saving file", e);
|
Logger.logStackTraceWithMessage(LOG_TAG, "Error saving file", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
9
app/src/main/res/layout/settings_activity.xml
Normal file
9
app/src/main/res/layout/settings_activity.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/settings"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
</LinearLayout>
|
@@ -63,4 +63,35 @@
|
|||||||
<string name="file_received_title">Save file in ~/downloads/</string>
|
<string name="file_received_title">Save file in ~/downloads/</string>
|
||||||
<string name="file_received_edit_button">Edit</string>
|
<string name="file_received_edit_button">Edit</string>
|
||||||
<string name="file_received_open_folder_button">Open folder</string>
|
<string name="file_received_open_folder_button">Open folder</string>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Termux Settings -->
|
||||||
|
<string name="settings">Settings</string>
|
||||||
|
<string name="title_activity_termux_settings">Termux Settings</string>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Debugging Preferences -->
|
||||||
|
<string name="debugging_preferences">Debugging</string>
|
||||||
|
|
||||||
|
<!-- Logging Category -->
|
||||||
|
<string name="logging_header">Logging</string>
|
||||||
|
|
||||||
|
<!-- Log Level -->
|
||||||
|
<string name="log_level_title">Log Level</string>
|
||||||
|
<string name="log_level_off">"Off"</string>
|
||||||
|
<string name="log_level_normal">"Normal"</string>
|
||||||
|
<string name="log_level_debug">"Debug"</string>
|
||||||
|
<string name="log_level_verbose">"Verbose"</string>
|
||||||
|
<string name="log_level_unknown">"*Unknown*"</string>
|
||||||
|
<string name="log_level_value">Logcat log level set to \"%1$s\"</string>
|
||||||
|
|
||||||
|
<!-- Terminal View Key Logging -->
|
||||||
|
<string name="terminal_view_key_logging_title">Terminal View Key Logging</string>
|
||||||
|
<string name="terminal_view_key_logging_off">Logs will not have entries for terminal view keys. (Default)</string>
|
||||||
|
<string name="terminal_view_key_logging_on">Logcat logs will have entries for terminal view keys. These are very verbose and should be disabled under normal circumstances or will cause performance issues.</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
21
app/src/main/res/xml/debugging_preferences.xml
Normal file
21
app/src/main/res/xml/debugging_preferences.xml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<PreferenceCategory
|
||||||
|
app:key="logging"
|
||||||
|
app:title="@string/logging_header">
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
app:defaultValue="1"
|
||||||
|
app:key="log_level"
|
||||||
|
app:title="@string/log_level_title"
|
||||||
|
app:useSimpleSummaryProvider="true" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
app:key="terminal_view_key_logging_enabled"
|
||||||
|
app:summaryOff="@string/terminal_view_key_logging_off"
|
||||||
|
app:summaryOn="@string/terminal_view_key_logging_on"
|
||||||
|
app:title="@string/terminal_view_key_logging_title" />
|
||||||
|
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
8
app/src/main/res/xml/root_preferences.xml
Normal file
8
app/src/main/res/xml/root_preferences.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
app:title="@string/debugging_preferences"
|
||||||
|
app:summary="Preferences for debugging"
|
||||||
|
app:fragment="com.termux.app.settings.DebuggingPreferencesFragment"/>
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
@@ -12,7 +12,6 @@ import android.text.Editable;
|
|||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.ActionMode;
|
import android.view.ActionMode;
|
||||||
import android.view.HapticFeedbackConstants;
|
import android.view.HapticFeedbackConstants;
|
||||||
import android.view.InputDevice;
|
import android.view.InputDevice;
|
||||||
@@ -31,7 +30,6 @@ import android.widget.Scroller;
|
|||||||
|
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
import com.termux.terminal.EmulatorDebug;
|
|
||||||
import com.termux.terminal.KeyHandler;
|
import com.termux.terminal.KeyHandler;
|
||||||
import com.termux.terminal.TerminalEmulator;
|
import com.termux.terminal.TerminalEmulator;
|
||||||
import com.termux.terminal.TerminalSession;
|
import com.termux.terminal.TerminalSession;
|
||||||
@@ -40,8 +38,8 @@ import com.termux.view.textselection.TextSelectionCursorController;
|
|||||||
/** View displaying and interacting with a {@link TerminalSession}. */
|
/** View displaying and interacting with a {@link TerminalSession}. */
|
||||||
public final class TerminalView extends View {
|
public final class TerminalView extends View {
|
||||||
|
|
||||||
/** Log view key and IME events. */
|
/** Log terminal view key and IME events. */
|
||||||
private static final boolean LOG_KEY_EVENTS = false;
|
private static boolean TERMINAL_VIEW_KEY_LOGGING_ENABLED = false;
|
||||||
|
|
||||||
/** The currently displayed terminal session, whose emulator is {@link #mEmulator}. */
|
/** The currently displayed terminal session, whose emulator is {@link #mEmulator}. */
|
||||||
public TerminalSession mTermSession;
|
public TerminalSession mTermSession;
|
||||||
@@ -76,6 +74,8 @@ public final class TerminalView extends View {
|
|||||||
|
|
||||||
private final boolean mAccessibilityEnabled;
|
private final boolean mAccessibilityEnabled;
|
||||||
|
|
||||||
|
private static final String LOG_TAG = "TerminalView";
|
||||||
|
|
||||||
public TerminalView(Context context, AttributeSet attributes) { // NO_UCD (unused code)
|
public TerminalView(Context context, AttributeSet attributes) { // NO_UCD (unused code)
|
||||||
super(context, attributes);
|
super(context, attributes);
|
||||||
mGestureRecognizer = new GestureAndScaleRecognizer(context, new GestureAndScaleRecognizer.Listener() {
|
mGestureRecognizer = new GestureAndScaleRecognizer(context, new GestureAndScaleRecognizer.Listener() {
|
||||||
@@ -210,11 +210,23 @@ public final class TerminalView extends View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param onKeyListener Listener for all kinds of key events, both hardware and IME (which makes it different from that
|
* @param terminalViewClient Interface for communicating with the terminal view client. It allows
|
||||||
* available with {@link View#setOnKeyListener(OnKeyListener)}.
|
* for getting various configuration options from the client and
|
||||||
|
* for sending back data to the client like logs, key events, both
|
||||||
|
* hardware and IME (which makes it different from that available with
|
||||||
|
* {@link View#setOnKeyListener(OnKeyListener)}, etc.
|
||||||
*/
|
*/
|
||||||
public void setOnKeyListener(TerminalViewClient onKeyListener) {
|
public void setTerminalViewClient(TerminalViewClient terminalViewClient) {
|
||||||
this.mClient = onKeyListener;
|
this.mClient = terminalViewClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets terminal view key logging is enabled or not.
|
||||||
|
*
|
||||||
|
* @param value The boolean value that defines the state.
|
||||||
|
*/
|
||||||
|
public void setIsTerminalViewKeyLoggingEnabled(boolean value) {
|
||||||
|
TERMINAL_VIEW_KEY_LOGGING_ENABLED = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -264,7 +276,7 @@ public final class TerminalView extends View {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean finishComposingText() {
|
public boolean finishComposingText() {
|
||||||
if (LOG_KEY_EVENTS) Log.i(EmulatorDebug.LOG_TAG, "IME: finishComposingText()");
|
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED) mClient.logInfo(LOG_TAG, "IME: finishComposingText()");
|
||||||
super.finishComposingText();
|
super.finishComposingText();
|
||||||
|
|
||||||
sendTextToTerminal(getEditable());
|
sendTextToTerminal(getEditable());
|
||||||
@@ -274,8 +286,8 @@ public final class TerminalView extends View {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean commitText(CharSequence text, int newCursorPosition) {
|
public boolean commitText(CharSequence text, int newCursorPosition) {
|
||||||
if (LOG_KEY_EVENTS) {
|
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED) {
|
||||||
Log.i(EmulatorDebug.LOG_TAG, "IME: commitText(\"" + text + "\", " + newCursorPosition + ")");
|
mClient.logInfo(LOG_TAG, "IME: commitText(\"" + text + "\", " + newCursorPosition + ")");
|
||||||
}
|
}
|
||||||
super.commitText(text, newCursorPosition);
|
super.commitText(text, newCursorPosition);
|
||||||
|
|
||||||
@@ -289,8 +301,8 @@ public final class TerminalView extends View {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean deleteSurroundingText(int leftLength, int rightLength) {
|
public boolean deleteSurroundingText(int leftLength, int rightLength) {
|
||||||
if (LOG_KEY_EVENTS) {
|
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED) {
|
||||||
Log.i(EmulatorDebug.LOG_TAG, "IME: deleteSurroundingText(" + leftLength + ", " + rightLength + ")");
|
mClient.logInfo(LOG_TAG, "IME: deleteSurroundingText(" + leftLength + ", " + rightLength + ")");
|
||||||
}
|
}
|
||||||
// The stock Samsung keyboard with 'Auto check spelling' enabled sends leftLength > 1.
|
// The stock Samsung keyboard with 'Auto check spelling' enabled sends leftLength > 1.
|
||||||
KeyEvent deleteKey = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL);
|
KeyEvent deleteKey = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL);
|
||||||
@@ -521,8 +533,8 @@ public final class TerminalView extends View {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
|
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
|
||||||
if (LOG_KEY_EVENTS)
|
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED)
|
||||||
Log.i(EmulatorDebug.LOG_TAG, "onKeyPreIme(keyCode=" + keyCode + ", event=" + event + ")");
|
mClient.logInfo(LOG_TAG, "onKeyPreIme(keyCode=" + keyCode + ", event=" + event + ")");
|
||||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||||
if (isSelectingText()) {
|
if (isSelectingText()) {
|
||||||
stopTextSelectionMode();
|
stopTextSelectionMode();
|
||||||
@@ -547,8 +559,8 @@ public final class TerminalView extends View {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||||
if (LOG_KEY_EVENTS)
|
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED)
|
||||||
Log.i(EmulatorDebug.LOG_TAG, "onKeyDown(keyCode=" + keyCode + ", isSystem()=" + event.isSystem() + ", event=" + event + ")");
|
mClient.logInfo(LOG_TAG, "onKeyDown(keyCode=" + keyCode + ", isSystem()=" + event.isSystem() + ", event=" + event + ")");
|
||||||
if (mEmulator == null) return true;
|
if (mEmulator == null) return true;
|
||||||
if (isSelectingText()) {
|
if (isSelectingText()) {
|
||||||
stopTextSelectionMode();
|
stopTextSelectionMode();
|
||||||
@@ -575,7 +587,7 @@ public final class TerminalView extends View {
|
|||||||
if (event.isShiftPressed()) keyMod |= KeyHandler.KEYMOD_SHIFT;
|
if (event.isShiftPressed()) keyMod |= KeyHandler.KEYMOD_SHIFT;
|
||||||
if (event.isNumLockOn()) keyMod |= KeyHandler.KEYMOD_NUM_LOCK;
|
if (event.isNumLockOn()) keyMod |= KeyHandler.KEYMOD_NUM_LOCK;
|
||||||
if (!event.isFunctionPressed() && handleKeyCode(keyCode, keyMod)) {
|
if (!event.isFunctionPressed() && handleKeyCode(keyCode, keyMod)) {
|
||||||
if (LOG_KEY_EVENTS) Log.i(EmulatorDebug.LOG_TAG, "handleKeyCode() took key event");
|
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED) mClient.logInfo(LOG_TAG, "handleKeyCode() took key event");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -590,8 +602,8 @@ public final class TerminalView extends View {
|
|||||||
int effectiveMetaState = event.getMetaState() & ~bitsToClear;
|
int effectiveMetaState = event.getMetaState() & ~bitsToClear;
|
||||||
|
|
||||||
int result = event.getUnicodeChar(effectiveMetaState);
|
int result = event.getUnicodeChar(effectiveMetaState);
|
||||||
if (LOG_KEY_EVENTS)
|
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED)
|
||||||
Log.i(EmulatorDebug.LOG_TAG, "KeyEvent#getUnicodeChar(" + effectiveMetaState + ") returned: " + result);
|
mClient.logInfo(LOG_TAG, "KeyEvent#getUnicodeChar(" + effectiveMetaState + ") returned: " + result);
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -617,8 +629,8 @@ public final class TerminalView extends View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void inputCodePoint(int codePoint, boolean controlDownFromEvent, boolean leftAltDownFromEvent) {
|
public void inputCodePoint(int codePoint, boolean controlDownFromEvent, boolean leftAltDownFromEvent) {
|
||||||
if (LOG_KEY_EVENTS) {
|
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED) {
|
||||||
Log.i(EmulatorDebug.LOG_TAG, "inputCodePoint(codePoint=" + codePoint + ", controlDownFromEvent=" + controlDownFromEvent + ", leftAltDownFromEvent="
|
mClient.logInfo(LOG_TAG, "inputCodePoint(codePoint=" + codePoint + ", controlDownFromEvent=" + controlDownFromEvent + ", leftAltDownFromEvent="
|
||||||
+ leftAltDownFromEvent + ")");
|
+ leftAltDownFromEvent + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -692,8 +704,8 @@ public final class TerminalView extends View {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||||
if (LOG_KEY_EVENTS)
|
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED)
|
||||||
Log.i(EmulatorDebug.LOG_TAG, "onKeyUp(keyCode=" + keyCode + ", event=" + event + ")");
|
mClient.logInfo(LOG_TAG, "onKeyUp(keyCode=" + keyCode + ", event=" + event + ")");
|
||||||
if (mEmulator == null) return true;
|
if (mEmulator == null) return true;
|
||||||
|
|
||||||
if (mClient.onKeyUp(keyCode, event)) {
|
if (mClient.onKeyUp(keyCode, event)) {
|
||||||
|
@@ -8,7 +8,7 @@ import com.termux.terminal.TerminalSession;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Input and scale listener which may be set on a {@link TerminalView} through
|
* Input and scale listener which may be set on a {@link TerminalView} through
|
||||||
* {@link TerminalView#setOnKeyListener(TerminalViewClient)}.
|
* {@link TerminalView#setTerminalViewClient(TerminalViewClient)}.
|
||||||
* <p/>
|
* <p/>
|
||||||
*/
|
*/
|
||||||
public interface TerminalViewClient {
|
public interface TerminalViewClient {
|
||||||
@@ -18,6 +18,8 @@ public interface TerminalViewClient {
|
|||||||
*/
|
*/
|
||||||
float onScale(float scale);
|
float onScale(float scale);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On a single tap on the terminal if terminal mouse reporting not enabled.
|
* On a single tap on the terminal if terminal mouse reporting not enabled.
|
||||||
*/
|
*/
|
||||||
@@ -29,18 +31,41 @@ public interface TerminalViewClient {
|
|||||||
|
|
||||||
boolean shouldUseCtrlSpaceWorkaround();
|
boolean shouldUseCtrlSpaceWorkaround();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void copyModeChanged(boolean copyMode);
|
void copyModeChanged(boolean copyMode);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
boolean onKeyDown(int keyCode, KeyEvent e, TerminalSession session);
|
boolean onKeyDown(int keyCode, KeyEvent e, TerminalSession session);
|
||||||
|
|
||||||
boolean onKeyUp(int keyCode, KeyEvent e);
|
boolean onKeyUp(int keyCode, KeyEvent e);
|
||||||
|
|
||||||
|
boolean onLongPress(MotionEvent event);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
boolean readControlKey();
|
boolean readControlKey();
|
||||||
|
|
||||||
boolean readAltKey();
|
boolean readAltKey();
|
||||||
|
|
||||||
|
|
||||||
boolean onCodePoint(int codePoint, boolean ctrlDown, TerminalSession session);
|
boolean onCodePoint(int codePoint, boolean ctrlDown, TerminalSession session);
|
||||||
|
|
||||||
boolean onLongPress(MotionEvent event);
|
|
||||||
|
|
||||||
|
void logError(String tag, String message);
|
||||||
|
|
||||||
|
void logWarn(String tag, String message);
|
||||||
|
|
||||||
|
void logInfo(String tag, String message);
|
||||||
|
|
||||||
|
void logDebug(String tag, String message);
|
||||||
|
|
||||||
|
void logVerbose(String tag, String message);
|
||||||
|
|
||||||
|
void logStackTraceWithMessage(String tag, String message, Exception e);
|
||||||
|
|
||||||
|
void logStackTrace(String tag, Exception e);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user