diff --git a/app/src/main/java/com/termux/app/TermuxService.java b/app/src/main/java/com/termux/app/TermuxService.java index 5cac2719d0..fe7abe74fa 100644 --- a/app/src/main/java/com/termux/app/TermuxService.java +++ b/app/src/main/java/com/termux/app/TermuxService.java @@ -63,12 +63,15 @@ class LocalBinder extends Binder { /** * The terminal sessions which this service manages. - *

- * Note that this list is observed by {@link TermuxActivity#mTermuxSessionListViewController}, so any changes must be made on the UI - * thread and followed by a call to {@link ArrayAdapter#notifyDataSetChanged()} }. + * Note that this list is observed by {@link TermuxActivity#mTermuxSessionListViewController}, + * so any changes must be made on the UI thread and followed by a call to + * {@link ArrayAdapter#notifyDataSetChanged()} }. */ final List mTerminalSessions = new ArrayList<>(); + /** + * The background jobs which this service manages. + */ final List mBackgroundTasks = new ArrayList<>(); /** The full implementation of the {@link TerminalSessionClient} interface to be used by {@link TerminalSession} @@ -86,89 +89,49 @@ class LocalBinder extends Binder { private PowerManager.WakeLock mWakeLock; private WifiManager.WifiLock mWifiLock; - /** If the user has executed the {@link TermuxConstants.TERMUX_APP.TERMUX_SERVICE#ACTION_STOP_SERVICE} intent. */ + /** If the user has executed the {@link TERMUX_SERVICE#ACTION_STOP_SERVICE} intent. */ boolean mWantsToStop = false; private static final String LOG_TAG = "TermuxService"; + @Override + public void onCreate() { + Logger.logVerbose(LOG_TAG, "onCreate"); + runStartForeground(); + } + @SuppressLint("Wakelock") @Override public int onStartCommand(Intent intent, int flags, int startId) { - String action = intent.getAction(); - if (TERMUX_SERVICE.ACTION_STOP_SERVICE.equals(action)) { - mWantsToStop = true; - for (int i = 0; i < mTerminalSessions.size(); i++) - mTerminalSessions.get(i).finishIfRunning(); - stopSelf(); - } 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, 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, TermuxConstants.TERMUX_APP_NAME.toLowerCase()); - mWifiLock.acquire(); - - 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) { - Logger.logStackTraceWithMessage(LOG_TAG, "Failed to call ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS", e); - } - } - - updateNotification(); - } - } else if (TERMUX_SERVICE.ACTION_WAKE_UNLOCK.equals(action)) { - if (mWakeLock != null) { - mWakeLock.release(); - mWakeLock = null; + Logger.logDebug(LOG_TAG, "onStartCommand"); - mWifiLock.release(); - mWifiLock = null; - - updateNotification(); - } - } else if (TERMUX_SERVICE.ACTION_SERVICE_EXECUTE.equals(action)) { - Uri executableUri = intent.getData(); - String executablePath = (executableUri == null ? null : executableUri.getPath()); - - String[] arguments = (executableUri == null ? null : intent.getStringArrayExtra(TERMUX_SERVICE.EXTRA_ARGUMENTS)); - String cwd = intent.getStringExtra(TERMUX_SERVICE.EXTRA_WORKDIR); - - if (intent.getBooleanExtra(TERMUX_SERVICE.EXTRA_BACKGROUND, false)) { - BackgroundJob task = new BackgroundJob(cwd, executablePath, arguments, this, intent.getParcelableExtra("pendingIntent")); - mBackgroundTasks.add(task); - updateNotification(); - } else { - boolean failsafe = intent.getBooleanExtra(TERMUX_ACTIVITY.ACTION_FAILSAFE_SESSION, false); - TerminalSession newSession = createTermSession(executablePath, arguments, cwd, failsafe); - - // 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; - } + // Run again in case service is already started and onCreate() is not called + runStartForeground(); - // Make the newly created session the current one to be displayed: - TermuxAppSharedPreferences preferences = new TermuxAppSharedPreferences(this); - preferences.setCurrentSession(newSession.mHandle); + String action = intent.getAction(); - // Launch the main Termux app, which will now show the current session: - startActivity(new Intent(this, TermuxActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + if (action != null) { + switch (action) { + case TERMUX_SERVICE.ACTION_STOP_SERVICE: + Logger.logDebug(LOG_TAG, "ACTION_STOP_SERVICE intent received"); + actionStopService(); + break; + case TERMUX_SERVICE.ACTION_WAKE_LOCK: + Logger.logDebug(LOG_TAG, "ACTION_WAKE_LOCK intent received"); + actionAcquireWakeLock(); + break; + case TERMUX_SERVICE.ACTION_WAKE_UNLOCK: + Logger.logDebug(LOG_TAG, "ACTION_WAKE_UNLOCK intent received"); + actionReleaseWakeLock(true); + break; + case TERMUX_SERVICE.ACTION_SERVICE_EXECUTE: + Logger.logDebug(LOG_TAG, "ACTION_SERVICE_EXECUTE intent received"); + actionServiceExecute(intent); + break; + default: + Logger.logError(LOG_TAG, "Invalid action: \"" + action + "\""); + break; } - } else if (action != null) { - 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 @@ -176,108 +139,194 @@ public int onStartCommand(Intent intent, int flags, int startId) { return Service.START_NOT_STICKY; } + @Override + public void onDestroy() { + Logger.logVerbose(LOG_TAG, "onDestroy"); + File termuxTmpDir = TermuxConstants.TERMUX_TMP_DIR; + + if (termuxTmpDir.exists()) { + try { + TermuxInstaller.deleteFolder(termuxTmpDir.getCanonicalFile()); + } catch (Exception e) { + Logger.logStackTraceWithMessage(LOG_TAG, "Error while removing file at " + termuxTmpDir.getAbsolutePath(), e); + } + + termuxTmpDir.mkdirs(); + } + + actionReleaseWakeLock(false); + finishAllTerminalSessions(); + runStopForeground(); + } + @Override public IBinder onBind(Intent intent) { + Logger.logVerbose(LOG_TAG, "onBind"); return mBinder; } @Override - public void onCreate() { + public boolean onUnbind(Intent intent) { + Logger.logVerbose(LOG_TAG, "onUnbind"); + + // Since we cannot rely on {@link TermuxActivity.onDestroy()} to always complete, + // we unset clients here as well if it failed, so that we do not leave service and session + // clients with references to the activity. + if(mTermuxSessionClient != null) + unsetTermuxSessionClient(); + return false; + } + + /** Make service run in foreground mode. */ + private void runStartForeground() { setupNotificationChannel(); startForeground(NOTIFICATION_ID, buildNotification()); } - /** Update the shown foreground service notification after making any changes that affect it. */ - void updateNotification() { - if (mWakeLock == null && mTerminalSessions.isEmpty() && mBackgroundTasks.isEmpty()) { - // Exit if we are updating after the user disabled all locks with no sessions or tasks running. - stopSelf(); + /** Make service leave foreground mode. */ + private void runStopForeground() { + stopForeground(true); + } + + /** Request to stop service. */ + private void requestStopService() { + Logger.logDebug(LOG_TAG, "Requesting to stop service"); + runStopForeground(); + stopSelf(); + } + + + + /** Process action to stop service. */ + private void actionStopService() { + mWantsToStop = true; + finishAllTerminalSessions(); + requestStopService(); + } + + /** Process action to acquire Power and Wi-Fi WakeLocks. */ + @SuppressLint({"WakelockTimeout", "BatteryLife"}) + private void actionAcquireWakeLock() { + if (mWakeLock == null) { + Logger.logDebug(LOG_TAG, "Acquiring WakeLocks"); + + PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + 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, TermuxConstants.TERMUX_APP_NAME.toLowerCase()); + mWifiLock.acquire(); + + 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) { + Logger.logStackTraceWithMessage(LOG_TAG, "Failed to call ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS", e); + } + } + + updateNotification(); + + Logger.logDebug(LOG_TAG, "WakeLocks acquired successfully"); } else { - ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).notify(NOTIFICATION_ID, buildNotification()); + Logger.logDebug(LOG_TAG, "Ignoring acquiring WakeLocks since they are already held"); } } - 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); + /** Process action to release Power and Wi-Fi WakeLocks. */ + private void actionReleaseWakeLock(boolean updateNotification) { + if (mWakeLock == null && mWifiLock == null){ + Logger.logDebug(LOG_TAG, "Ignoring releasing WakeLocks since none are already held"); + return; + } - int sessionCount = mTerminalSessions.size(); - int taskCount = mBackgroundTasks.size(); - String contentText = sessionCount + " session" + (sessionCount == 1 ? "" : "s"); - if (taskCount > 0) { - contentText += ", " + taskCount + " task" + (taskCount == 1 ? "" : "s"); + Logger.logDebug(LOG_TAG, "Releasing WakeLocks"); + + if (mWakeLock != null) { + mWakeLock.release(); + mWakeLock = null; } - final boolean wakeLockHeld = mWakeLock != null; - if (wakeLockHeld) contentText += " (wake lock held)"; + if (mWifiLock != null) { + mWifiLock.release(); + mWifiLock = null; + } - 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(updateNotification) + updateNotification(); - // If holding a wake or wifi lock consider the notification of high priority since it's using power, - // otherwise use a low priority - builder.setPriority((wakeLockHeld) ? Notification.PRIORITY_HIGH : Notification.PRIORITY_LOW); + Logger.logDebug(LOG_TAG, "WakeLocks released successfully"); + } - // No need to show a timestamp: - builder.setShowWhen(false); + /** Process action to execute a shell command in a foreground session or in background. */ + private void actionServiceExecute(Intent intent) { + Uri executableUri = intent.getData(); + String executablePath = (executableUri == null ? null : executableUri.getPath()); - // Background color for small notification icon: - builder.setColor(0xFF607D8B); + String[] arguments = (executableUri == null ? null : intent.getStringArrayExtra(TERMUX_SERVICE.EXTRA_ARGUMENTS)); + String cwd = intent.getStringExtra(TERMUX_SERVICE.EXTRA_WORKDIR); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - builder.setChannelId(NOTIFICATION_CHANNEL_ID); + PendingIntent pendingIntent = intent.getParcelableExtra(TERMUX_SERVICE.EXTRA_PENDING_INTENT); + + if (intent.getBooleanExtra(TERMUX_SERVICE.EXTRA_BACKGROUND, false)) { + executeBackgroundCommand(executablePath, arguments, cwd, pendingIntent); + } else { + executeForegroundCommand(intent, executablePath, arguments, cwd); } + } - Resources res = getResources(); - Intent exitIntent = new Intent(this, TermuxService.class).setAction(TERMUX_SERVICE.ACTION_STOP_SERVICE); - builder.addAction(android.R.drawable.ic_delete, res.getString(R.string.notification_action_exit), PendingIntent.getService(this, 0, exitIntent, 0)); + /** Execute a shell command in background with {@link BackgroundJob}. */ + private void executeBackgroundCommand(String executablePath, String[] arguments, String cwd, PendingIntent pendingIntent) { + Logger.logDebug(LOG_TAG, "Starting background command"); - String newWakeAction = wakeLockHeld ? TERMUX_SERVICE.ACTION_WAKE_UNLOCK : TERMUX_SERVICE.ACTION_WAKE_LOCK; - 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)); + BackgroundJob task = new BackgroundJob(cwd, executablePath, arguments, this, pendingIntent); + mBackgroundTasks.add(task); + updateNotification(); + } - return builder.build(); + /** Callback received when a {@link BackgroundJob} finishes. */ + public void onBackgroundJobExited(final BackgroundJob task) { + mHandler.post(() -> { + mBackgroundTasks.remove(task); + updateNotification(); + }); } - @Override - public void onDestroy() { - File termuxTmpDir = TermuxConstants.TERMUX_TMP_DIR; + /** Execute a shell command in a foreground terminal session. */ + private void executeForegroundCommand(Intent intent, String executablePath, String[] arguments, String cwd) { + Logger.logDebug(LOG_TAG, "Starting foreground command"); - if (termuxTmpDir.exists()) { - try { - TermuxInstaller.deleteFolder(termuxTmpDir.getCanonicalFile()); - } catch (Exception e) { - Logger.logStackTraceWithMessage(LOG_TAG, "Error while removing file at " + termuxTmpDir.getAbsolutePath(), e); - } + boolean failsafe = intent.getBooleanExtra(TERMUX_ACTIVITY.ACTION_FAILSAFE_SESSION, false); + TerminalSession newSession = createTerminalSession(executablePath, arguments, cwd, failsafe); - termuxTmpDir.mkdirs(); + // 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; } - if (mWakeLock != null) mWakeLock.release(); - if (mWifiLock != null) mWifiLock.release(); - - stopForeground(true); + // Make the newly created session the current one to be displayed: + TermuxAppSharedPreferences preferences = new TermuxAppSharedPreferences(this); + preferences.setCurrentSession(newSession.mHandle); - for (int i = 0; i < mTerminalSessions.size(); i++) - mTerminalSessions.get(i).finishIfRunning(); + // Launch the main Termux app, which will now show the current session: + startActivity(new Intent(this, TermuxActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); } - public List getSessions() { - return mTerminalSessions; - } - public TerminalSession createTermSession(String executablePath, String[] arguments, String cwd, boolean failSafe) { + + /** Create a terminal session. */ + public TerminalSession createTerminalSession(String executablePath, String[] arguments, String cwd, boolean failSafe) { TermuxConstants.TERMUX_HOME_DIR.mkdirs(); if (cwd == null || cwd.isEmpty()) cwd = TermuxConstants.TERMUX_HOME_DIR_PATH; @@ -325,19 +374,29 @@ public TerminalSession createTermSession(String executablePath, String[] argumen return session; } - public int removeTermSession(TerminalSession sessionToRemove) { + /** Remove a terminal session. */ + public int removeTerminalSession(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(); + requestStopService(); } else { updateNotification(); } return indexOfRemoved; } + /** Finish all terminal sessions by sending SIGKILL to their shells. */ + private void finishAllTerminalSessions() { + for (int i = 0; i < mTerminalSessions.size(); i++) + mTerminalSessions.get(i).finishIfRunning(); + } + + + /** If {@link TermuxActivity} has not bound to the {@link TermuxService} yet or is destroyed, then * interface functions requiring the activity should not be available to the terminal sessions, * so we just return the {@link #mTermuxSessionClientBase}. Once {@link TermuxActivity} bind @@ -371,22 +430,70 @@ public void setTermuxSessionClient(TermuxSessionClient termuxSessionClient) { mTerminalSessions.get(i).updateTerminalSessionClient(mTermuxSessionClient); } - /** This should be called when {@link TermuxActivity} has been destroyed so that the - * {@link TermuxService} and {@link TerminalSession} and {@link TerminalEmulator} clients do not - * hold an activity references. + /** This should be called when {@link TermuxActivity} has been destroyed and in {@link #onUnbind(Intent)} + * so that the {@link TermuxService} and {@link TerminalSession} and {@link TerminalEmulator} + * clients do not hold an activity references. */ public void unsetTermuxSessionClient() { - mTermuxSessionClient = null; - for (int i = 0; i < mTerminalSessions.size(); i++) mTerminalSessions.get(i).updateTerminalSessionClient(mTermuxSessionClientBase); + + mTermuxSessionClient = null; } - public void onBackgroundJobExited(final BackgroundJob task) { - mHandler.post(() -> { - mBackgroundTasks.remove(task); - updateNotification(); - }); + + + 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(); + int taskCount = mBackgroundTasks.size(); + String contentText = sessionCount + " session" + (sessionCount == 1 ? "" : "s"); + if (taskCount > 0) { + contentText += ", " + taskCount + " task" + (taskCount == 1 ? "" : "s"); + } + + final boolean wakeLockHeld = mWakeLock != null; + if (wakeLockHeld) contentText += " (wake lock held)"; + + 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, + // otherwise use a low priority + builder.setPriority((wakeLockHeld) ? Notification.PRIORITY_HIGH : Notification.PRIORITY_LOW); + + // No need to show a timestamp: + builder.setShowWhen(false); + + // Background color for small notification icon: + builder.setColor(0xFF607D8B); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + builder.setChannelId(NOTIFICATION_CHANNEL_ID); + } + + Resources res = getResources(); + Intent exitIntent = new Intent(this, TermuxService.class).setAction(TERMUX_SERVICE.ACTION_STOP_SERVICE); + builder.addAction(android.R.drawable.ic_delete, res.getString(R.string.notification_action_exit), PendingIntent.getService(this, 0, exitIntent, 0)); + + String newWakeAction = wakeLockHeld ? TERMUX_SERVICE.ACTION_WAKE_UNLOCK : TERMUX_SERVICE.ACTION_WAKE_LOCK; + 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)); + + return builder.build(); } private void setupNotificationChannel() { @@ -402,7 +509,24 @@ private void setupNotificationChannel() { manager.createNotificationChannel(channel); } + /** Update the shown foreground service notification after making any changes that affect it. */ + void updateNotification() { + if (mWakeLock == null && mTerminalSessions.isEmpty() && mBackgroundTasks.isEmpty()) { + // Exit if we are updating after the user disabled all locks with no sessions or tasks running. + requestStopService(); + } else { + ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).notify(NOTIFICATION_ID, buildNotification()); + } + } + + + public boolean wantsToStop() { return mWantsToStop; } + + public List getSessions() { + return mTerminalSessions; + } + } diff --git a/app/src/main/java/com/termux/app/terminal/TermuxSessionClient.java b/app/src/main/java/com/termux/app/terminal/TermuxSessionClient.java index 6222b0aa92..7997277367 100644 --- a/app/src/main/java/com/termux/app/terminal/TermuxSessionClient.java +++ b/app/src/main/java/com/termux/app/terminal/TermuxSessionClient.java @@ -153,9 +153,10 @@ void noteSessionInfo() { final int indexOfSession = mActivity.getTermuxService().getSessions().indexOf(session); mActivity.showToast(toToastTitle(session), false); mActivity.terminalSessionListNotifyUpdated(); - final ListView lv = mActivity.findViewById(R.id.terminal_sessions_list); - lv.setItemChecked(indexOfSession, true); - lv.smoothScrollToPosition(indexOfSession); + + final ListView termuxSessionsListView = mActivity.findViewById(R.id.terminal_sessions_list); + termuxSessionsListView.setItemChecked(indexOfSession, true); + termuxSessionsListView.smoothScrollToPosition(indexOfSession); } public void switchToSession(boolean forward) { @@ -195,7 +196,7 @@ public void addNewSession(boolean failSafe, String sessionName) { workingDirectory = currentSession.getCwd(); } - TerminalSession newSession = mActivity.getTermuxService().createTermSession(null, null, workingDirectory, failSafe); + TerminalSession newSession = mActivity.getTermuxService().createTerminalSession(null, null, workingDirectory, failSafe); if (sessionName != null) { newSession.mSessionName = sessionName; } @@ -248,7 +249,7 @@ public void removeFinishedSession(TerminalSession finishedSession) { // Return pressed with finished session - remove it. TermuxService service = mActivity.getTermuxService(); - int index = service.removeTermSession(finishedSession); + int index = service.removeTerminalSession(finishedSession); mActivity.terminalSessionListNotifyUpdated(); if (mActivity.getTermuxService().getSessions().isEmpty()) { // There are no sessions to show, so finish the activity.