2015-10-25 14:27:32 +00:00
|
|
|
package com.termux.app;
|
|
|
|
|
|
|
|
import android.annotation.SuppressLint;
|
|
|
|
import android.app.Notification;
|
2017-11-01 20:15:41 +00:00
|
|
|
import android.app.NotificationChannel;
|
2015-10-25 14:27:32 +00:00
|
|
|
import android.app.NotificationManager;
|
|
|
|
import android.app.PendingIntent;
|
|
|
|
import android.app.Service;
|
2019-09-06 12:05:26 +00:00
|
|
|
import android.content.ActivityNotFoundException;
|
2015-10-25 14:27:32 +00:00
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.res.Resources;
|
|
|
|
import android.net.Uri;
|
|
|
|
import android.net.wifi.WifiManager;
|
|
|
|
import android.os.Binder;
|
2017-11-01 20:15:41 +00:00
|
|
|
import android.os.Build;
|
2016-12-04 03:37:13 +00:00
|
|
|
import android.os.Handler;
|
2015-10-25 14:27:32 +00:00
|
|
|
import android.os.IBinder;
|
|
|
|
import android.os.PowerManager;
|
2019-08-15 19:40:14 +00:00
|
|
|
import android.provider.Settings;
|
2015-10-25 14:27:32 +00:00
|
|
|
import android.util.Log;
|
|
|
|
import android.widget.ArrayAdapter;
|
|
|
|
|
2016-06-27 23:03:03 +00:00
|
|
|
import com.termux.R;
|
2021-03-08 10:09:22 +00:00
|
|
|
import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_ACTIVITY;
|
|
|
|
import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_SERVICE;
|
2021-03-12 01:01:31 +00:00
|
|
|
import com.termux.app.settings.preferences.TermuxSharedPreferences;
|
2016-06-27 23:03:03 +00:00
|
|
|
import com.termux.terminal.EmulatorDebug;
|
|
|
|
import com.termux.terminal.TerminalSession;
|
|
|
|
import com.termux.terminal.TerminalSession.SessionChangedCallback;
|
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.List;
|
|
|
|
|
2015-10-25 14:27:32 +00:00
|
|
|
/**
|
|
|
|
* A service holding a list of terminal sessions, {@link #mTerminalSessions}, showing a foreground notification while
|
|
|
|
* running so that it is not terminated. The user interacts with the session through {@link TermuxActivity}, but this
|
|
|
|
* service may outlive the activity when the user or the system disposes of the activity. In that case the user may
|
|
|
|
* restart {@link TermuxActivity} later to yet again access the sessions.
|
2016-06-27 23:03:03 +00:00
|
|
|
* <p/>
|
2015-10-25 14:27:32 +00:00
|
|
|
* In order to keep both terminal sessions and spawned processes (who may outlive the terminal sessions) alive as long
|
|
|
|
* as wanted by the user this service is a foreground service, {@link Service#startForeground(int, Notification)}.
|
2016-06-27 23:03:03 +00:00
|
|
|
* <p/>
|
2015-10-25 14:27:32 +00:00
|
|
|
* Optionally may hold a wake and a wifi lock, in which case that is shown in the notification - see
|
|
|
|
* {@link #buildNotification()}.
|
|
|
|
*/
|
|
|
|
public final class TermuxService extends Service implements SessionChangedCallback {
|
|
|
|
|
2017-11-01 20:15:41 +00:00
|
|
|
private static final String NOTIFICATION_CHANNEL_ID = "termux_notification_channel";
|
2016-06-27 23:03:03 +00:00
|
|
|
private static final int NOTIFICATION_ID = 1337;
|
2015-10-25 14:27:32 +00:00
|
|
|
|
2016-06-27 23:03:03 +00:00
|
|
|
/** This service is only bound from inside the same process and never uses IPC. */
|
|
|
|
class LocalBinder extends Binder {
|
|
|
|
public final TermuxService service = TermuxService.this;
|
|
|
|
}
|
|
|
|
|
|
|
|
private final IBinder mBinder = new LocalBinder();
|
|
|
|
|
2016-12-04 03:37:13 +00:00
|
|
|
private final Handler mHandler = new Handler();
|
|
|
|
|
2016-06-27 23:03:03 +00:00
|
|
|
/**
|
|
|
|
* The terminal sessions which this service manages.
|
|
|
|
* <p/>
|
|
|
|
* Note that this list is observed by {@link TermuxActivity#mListViewAdapter}, so any changes must be made on the UI
|
|
|
|
* thread and followed by a call to {@link ArrayAdapter#notifyDataSetChanged()} }.
|
|
|
|
*/
|
|
|
|
final List<TerminalSession> mTerminalSessions = new ArrayList<>();
|
|
|
|
|
2016-12-04 03:37:13 +00:00
|
|
|
final List<BackgroundJob> mBackgroundTasks = new ArrayList<>();
|
|
|
|
|
2016-06-27 23:03:03 +00:00
|
|
|
/** Note that the service may often outlive the activity, so need to clear this reference. */
|
|
|
|
SessionChangedCallback mSessionChangeCallback;
|
|
|
|
|
2016-12-04 03:37:13 +00:00
|
|
|
/** The wake lock and wifi lock are always acquired and released together. */
|
2016-06-27 23:03:03 +00:00
|
|
|
private PowerManager.WakeLock mWakeLock;
|
|
|
|
private WifiManager.WifiLock mWifiLock;
|
|
|
|
|
2021-03-08 10:09:22 +00:00
|
|
|
/** If the user has executed the {@link TermuxConstants.TERMUX_APP.TERMUX_SERVICE#ACTION_STOP_SERVICE} intent. */
|
2016-06-27 23:03:03 +00:00
|
|
|
boolean mWantsToStop = false;
|
|
|
|
|
|
|
|
@SuppressLint("Wakelock")
|
|
|
|
@Override
|
|
|
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
|
|
|
String action = intent.getAction();
|
2021-03-08 10:09:22 +00:00
|
|
|
if (TERMUX_SERVICE.ACTION_STOP_SERVICE.equals(action)) {
|
2016-06-27 23:03:03 +00:00
|
|
|
mWantsToStop = true;
|
|
|
|
for (int i = 0; i < mTerminalSessions.size(); i++)
|
|
|
|
mTerminalSessions.get(i).finishIfRunning();
|
|
|
|
stopSelf();
|
2021-03-08 10:09:22 +00:00
|
|
|
} else if (TERMUX_SERVICE.ACTION_WAKE_LOCK.equals(action)) {
|
2016-06-27 23:03:03 +00:00
|
|
|
if (mWakeLock == null) {
|
|
|
|
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
2020-08-14 12:03:14 +00:00
|
|
|
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, EmulatorDebug.LOG_TAG + ":service-wakelock");
|
2016-06-27 23:03:03 +00:00
|
|
|
mWakeLock.acquire();
|
2016-12-04 03:37:13 +00:00
|
|
|
|
2017-03-06 00:42:33 +00:00
|
|
|
// http://tools.android.com/tech-docs/lint-in-studio-2-3#TOC-WifiManager-Leak
|
|
|
|
WifiManager wm = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
|
2016-06-27 23:03:03 +00:00
|
|
|
mWifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, EmulatorDebug.LOG_TAG);
|
|
|
|
mWifiLock.acquire();
|
2016-12-04 03:37:13 +00:00
|
|
|
|
2019-11-03 21:39:21 +00:00
|
|
|
String packageName = getPackageName();
|
|
|
|
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
|
|
|
|
Intent whitelist = new Intent();
|
|
|
|
whitelist.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
|
|
|
|
whitelist.setData(Uri.parse("package:" + packageName));
|
|
|
|
whitelist.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
|
|
|
|
|
|
try {
|
|
|
|
startActivity(whitelist);
|
|
|
|
} catch (ActivityNotFoundException e) {
|
|
|
|
Log.e(EmulatorDebug.LOG_TAG, "Failed to call ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS", e);
|
2019-08-15 19:40:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-04 03:37:13 +00:00
|
|
|
updateNotification();
|
|
|
|
}
|
2021-03-08 10:09:22 +00:00
|
|
|
} else if (TERMUX_SERVICE.ACTION_WAKE_UNLOCK.equals(action)) {
|
2016-12-04 03:37:13 +00:00
|
|
|
if (mWakeLock != null) {
|
|
|
|
mWakeLock.release();
|
|
|
|
mWakeLock = null;
|
|
|
|
|
2016-06-27 23:03:03 +00:00
|
|
|
mWifiLock.release();
|
|
|
|
mWifiLock = null;
|
2016-12-04 03:37:13 +00:00
|
|
|
|
|
|
|
updateNotification();
|
2016-06-27 23:03:03 +00:00
|
|
|
}
|
2021-03-08 10:09:22 +00:00
|
|
|
} else if (TERMUX_SERVICE.ACTION_SERVICE_EXECUTE.equals(action)) {
|
2016-06-27 23:03:03 +00:00
|
|
|
Uri executableUri = intent.getData();
|
|
|
|
String executablePath = (executableUri == null ? null : executableUri.getPath());
|
2016-12-23 23:25:24 +00:00
|
|
|
|
2021-03-08 10:09:22 +00:00
|
|
|
String[] arguments = (executableUri == null ? null : intent.getStringArrayExtra(TERMUX_SERVICE.EXTRA_ARGUMENTS));
|
|
|
|
String cwd = intent.getStringExtra(TERMUX_SERVICE.EXTRA_WORKDIR);
|
2016-06-27 23:03:03 +00:00
|
|
|
|
2021-03-08 10:09:22 +00:00
|
|
|
if (intent.getBooleanExtra(TERMUX_SERVICE.EXTRA_BACKGROUND, false)) {
|
2019-12-02 09:38:37 +00:00
|
|
|
BackgroundJob task = new BackgroundJob(cwd, executablePath, arguments, this, intent.getParcelableExtra("pendingIntent"));
|
2016-12-23 23:25:24 +00:00
|
|
|
mBackgroundTasks.add(task);
|
2016-12-04 03:37:13 +00:00
|
|
|
updateNotification();
|
2016-10-26 00:26:44 +00:00
|
|
|
} else {
|
2021-03-08 10:09:22 +00:00
|
|
|
boolean failsafe = intent.getBooleanExtra(TERMUX_ACTIVITY.ACTION_FAILSAFE_SESSION, false);
|
2019-06-09 17:59:58 +00:00
|
|
|
TerminalSession newSession = createTermSession(executablePath, arguments, cwd, failsafe);
|
2016-10-26 00:26:44 +00:00
|
|
|
|
|
|
|
// Transform executable path to session name, e.g. "/bin/do-something.sh" => "do something.sh".
|
|
|
|
if (executablePath != null) {
|
|
|
|
int lastSlash = executablePath.lastIndexOf('/');
|
|
|
|
String name = (lastSlash == -1) ? executablePath : executablePath.substring(lastSlash + 1);
|
|
|
|
name = name.replace('-', ' ');
|
|
|
|
newSession.mSessionName = name;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make the newly created session the current one to be displayed:
|
2021-03-12 01:01:31 +00:00
|
|
|
TermuxSharedPreferences preferences = new TermuxSharedPreferences(this);
|
|
|
|
preferences.setCurrentSession(newSession.mHandle);
|
2016-06-27 23:03:03 +00:00
|
|
|
|
2016-12-04 03:37:13 +00:00
|
|
|
// Launch the main Termux app, which will now show the current session:
|
2016-10-26 00:26:44 +00:00
|
|
|
startActivity(new Intent(this, TermuxActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
|
|
|
}
|
2016-06-27 23:03:03 +00:00
|
|
|
} else if (action != null) {
|
|
|
|
Log.e(EmulatorDebug.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
|
|
|
|
// start of {@link Term):
|
|
|
|
return Service.START_NOT_STICKY;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public IBinder onBind(Intent intent) {
|
|
|
|
return mBinder;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onCreate() {
|
2017-11-01 20:15:41 +00:00
|
|
|
setupNotificationChannel();
|
2016-06-27 23:03:03 +00:00
|
|
|
startForeground(NOTIFICATION_ID, buildNotification());
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Update the shown foreground service notification after making any changes that affect it. */
|
2017-03-06 00:53:09 +00:00
|
|
|
void updateNotification() {
|
2016-12-04 03:37:13 +00:00
|
|
|
if (mWakeLock == null && mTerminalSessions.isEmpty() && mBackgroundTasks.isEmpty()) {
|
|
|
|
// Exit if we are updating after the user disabled all locks with no sessions or tasks running.
|
2016-06-27 23:03:03 +00:00
|
|
|
stopSelf();
|
|
|
|
} else {
|
|
|
|
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).notify(NOTIFICATION_ID, buildNotification());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private Notification buildNotification() {
|
|
|
|
Intent notifyIntent = new Intent(this, TermuxActivity.class);
|
|
|
|
// PendingIntent#getActivity(): "Note that the activity will be started outside of the context of an existing
|
|
|
|
// activity, so you must use the Intent.FLAG_ACTIVITY_NEW_TASK launch flag in the Intent":
|
|
|
|
notifyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
|
|
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notifyIntent, 0);
|
|
|
|
|
|
|
|
int sessionCount = mTerminalSessions.size();
|
2016-12-04 03:37:13 +00:00
|
|
|
int taskCount = mBackgroundTasks.size();
|
|
|
|
String contentText = sessionCount + " session" + (sessionCount == 1 ? "" : "s");
|
|
|
|
if (taskCount > 0) {
|
|
|
|
contentText += ", " + taskCount + " task" + (taskCount == 1 ? "" : "s");
|
2016-06-27 23:03:03 +00:00
|
|
|
}
|
|
|
|
|
2016-12-04 03:37:13 +00:00
|
|
|
final boolean wakeLockHeld = mWakeLock != null;
|
|
|
|
if (wakeLockHeld) contentText += " (wake lock held)";
|
|
|
|
|
2016-06-27 23:03:03 +00:00
|
|
|
Notification.Builder builder = new Notification.Builder(this);
|
|
|
|
builder.setContentTitle(getText(R.string.application_name));
|
|
|
|
builder.setContentText(contentText);
|
|
|
|
builder.setSmallIcon(R.drawable.ic_service_notification);
|
|
|
|
builder.setContentIntent(pendingIntent);
|
|
|
|
builder.setOngoing(true);
|
|
|
|
|
|
|
|
// If holding a wake or wifi lock consider the notification of high priority since it's using power,
|
2017-11-09 16:26:03 +00:00
|
|
|
// otherwise use a low priority
|
|
|
|
builder.setPriority((wakeLockHeld) ? Notification.PRIORITY_HIGH : Notification.PRIORITY_LOW);
|
2016-06-27 23:03:03 +00:00
|
|
|
|
|
|
|
// No need to show a timestamp:
|
|
|
|
builder.setShowWhen(false);
|
|
|
|
|
|
|
|
// Background color for small notification icon:
|
2019-05-17 17:32:19 +00:00
|
|
|
builder.setColor(0xFF607D8B);
|
2016-06-27 23:03:03 +00:00
|
|
|
|
2017-11-01 20:15:41 +00:00
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
|
|
builder.setChannelId(NOTIFICATION_CHANNEL_ID);
|
|
|
|
}
|
|
|
|
|
2016-06-27 23:03:03 +00:00
|
|
|
Resources res = getResources();
|
2021-03-08 10:09:22 +00:00
|
|
|
Intent exitIntent = new Intent(this, TermuxService.class).setAction(TERMUX_SERVICE.ACTION_STOP_SERVICE);
|
2016-06-27 23:03:03 +00:00
|
|
|
builder.addAction(android.R.drawable.ic_delete, res.getString(R.string.notification_action_exit), PendingIntent.getService(this, 0, exitIntent, 0));
|
|
|
|
|
2021-03-08 10:09:22 +00:00
|
|
|
String newWakeAction = wakeLockHeld ? TERMUX_SERVICE.ACTION_WAKE_UNLOCK : TERMUX_SERVICE.ACTION_WAKE_LOCK;
|
2016-12-04 03:37:13 +00:00
|
|
|
Intent toggleWakeLockIntent = new Intent(this, TermuxService.class).setAction(newWakeAction);
|
|
|
|
String actionTitle = res.getString(wakeLockHeld ?
|
|
|
|
R.string.notification_action_wake_unlock :
|
|
|
|
R.string.notification_action_wake_lock);
|
|
|
|
int actionIcon = wakeLockHeld ? android.R.drawable.ic_lock_idle_lock : android.R.drawable.ic_lock_lock;
|
|
|
|
builder.addAction(actionIcon, actionTitle, PendingIntent.getService(this, 0, toggleWakeLockIntent, 0));
|
2016-06-27 23:03:03 +00:00
|
|
|
|
|
|
|
return builder.build();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onDestroy() {
|
2021-03-11 15:11:59 +00:00
|
|
|
File termuxTmpDir = TermuxConstants.TERMUX_TMP_DIR;
|
2019-05-21 18:17:51 +00:00
|
|
|
|
|
|
|
if (termuxTmpDir.exists()) {
|
|
|
|
try {
|
|
|
|
TermuxInstaller.deleteFolder(termuxTmpDir.getCanonicalFile());
|
|
|
|
} catch (Exception e) {
|
2019-06-09 11:31:09 +00:00
|
|
|
Log.e(EmulatorDebug.LOG_TAG, "Error while removing file at " + termuxTmpDir.getAbsolutePath(), e);
|
2019-05-21 18:17:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
termuxTmpDir.mkdirs();
|
|
|
|
}
|
|
|
|
|
2016-06-27 23:03:03 +00:00
|
|
|
if (mWakeLock != null) mWakeLock.release();
|
|
|
|
if (mWifiLock != null) mWifiLock.release();
|
|
|
|
|
|
|
|
stopForeground(true);
|
|
|
|
|
|
|
|
for (int i = 0; i < mTerminalSessions.size(); i++)
|
|
|
|
mTerminalSessions.get(i).finishIfRunning();
|
|
|
|
}
|
|
|
|
|
|
|
|
public List<TerminalSession> getSessions() {
|
|
|
|
return mTerminalSessions;
|
|
|
|
}
|
|
|
|
|
|
|
|
TerminalSession createTermSession(String executablePath, String[] arguments, String cwd, boolean failSafe) {
|
2021-03-11 15:11:59 +00:00
|
|
|
TermuxConstants.TERMUX_HOME_DIR.mkdirs();
|
2016-06-27 23:03:03 +00:00
|
|
|
|
2021-03-11 15:11:59 +00:00
|
|
|
if (cwd == null || cwd.isEmpty()) cwd = TermuxConstants.TERMUX_HOME_DIR_PATH;
|
2016-06-27 23:03:03 +00:00
|
|
|
|
2016-10-26 00:26:44 +00:00
|
|
|
String[] env = BackgroundJob.buildEnvironment(failSafe, cwd);
|
2016-11-23 00:55:29 +00:00
|
|
|
boolean isLoginShell = false;
|
2016-06-27 23:03:03 +00:00
|
|
|
|
|
|
|
if (executablePath == null) {
|
2019-06-09 17:59:58 +00:00
|
|
|
if (!failSafe) {
|
|
|
|
for (String shellBinary : new String[]{"login", "bash", "zsh"}) {
|
2021-03-11 15:11:59 +00:00
|
|
|
File shellFile = new File(TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH, shellBinary);
|
2019-06-09 17:59:58 +00:00
|
|
|
if (shellFile.canExecute()) {
|
|
|
|
executablePath = shellFile.getAbsolutePath();
|
|
|
|
break;
|
|
|
|
}
|
2016-06-27 23:03:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (executablePath == null) {
|
|
|
|
// Fall back to system shell as last resort:
|
|
|
|
executablePath = "/system/bin/sh";
|
|
|
|
}
|
2016-11-23 00:55:29 +00:00
|
|
|
isLoginShell = true;
|
2016-06-27 23:03:03 +00:00
|
|
|
}
|
|
|
|
|
2016-11-23 00:55:29 +00:00
|
|
|
String[] processArgs = BackgroundJob.setupProcessArgs(executablePath, arguments);
|
|
|
|
executablePath = processArgs[0];
|
|
|
|
int lastSlashIndex = executablePath.lastIndexOf('/');
|
|
|
|
String processName = (isLoginShell ? "-" : "") +
|
|
|
|
(lastSlashIndex == -1 ? executablePath : executablePath.substring(lastSlashIndex + 1));
|
2016-06-27 23:03:03 +00:00
|
|
|
|
2016-11-23 00:55:29 +00:00
|
|
|
String[] args = new String[processArgs.length];
|
|
|
|
args[0] = processName;
|
|
|
|
if (processArgs.length > 1) System.arraycopy(processArgs, 1, args, 1, processArgs.length - 1);
|
2016-06-27 23:03:03 +00:00
|
|
|
|
|
|
|
TerminalSession session = new TerminalSession(executablePath, cwd, args, env, this);
|
|
|
|
mTerminalSessions.add(session);
|
|
|
|
updateNotification();
|
2019-02-12 16:16:06 +00:00
|
|
|
|
|
|
|
// Make sure that terminal styling is always applied.
|
2021-03-08 10:09:22 +00:00
|
|
|
Intent stylingIntent = new Intent(TERMUX_ACTIVITY.ACTION_RELOAD_STYLE);
|
|
|
|
stylingIntent.putExtra(TERMUX_ACTIVITY.EXTRA_RELOAD_STYLE, "styling");
|
2019-02-12 16:16:06 +00:00
|
|
|
sendBroadcast(stylingIntent);
|
|
|
|
|
2016-06-27 23:03:03 +00:00
|
|
|
return session;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int removeTermSession(TerminalSession sessionToRemove) {
|
|
|
|
int indexOfRemoved = mTerminalSessions.indexOf(sessionToRemove);
|
|
|
|
mTerminalSessions.remove(indexOfRemoved);
|
|
|
|
if (mTerminalSessions.isEmpty() && mWakeLock == null) {
|
|
|
|
// Finish if there are no sessions left and the wake lock is not held, otherwise keep the service alive if
|
|
|
|
// holding wake lock since there may be daemon processes (e.g. sshd) running.
|
|
|
|
stopSelf();
|
|
|
|
} else {
|
|
|
|
updateNotification();
|
|
|
|
}
|
|
|
|
return indexOfRemoved;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onTitleChanged(TerminalSession changedSession) {
|
|
|
|
if (mSessionChangeCallback != null) mSessionChangeCallback.onTitleChanged(changedSession);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSessionFinished(final TerminalSession finishedSession) {
|
|
|
|
if (mSessionChangeCallback != null)
|
|
|
|
mSessionChangeCallback.onSessionFinished(finishedSession);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onTextChanged(TerminalSession changedSession) {
|
|
|
|
if (mSessionChangeCallback != null) mSessionChangeCallback.onTextChanged(changedSession);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onClipboardText(TerminalSession session, String text) {
|
|
|
|
if (mSessionChangeCallback != null) mSessionChangeCallback.onClipboardText(session, text);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onBell(TerminalSession session) {
|
|
|
|
if (mSessionChangeCallback != null) mSessionChangeCallback.onBell(session);
|
|
|
|
}
|
2015-10-25 14:27:32 +00:00
|
|
|
|
2016-06-27 22:56:30 +00:00
|
|
|
@Override
|
|
|
|
public void onColorsChanged(TerminalSession session) {
|
|
|
|
if (mSessionChangeCallback != null) mSessionChangeCallback.onColorsChanged(session);
|
|
|
|
}
|
|
|
|
|
2016-12-04 03:37:13 +00:00
|
|
|
public void onBackgroundJobExited(final BackgroundJob task) {
|
2018-09-28 22:49:05 +00:00
|
|
|
mHandler.post(() -> {
|
|
|
|
mBackgroundTasks.remove(task);
|
|
|
|
updateNotification();
|
2016-12-04 03:37:13 +00:00
|
|
|
});
|
|
|
|
}
|
2017-11-01 20:15:41 +00:00
|
|
|
|
|
|
|
private void setupNotificationChannel() {
|
|
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
|
|
|
|
|
|
|
|
String channelName = "Termux";
|
|
|
|
String channelDescription = "Notifications from Termux";
|
|
|
|
int importance = NotificationManager.IMPORTANCE_LOW;
|
|
|
|
|
|
|
|
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName,importance);
|
|
|
|
channel.setDescription(channelDescription);
|
|
|
|
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
|
|
|
manager.createNotificationChannel(channel);
|
|
|
|
}
|
2015-10-25 14:27:32 +00:00
|
|
|
}
|