diff --git a/app/build.gradle b/app/build.gradle
index c1f0c6df..52c51d4b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -87,6 +87,8 @@ android {
}
dependencies {
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'androidx.preference:preference:1.1.1'
testImplementation 'junit:junit:4.13.1'
testImplementation 'org.robolectric:robolectric:4.4'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0a2e70a6..346f6d3b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,17 +1,23 @@
+
+ android:sharedUserLabel="@string/shared_user_label">
-
-
+
+
-
@@ -20,65 +26,95 @@
-
-
+
+
+ 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">
-
-
+
+
+ android:windowSoftInputMode="adjustResize|stateAlwaysVisible">
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:theme="@android:style/Theme.Material.Light.DarkActionBar" />
+
+
+ android:taskAffinity="${TERMUX_PACKAGE_NAME}.filereceiver">
+
-
+
+
+
@@ -89,8 +125,10 @@
-
+
+
+
@@ -99,23 +137,11 @@
-
-
-
-
-
-
-
-
-
-
@@ -125,11 +151,10 @@
-
+ android:permission="${TERMUX_PACKAGE_NAME}.permission.RUN_COMMAND">
@@ -137,13 +162,19 @@
-
-
-
+
+
+
+
diff --git a/app/src/main/java/com/termux/app/BackgroundJob.java b/app/src/main/java/com/termux/app/BackgroundJob.java
index 3f502d16..61e32317 100644
--- a/app/src/main/java/com/termux/app/BackgroundJob.java
+++ b/app/src/main/java/com/termux/app/BackgroundJob.java
@@ -4,9 +4,9 @@ import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
-import android.util.Log;
import com.termux.BuildConfig;
+import com.termux.app.utils.Logger;
import java.io.BufferedReader;
import java.io.File;
@@ -26,10 +26,10 @@ import java.util.List;
*/
public final class BackgroundJob {
- private static final String LOG_TAG = "termux-task";
-
final Process mProcess;
+ private static final String LOG_TAG = "BackgroundJob";
+
public BackgroundJob(String cwd, String fileToExecute, final String[] args, final TermuxService service){
this(cwd, fileToExecute, args, service, null);
}
@@ -47,7 +47,7 @@ public final class BackgroundJob {
} catch (IOException e) {
mProcess = null;
// 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;
}
@@ -67,7 +67,7 @@ public final class BackgroundJob {
// FIXME: Long lines.
while ((line = reader.readLine()) != null) {
errResult.append(line).append('\n');
- Log.i(LOG_TAG, "[" + pid + "] stderr: " + line);
+ Logger.logDebug(LOG_TAG, "[" + pid + "] stderr: " + line);
}
} catch (IOException e) {
// Ignore.
@@ -79,7 +79,7 @@ public final class BackgroundJob {
new Thread() {
@Override
public void run() {
- Log.i(LOG_TAG, "[" + pid + "] starting: " + processDescription);
+ Logger.logDebug(LOG_TAG, "[" + pid + "] starting: " + processDescription);
InputStream stdout = mProcess.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(stdout, StandardCharsets.UTF_8));
@@ -87,20 +87,20 @@ public final class BackgroundJob {
try {
// FIXME: Long lines.
while ((line = reader.readLine()) != null) {
- Log.i(LOG_TAG, "[" + pid + "] stdout: " + line);
+ Logger.logDebug(LOG_TAG, "[" + pid + "] stdout: " + line);
outResult.append(line).append('\n');
}
} catch (IOException e) {
- Log.e(LOG_TAG, "Error reading output", e);
+ Logger.logStackTraceWithMessage(LOG_TAG, "Error reading output", e);
}
try {
int exitCode = mProcess.waitFor();
service.onBackgroundJobExited(BackgroundJob.this);
if (exitCode == 0) {
- Log.i(LOG_TAG, "[" + pid + "] exited normally");
+ Logger.logDebug(LOG_TAG, "[" + pid + "] exited normally");
} else {
- Log.w(LOG_TAG, "[" + pid + "] exited with code: " + exitCode);
+ Logger.logDebug(LOG_TAG, "[" + pid + "] exited with code: " + exitCode);
}
result.putString("stdout", outResult.toString());
diff --git a/app/src/main/java/com/termux/app/RunCommandService.java b/app/src/main/java/com/termux/app/RunCommandService.java
index f186e5b2..8745d31a 100644
--- a/app/src/main/java/com/termux/app/RunCommandService.java
+++ b/app/src/main/java/com/termux/app/RunCommandService.java
@@ -10,13 +10,13 @@ import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
-import android.util.Log;
import com.termux.R;
import com.termux.app.TermuxConstants.TERMUX_APP.RUN_COMMAND_SERVICE;
import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_SERVICE;
import com.termux.app.settings.properties.TermuxPropertyConstants;
import com.termux.app.settings.properties.TermuxSharedProperties;
+import com.termux.app.utils.Logger;
import java.io.File;
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 int NOTIFICATION_ID = 1338;
+ private static final String LOG_TAG = "RunCommandService";
+
class LocalBinder extends Binder {
public final RunCommandService service = RunCommandService.this;
}
@@ -107,13 +109,13 @@ public class RunCommandService extends Service {
// If wrong action passed, then just return
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;
}
// If allow-external-apps property is not set to "true"
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;
}
@@ -155,7 +157,7 @@ public class RunCommandService extends Service {
private Notification buildNotification() {
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);
// Use a low priority:
@@ -177,7 +179,7 @@ public class RunCommandService extends Service {
private void setupNotificationChannel() {
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;
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, importance);
diff --git a/app/src/main/java/com/termux/app/TermuxActivity.java b/app/src/main/java/com/termux/app/TermuxActivity.java
index ed12ec61..8b75f7ee 100644
--- a/app/src/main/java/com/termux/app/TermuxActivity.java
+++ b/app/src/main/java/com/termux/app/TermuxActivity.java
@@ -28,7 +28,6 @@ import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.StyleSpan;
-import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
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.settings.properties.TermuxPropertyConstants;
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.TerminalSession;
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_TOGGLE_KEEP_SCREEN_ON = 9;
private static final int CONTEXTMENU_AUTOFILL_ID = 10;
+ private static final int CONTEXTMENU_SETTINGS_ID = 11;
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();
int mBellSoundId;
+ private static final String LOG_TAG = "TermuxActivity";
+
private final BroadcastReceiver mBroadcastReceiever = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (mIsVisible) {
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 (ensureStoragePermissionGranted())
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;
mTerminalView.setTypeface(newTypeface);
} 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.setOnKeyListener(new TermuxViewClient(this));
+
+ mTerminalView.setTerminalViewClient(new TermuxViewClient(this));
mTerminalView.setTextSize(mPreferences.getFontSize());
mTerminalView.setKeepScreenOn(mPreferences.getKeepScreenOn());
+ mTerminalView.setIsTerminalViewKeyLoggingEnabled(mPreferences.getTerminalViewKeyLoggingEnabled());
mTerminalView.requestFocus();
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));
+ mTerminalView.setIsTerminalViewKeyLoggingEnabled(mPreferences.getTerminalViewKeyLoggingEnabled());
+
// The current terminal session may have changed while being away, force
// a refresh of the displayed terminal:
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_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_SETTINGS_ID, Menu.NONE, R.string.settings);
}
/** Hook system menu to show context menu instead. */
@@ -948,6 +955,9 @@ public final class TermuxActivity extends Activity implements ServiceConnection
case CONTEXTMENU_HELP_ID:
startActivity(new Intent(this, TermuxHelpActivity.class));
return true;
+ case CONTEXTMENU_SETTINGS_ID:
+ startActivity(new Intent(this, TermuxSettingsActivity.class));
+ return true;
case CONTEXTMENU_TOGGLE_KEEP_SCREEN_ON: {
if(mTerminalView.getKeepScreenOn()) {
mTerminalView.setKeepScreenOn(false);
@@ -1036,6 +1046,10 @@ public final class TermuxActivity extends Activity implements ServiceConnection
}
}
+ public boolean isVisible() {
+ return mIsVisible;
+ }
+
public TermuxService getTermService() {
return mTermService;
}
@@ -1047,8 +1061,13 @@ public final class TermuxActivity extends Activity implements ServiceConnection
public ExtraKeysView getExtraKeysView() {
return mExtraKeysView;
}
+
public TermuxSharedProperties getProperties() {
return mProperties;
}
+ public TermuxSharedPreferences getPreferences() {
+ return mPreferences;
+ }
+
}
diff --git a/app/src/main/java/com/termux/app/TermuxApplication.java b/app/src/main/java/com/termux/app/TermuxApplication.java
new file mode 100644
index 00000000..94c4dbcc
--- /dev/null
+++ b/app/src/main/java/com/termux/app/TermuxApplication.java
@@ -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");
+ }
+}
+
diff --git a/app/src/main/java/com/termux/app/TermuxInstaller.java b/app/src/main/java/com/termux/app/TermuxInstaller.java
index c5bf3d17..6b218a4f 100644
--- a/app/src/main/java/com/termux/app/TermuxInstaller.java
+++ b/app/src/main/java/com/termux/app/TermuxInstaller.java
@@ -7,12 +7,11 @@ import android.content.Context;
import android.os.Environment;
import android.os.UserManager;
import android.system.Os;
-import android.util.Log;
import android.util.Pair;
import android.view.WindowManager;
import com.termux.R;
-import com.termux.terminal.EmulatorDebug;
+import com.termux.app.utils.Logger;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
@@ -46,6 +45,8 @@ import java.util.zip.ZipInputStream;
*/
final class TermuxInstaller {
+ private static final String LOG_TAG = "TermuxInstaller";
+
/** Performs setup if necessary. */
static void setupIfNeeded(final Activity activity, final Runnable whenDone) {
// Termux can only be run as the primary user (device owner) since only that
@@ -130,7 +131,7 @@ final class TermuxInstaller {
activity.runOnUiThread(whenDone);
} catch (final Exception e) {
- Log.e(EmulatorDebug.LOG_TAG, "Bootstrap error", e);
+ Logger.logStackTraceWithMessage(LOG_TAG, "Bootstrap error", e);
activity.runOnUiThread(() -> {
try {
new AlertDialog.Builder(activity).setTitle(R.string.bootstrap_error_title).setMessage(R.string.bootstrap_error_body)
@@ -200,13 +201,13 @@ final class TermuxInstaller {
try {
deleteFolder(storageDir);
} 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;
}
}
if (!storageDir.mkdirs()) {
- Log.e(LOG_TAG, "Unable to mkdirs() for $HOME/storage");
+ Logger.logError(LOG_TAG, "Unable to mkdirs() for $HOME/storage");
return;
}
@@ -238,7 +239,7 @@ final class TermuxInstaller {
}
}
} catch (Exception e) {
- Log.e(LOG_TAG, "Error setting up link", e);
+ Logger.logStackTraceWithMessage(LOG_TAG, "Error setting up link", e);
}
}
}.start();
diff --git a/app/src/main/java/com/termux/app/TermuxOpenReceiver.java b/app/src/main/java/com/termux/app/TermuxOpenReceiver.java
index 960ab8e5..92ce70f8 100644
--- a/app/src/main/java/com/termux/app/TermuxOpenReceiver.java
+++ b/app/src/main/java/com/termux/app/TermuxOpenReceiver.java
@@ -11,10 +11,9 @@ import android.net.Uri;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
-import android.util.Log;
import android.webkit.MimeTypeMap;
-import com.termux.terminal.EmulatorDebug;
+import com.termux.app.utils.Logger;
import java.io.File;
import java.io.FileNotFoundException;
@@ -24,11 +23,13 @@ import androidx.annotation.NonNull;
public class TermuxOpenReceiver extends BroadcastReceiver {
+ private static final String LOG_TAG = "TermuxOpenReceiver";
+
@Override
public void onReceive(Context context, Intent intent) {
final Uri data = intent.getData();
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;
}
@@ -42,7 +43,7 @@ public class TermuxOpenReceiver extends BroadcastReceiver {
// Ok.
break;
default:
- Log.e(EmulatorDebug.LOG_TAG, "Invalid action '" + intentAction + "', using 'view'");
+ Logger.logError(LOG_TAG, "Invalid action '" + intentAction + "', using 'view'");
break;
}
@@ -59,14 +60,14 @@ public class TermuxOpenReceiver extends BroadcastReceiver {
try {
context.startActivity(urlIntent);
} 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;
}
final File fileToShare = new File(filePath);
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;
}
@@ -103,7 +104,7 @@ public class TermuxOpenReceiver extends BroadcastReceiver {
try {
context.startActivity(sendIntent);
} 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);
}
}
diff --git a/app/src/main/java/com/termux/app/TermuxService.java b/app/src/main/java/com/termux/app/TermuxService.java
index 3301b30a..cc624d60 100644
--- a/app/src/main/java/com/termux/app/TermuxService.java
+++ b/app/src/main/java/com/termux/app/TermuxService.java
@@ -18,14 +18,13 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
import android.provider.Settings;
-import android.util.Log;
import android.widget.ArrayAdapter;
import com.termux.R;
import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_ACTIVITY;
import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_SERVICE;
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.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 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. */
class LocalBinder extends Binder {
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)) {
if (mWakeLock == null) {
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();
// http://tools.android.com/tech-docs/lint-in-studio-2-3#TOC-WifiManager-Leak
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();
String packageName = getPackageName();
@@ -109,7 +110,7 @@ public final class TermuxService extends Service implements SessionChangedCallba
try {
startActivity(whitelist);
} 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));
}
} 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
@@ -246,7 +247,7 @@ public final class TermuxService extends Service implements SessionChangedCallba
try {
TermuxInstaller.deleteFolder(termuxTmpDir.getCanonicalFile());
} 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();
@@ -367,8 +368,8 @@ public final class TermuxService extends Service implements SessionChangedCallba
private void setupNotificationChannel() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
- String channelName = "Termux";
- String channelDescription = "Notifications from Termux";
+ String channelName = TermuxConstants.TERMUX_APP_NAME;
+ String channelDescription = "Notifications from " + TermuxConstants.TERMUX_APP_NAME;
int importance = NotificationManager.IMPORTANCE_LOW;
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);
manager.createNotificationChannel(channel);
}
+
+ public boolean wantsToStop() {
+ return mWantsToStop;
+ }
}
diff --git a/app/src/main/java/com/termux/app/TermuxSettingsActivity.java b/app/src/main/java/com/termux/app/TermuxSettingsActivity.java
new file mode 100644
index 00000000..74604c96
--- /dev/null
+++ b/app/src/main/java/com/termux/app/TermuxSettingsActivity.java
@@ -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);
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/termux/app/settings/DebuggingPreferencesFragment.java b/app/src/main/java/com/termux/app/settings/DebuggingPreferencesFragment.java
new file mode 100644
index 00000000..c976c30e
--- /dev/null
+++ b/app/src/main/java/com/termux/app/settings/DebuggingPreferencesFragment.java
@@ -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;
+ }
+ }
+}
diff --git a/app/src/main/java/com/termux/app/settings/preferences/TermuxPreferenceConstants.java b/app/src/main/java/com/termux/app/settings/preferences/TermuxPreferenceConstants.java
index f9ba174d..a12aa954 100644
--- a/app/src/main/java/com/termux/app/settings/preferences/TermuxPreferenceConstants.java
+++ b/app/src/main/java/com/termux/app/settings/preferences/TermuxPreferenceConstants.java
@@ -3,13 +3,14 @@ package com.termux.app.settings.preferences;
import com.termux.app.TermuxConstants;
/*
- * Version: v0.1.0
+ * Version: v0.2.0
*
* Changelog
*
* - 0.1.0 (2021-03-12)
* - 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;
+
}
diff --git a/app/src/main/java/com/termux/app/settings/preferences/TermuxSharedPreferences.java b/app/src/main/java/com/termux/app/settings/preferences/TermuxSharedPreferences.java
index 65fe9679..1278dd3e 100644
--- a/app/src/main/java/com/termux/app/settings/preferences/TermuxSharedPreferences.java
+++ b/app/src/main/java/com/termux/app/settings/preferences/TermuxSharedPreferences.java
@@ -2,11 +2,11 @@ package com.termux.app.settings.preferences;
import android.content.Context;
import android.content.SharedPreferences;
-import android.util.Log;
import android.util.TypedValue;
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;
@@ -21,17 +21,7 @@ public class TermuxSharedPreferences {
private int DEFAULT_FONTSIZE;
public TermuxSharedPreferences(@Nonnull Context context) {
- Context mTempContext;
-
- 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;
+ mContext = TermuxUtils.getTermuxPackageContext(context);
mSharedPreferences = getSharedPreferences(mContext);
setFontVariables(context);
@@ -135,4 +125,31 @@ public class TermuxSharedPreferences {
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();
+ }
+
}
diff --git a/app/src/main/java/com/termux/app/settings/properties/SharedProperties.java b/app/src/main/java/com/termux/app/settings/properties/SharedProperties.java
index db6eb637..bf47fa00 100644
--- a/app/src/main/java/com/termux/app/settings/properties/SharedProperties.java
+++ b/app/src/main/java/com/termux/app/settings/properties/SharedProperties.java
@@ -1,10 +1,10 @@
package com.termux.app.settings.properties;
import android.content.Context;
-import android.util.Log;
import android.widget.Toast;
import com.google.common.primitives.Primitives;
+import com.termux.app.utils.Logger;
import java.io.File;
import java.io.FileInputStream;
@@ -57,6 +57,8 @@ public class SharedProperties {
private final Object mLock = new Object();
+ private static final String LOG_TAG = "SharedProperties";
+
/**
* Constructor for the SharedProperties class.
*
@@ -97,7 +99,7 @@ public class SharedProperties {
Object internalValue;
for (String key : mPropertiesList) {
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)}
// 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 map, String key, Object value) {
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;
}
// null keys are not allowed to be stored in mMap
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;
}
@@ -153,7 +155,7 @@ public class SharedProperties {
map.put(key, value);
return true;
} 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;
}
}
@@ -172,13 +174,13 @@ public class SharedProperties {
public static boolean putToProperties(Properties properties, String key, String value) {
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;
}
// null keys are not allowed to be stored in mMap
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;
}
@@ -206,19 +208,19 @@ public class SharedProperties {
Properties properties = new Properties();
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;
}
try {
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));
}
} catch (Exception e) {
if(context != null)
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;
}
diff --git a/app/src/main/java/com/termux/app/settings/properties/TermuxPropertyConstants.java b/app/src/main/java/com/termux/app/settings/properties/TermuxPropertyConstants.java
index a68e0466..6e3dd673 100644
--- a/app/src/main/java/com/termux/app/settings/properties/TermuxPropertyConstants.java
+++ b/app/src/main/java/com/termux/app/settings/properties/TermuxPropertyConstants.java
@@ -1,9 +1,8 @@
package com.termux.app.settings.properties;
-import android.util.Log;
-
import com.google.common.collect.ImmutableBiMap;
import com.termux.app.TermuxConstants;
+import com.termux.app.utils.Logger;
import java.io.File;
import java.util.Arrays;
@@ -237,7 +236,7 @@ public final class TermuxPropertyConstants {
if (propertiesFile.isFile() && propertiesFile.canRead()) {
return propertiesFile;
} else {
- Log.d("termux", "No readable termux.properties file found");
+ Logger.logDebug("No readable termux.properties file found");
return null;
}
}
diff --git a/app/src/main/java/com/termux/app/settings/properties/TermuxSharedProperties.java b/app/src/main/java/com/termux/app/settings/properties/TermuxSharedProperties.java
index 70a3dc3e..8c72793f 100644
--- a/app/src/main/java/com/termux/app/settings/properties/TermuxSharedProperties.java
+++ b/app/src/main/java/com/termux/app/settings/properties/TermuxSharedProperties.java
@@ -2,13 +2,12 @@ package com.termux.app.settings.properties;
import android.content.Context;
import android.content.res.Configuration;
-import android.util.Log;
-import android.widget.Toast;
import androidx.annotation.Nullable;
import com.termux.app.terminal.extrakeys.ExtraKeysInfo;
import com.termux.app.terminal.KeyboardShortcut;
+import com.termux.app.utils.Logger;
import org.json.JSONException;
@@ -30,6 +29,8 @@ public class TermuxSharedProperties implements SharedPropertiesParser {
private ExtraKeysInfo mExtraKeysInfo;
private final List mSessionShortcuts = new ArrayList<>();
+ private static final String LOG_TAG = "TermuxSharedProperties";
+
public TermuxSharedProperties(@Nonnull Context context) {
mContext = context;
mPropertiesFile = TermuxPropertyConstants.getTermuxPropertiesFile();
@@ -65,14 +66,14 @@ public class TermuxSharedProperties implements SharedPropertiesParser {
String extraKeysStyle = (String) getInternalPropertyValue(TermuxPropertyConstants.KEY_EXTRA_KEYS_STYLE, true);
mExtraKeysInfo = new ExtraKeysInfo(extrakeys, extraKeysStyle);
} 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();
- Log.e("termux", "Could not load and set the \"" + TermuxPropertyConstants.KEY_EXTRA_KEYS + "\" property from the properties file: ", e);
+ Logger.showToast(mContext, "Could not load and set the \"" + TermuxPropertyConstants.KEY_EXTRA_KEYS + "\" property from the properties file: " + e.toString(), true);
+ Logger.logStackTraceWithMessage(LOG_TAG, "Could not load and set the \"" + TermuxPropertyConstants.KEY_EXTRA_KEYS + "\" property from the properties file: ", e);
try {
mExtraKeysInfo = new ExtraKeysInfo(TermuxPropertyConstants.DEFAULT_IVALUE_EXTRA_KEYS, TermuxPropertyConstants.DEFAULT_IVALUE_EXTRA_KEYS_STYLE);
} catch (JSONException e2) {
- Toast.makeText(mContext, "Can't create default extra keys", Toast.LENGTH_LONG).show();
- Log.e("termux", "Could create default extra keys: ", e);
+ Logger.showToast(mContext, "Can't create default extra keys",true);
+ Logger.logStackTraceWithMessage(LOG_TAG, "Could create default extra keys: ", e);
mExtraKeysInfo = null;
}
}
@@ -262,7 +263,7 @@ public class TermuxSharedProperties implements SharedPropertiesParser {
// A null value can still be returned by
// {@link #getInternalPropertyValueFromValue(Context,String,String)} for some keys
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;
}
} else {
@@ -423,7 +424,7 @@ public class TermuxSharedProperties implements SharedPropertiesParser {
String[] parts = value.toLowerCase().trim().split("\\+");
String input = parts.length == 2 ? parts[1].trim() : null;
if (!(parts.length == 2 && parts[0].trim().equals("ctrl")) || input.isEmpty() || input.length() > 2) {
- Log.e("termux", "Keyboard shortcut '" + key + "' is not Ctrl+");
+ Logger.logError(LOG_TAG, "Keyboard shortcut '" + key + "' is not Ctrl+");
return null;
}
@@ -431,7 +432,7 @@ public class TermuxSharedProperties implements SharedPropertiesParser {
int codePoint = c;
if (Character.isLowSurrogate(c)) {
if (input.length() != 2 || Character.isHighSurrogate(input.charAt(1))) {
- Log.e("termux", "Keyboard shortcut '" + key + "' is not Ctrl+");
+ Logger.logError(LOG_TAG, "Keyboard shortcut '" + key + "' is not Ctrl+");
return null;
} else {
codePoint = Character.toCodePoint(input.charAt(1), c);
@@ -556,7 +557,7 @@ public class TermuxSharedProperties implements SharedPropertiesParser {
propertiesDump.append(" null");
}
- Log.d("termux", propertiesDump.toString());
+ Logger.logDebug(LOG_TAG, propertiesDump.toString());
}
public void dumpInternalPropertiesToLog() {
@@ -570,7 +571,7 @@ public class TermuxSharedProperties implements SharedPropertiesParser {
}
}
- Log.d("termux", internalPropertiesDump.toString());
+ Logger.logDebug(LOG_TAG, internalPropertiesDump.toString());
}
}
diff --git a/app/src/main/java/com/termux/app/terminal/TermuxViewClient.java b/app/src/main/java/com/termux/app/terminal/TermuxViewClient.java
index 567ebef8..8d49b6df 100644
--- a/app/src/main/java/com/termux/app/terminal/TermuxViewClient.java
+++ b/app/src/main/java/com/termux/app/terminal/TermuxViewClient.java
@@ -13,6 +13,7 @@ import com.termux.app.TermuxActivity;
import com.termux.app.TermuxService;
import com.termux.app.terminal.extrakeys.ExtraKeysView;
import com.termux.app.settings.properties.TermuxPropertyConstants;
+import com.termux.app.utils.Logger;
import com.termux.terminal.KeyHandler;
import com.termux.terminal.TerminalEmulator;
import com.termux.terminal.TerminalSession;
@@ -20,6 +21,7 @@ import com.termux.view.TerminalViewClient;
import java.util.List;
+
import androidx.drawerlayout.widget.DrawerLayout;
public final class TermuxViewClient implements TerminalViewClient {
@@ -43,6 +45,8 @@ public final class TermuxViewClient implements TerminalViewClient {
return scale;
}
+
+
@Override
public void onSingleTapUp(MotionEvent e) {
InputMethodManager mgr = (InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
@@ -64,12 +68,16 @@ public final class TermuxViewClient implements TerminalViewClient {
return mActivity.getProperties().isUsingCtrlSpaceWorkaround();
}
+
+
@Override
public void copyModeChanged(boolean copyMode) {
// Disable drawer while copying.
mActivity.getDrawer().setDrawerLockMode(copyMode ? DrawerLayout.LOCK_MODE_LOCKED_CLOSED : DrawerLayout.LOCK_MODE_UNLOCKED);
}
+
+
@SuppressLint("RtlHardcoded")
@Override
public boolean onKeyDown(int keyCode, KeyEvent e, TerminalSession currentSession) {
@@ -127,6 +135,26 @@ public final class TermuxViewClient implements TerminalViewClient {
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
public boolean readControlKey() {
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));
}
+ @Override
+ public boolean onLongPress(MotionEvent event) {
+ return false;
+ }
+
+
+
@Override
public boolean onCodePoint(final int codePoint, boolean ctrlDown, TerminalSession session) {
if (mVirtualFnKeyDown) {
@@ -273,27 +308,41 @@ public final class TermuxViewClient implements TerminalViewClient {
return false;
}
+
+
@Override
- public boolean onLongPress(MotionEvent event) {
- return false;
+ public void logError(String tag, String message) {
+ Logger.logError(tag, message);
}
- /** 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
+ public void logWarn(String tag, String message) {
+ Logger.logWarn(tag, message);
+ }
+
+ @Override
+ public void logInfo(String tag, String message) {
+ Logger.logInfo(tag, message);
+ }
+
+ @Override
+ public void logDebug(String tag, String message) {
+ Logger.logDebug(tag, message);
+ }
+
+ @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);
}
}
diff --git a/app/src/main/java/com/termux/app/utils/Logger.java b/app/src/main/java/com/termux/app/utils/Logger.java
new file mode 100644
index 00000000..aace3c25
--- /dev/null
+++ b/app/src/main/java/com/termux/app/utils/Logger.java
@@ -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= 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;
+ }
+
+}
diff --git a/app/src/main/java/com/termux/app/utils/TermuxUtils.java b/app/src/main/java/com/termux/app/utils/TermuxUtils.java
new file mode 100644
index 00000000..712b5b3a
--- /dev/null
+++ b/app/src/main/java/com/termux/app/utils/TermuxUtils.java
@@ -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;
+ }
+ }
+}
diff --git a/app/src/main/java/com/termux/filepicker/TermuxFileReceiverActivity.java b/app/src/main/java/com/termux/filepicker/TermuxFileReceiverActivity.java
index 2367bdf0..f0f53dcb 100644
--- a/app/src/main/java/com/termux/filepicker/TermuxFileReceiverActivity.java
+++ b/app/src/main/java/com/termux/filepicker/TermuxFileReceiverActivity.java
@@ -6,7 +6,6 @@ import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.provider.OpenableColumns;
-import android.util.Log;
import android.util.Patterns;
import com.termux.R;
@@ -14,6 +13,7 @@ import com.termux.app.DialogUtils;
import com.termux.app.TermuxConstants;
import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_SERVICE;
import com.termux.app.TermuxService;
+import com.termux.app.utils.Logger;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -39,6 +39,8 @@ public class TermuxFileReceiverActivity extends Activity {
*/
boolean mFinishOnDismissNameDialog = true;
+ private static final String LOG_TAG = "TermuxFileReceiverActivity";
+
static boolean isSharedTextAnUrl(String sharedText) {
return Patterns.WEB_URL.matcher(sharedText).matches()
|| Pattern.matches("magnet:\\?xt=urn:btih:.*?", sharedText);
@@ -111,7 +113,7 @@ public class TermuxFileReceiverActivity extends Activity {
promptNameAndSave(in, attachmentFileName);
} catch (Exception e) {
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;
} catch (IOException 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;
}
}
diff --git a/app/src/main/res/layout/settings_activity.xml b/app/src/main/res/layout/settings_activity.xml
new file mode 100644
index 00000000..7dc1a6c5
--- /dev/null
+++ b/app/src/main/res/layout/settings_activity.xml
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a34c5d11..968ac645 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -63,4 +63,35 @@
Save file in ~/downloads/
Edit
Open folder
+
+
+
+
+
+
+ Settings
+ Termux Settings
+
+
+
+
+ Debugging
+
+
+ Logging
+
+
+ Log Level
+ "Off"
+ "Normal"
+ "Debug"
+ "Verbose"
+ "*Unknown*"
+ Logcat log level set to \"%1$s\"
+
+
+ Terminal View Key Logging
+ Logs will not have entries for terminal view keys. (Default)
+ 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.
+
diff --git a/app/src/main/res/xml/debugging_preferences.xml b/app/src/main/res/xml/debugging_preferences.xml
new file mode 100644
index 00000000..ba1f328d
--- /dev/null
+++ b/app/src/main/res/xml/debugging_preferences.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml
new file mode 100644
index 00000000..3ea2b583
--- /dev/null
+++ b/app/src/main/res/xml/root_preferences.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/terminal-view/src/main/java/com/termux/view/TerminalView.java b/terminal-view/src/main/java/com/termux/view/TerminalView.java
index b9d18e69..995f8b9a 100644
--- a/terminal-view/src/main/java/com/termux/view/TerminalView.java
+++ b/terminal-view/src/main/java/com/termux/view/TerminalView.java
@@ -12,7 +12,6 @@ import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.ActionMode;
import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
@@ -31,7 +30,6 @@ import android.widget.Scroller;
import androidx.annotation.RequiresApi;
-import com.termux.terminal.EmulatorDebug;
import com.termux.terminal.KeyHandler;
import com.termux.terminal.TerminalEmulator;
import com.termux.terminal.TerminalSession;
@@ -40,8 +38,8 @@ import com.termux.view.textselection.TextSelectionCursorController;
/** View displaying and interacting with a {@link TerminalSession}. */
public final class TerminalView extends View {
- /** Log view key and IME events. */
- private static final boolean LOG_KEY_EVENTS = false;
+ /** Log terminal view key and IME events. */
+ private static boolean TERMINAL_VIEW_KEY_LOGGING_ENABLED = false;
/** The currently displayed terminal session, whose emulator is {@link #mEmulator}. */
public TerminalSession mTermSession;
@@ -76,6 +74,8 @@ public final class TerminalView extends View {
private final boolean mAccessibilityEnabled;
+ private static final String LOG_TAG = "TerminalView";
+
public TerminalView(Context context, AttributeSet attributes) { // NO_UCD (unused code)
super(context, attributes);
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
- * available with {@link View#setOnKeyListener(OnKeyListener)}.
+ * @param terminalViewClient Interface for communicating with the terminal view client. It allows
+ * 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) {
- this.mClient = onKeyListener;
+ public void setTerminalViewClient(TerminalViewClient terminalViewClient) {
+ 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
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();
sendTextToTerminal(getEditable());
@@ -274,8 +286,8 @@ public final class TerminalView extends View {
@Override
public boolean commitText(CharSequence text, int newCursorPosition) {
- if (LOG_KEY_EVENTS) {
- Log.i(EmulatorDebug.LOG_TAG, "IME: commitText(\"" + text + "\", " + newCursorPosition + ")");
+ if (TERMINAL_VIEW_KEY_LOGGING_ENABLED) {
+ mClient.logInfo(LOG_TAG, "IME: commitText(\"" + text + "\", " + newCursorPosition + ")");
}
super.commitText(text, newCursorPosition);
@@ -289,8 +301,8 @@ public final class TerminalView extends View {
@Override
public boolean deleteSurroundingText(int leftLength, int rightLength) {
- if (LOG_KEY_EVENTS) {
- Log.i(EmulatorDebug.LOG_TAG, "IME: deleteSurroundingText(" + leftLength + ", " + rightLength + ")");
+ if (TERMINAL_VIEW_KEY_LOGGING_ENABLED) {
+ mClient.logInfo(LOG_TAG, "IME: deleteSurroundingText(" + leftLength + ", " + rightLength + ")");
}
// The stock Samsung keyboard with 'Auto check spelling' enabled sends leftLength > 1.
KeyEvent deleteKey = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL);
@@ -521,8 +533,8 @@ public final class TerminalView extends View {
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
- if (LOG_KEY_EVENTS)
- Log.i(EmulatorDebug.LOG_TAG, "onKeyPreIme(keyCode=" + keyCode + ", event=" + event + ")");
+ if (TERMINAL_VIEW_KEY_LOGGING_ENABLED)
+ mClient.logInfo(LOG_TAG, "onKeyPreIme(keyCode=" + keyCode + ", event=" + event + ")");
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (isSelectingText()) {
stopTextSelectionMode();
@@ -547,8 +559,8 @@ public final class TerminalView extends View {
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (LOG_KEY_EVENTS)
- Log.i(EmulatorDebug.LOG_TAG, "onKeyDown(keyCode=" + keyCode + ", isSystem()=" + event.isSystem() + ", event=" + event + ")");
+ if (TERMINAL_VIEW_KEY_LOGGING_ENABLED)
+ mClient.logInfo(LOG_TAG, "onKeyDown(keyCode=" + keyCode + ", isSystem()=" + event.isSystem() + ", event=" + event + ")");
if (mEmulator == null) return true;
if (isSelectingText()) {
stopTextSelectionMode();
@@ -575,7 +587,7 @@ public final class TerminalView extends View {
if (event.isShiftPressed()) keyMod |= KeyHandler.KEYMOD_SHIFT;
if (event.isNumLockOn()) keyMod |= KeyHandler.KEYMOD_NUM_LOCK;
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;
}
@@ -590,8 +602,8 @@ public final class TerminalView extends View {
int effectiveMetaState = event.getMetaState() & ~bitsToClear;
int result = event.getUnicodeChar(effectiveMetaState);
- if (LOG_KEY_EVENTS)
- Log.i(EmulatorDebug.LOG_TAG, "KeyEvent#getUnicodeChar(" + effectiveMetaState + ") returned: " + result);
+ if (TERMINAL_VIEW_KEY_LOGGING_ENABLED)
+ mClient.logInfo(LOG_TAG, "KeyEvent#getUnicodeChar(" + effectiveMetaState + ") returned: " + result);
if (result == 0) {
return false;
}
@@ -617,8 +629,8 @@ public final class TerminalView extends View {
}
public void inputCodePoint(int codePoint, boolean controlDownFromEvent, boolean leftAltDownFromEvent) {
- if (LOG_KEY_EVENTS) {
- Log.i(EmulatorDebug.LOG_TAG, "inputCodePoint(codePoint=" + codePoint + ", controlDownFromEvent=" + controlDownFromEvent + ", leftAltDownFromEvent="
+ if (TERMINAL_VIEW_KEY_LOGGING_ENABLED) {
+ mClient.logInfo(LOG_TAG, "inputCodePoint(codePoint=" + codePoint + ", controlDownFromEvent=" + controlDownFromEvent + ", leftAltDownFromEvent="
+ leftAltDownFromEvent + ")");
}
@@ -692,8 +704,8 @@ public final class TerminalView extends View {
*/
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (LOG_KEY_EVENTS)
- Log.i(EmulatorDebug.LOG_TAG, "onKeyUp(keyCode=" + keyCode + ", event=" + event + ")");
+ if (TERMINAL_VIEW_KEY_LOGGING_ENABLED)
+ mClient.logInfo(LOG_TAG, "onKeyUp(keyCode=" + keyCode + ", event=" + event + ")");
if (mEmulator == null) return true;
if (mClient.onKeyUp(keyCode, event)) {
diff --git a/terminal-view/src/main/java/com/termux/view/TerminalViewClient.java b/terminal-view/src/main/java/com/termux/view/TerminalViewClient.java
index 390e9157..25be7d9d 100644
--- a/terminal-view/src/main/java/com/termux/view/TerminalViewClient.java
+++ b/terminal-view/src/main/java/com/termux/view/TerminalViewClient.java
@@ -8,7 +8,7 @@ import com.termux.terminal.TerminalSession;
/**
* Input and scale listener which may be set on a {@link TerminalView} through
- * {@link TerminalView#setOnKeyListener(TerminalViewClient)}.
+ * {@link TerminalView#setTerminalViewClient(TerminalViewClient)}.
*
*/
public interface TerminalViewClient {
@@ -18,6 +18,8 @@ public interface TerminalViewClient {
*/
float onScale(float scale);
+
+
/**
* On a single tap on the terminal if terminal mouse reporting not enabled.
*/
@@ -29,18 +31,41 @@ public interface TerminalViewClient {
boolean shouldUseCtrlSpaceWorkaround();
+
+
void copyModeChanged(boolean copyMode);
+
+
boolean onKeyDown(int keyCode, KeyEvent e, TerminalSession session);
boolean onKeyUp(int keyCode, KeyEvent e);
+ boolean onLongPress(MotionEvent event);
+
+
+
boolean readControlKey();
boolean readAltKey();
+
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);
}