mirror of
https://github.com/fankes/termux-app.git
synced 2025-09-06 10:45:23 +08:00
Add the TermuxSession class for linking a TerminalSession to an ExecutionCommand.
TermuxSession will maintain info for foreground Termux sessions. Each terminal session started by TermuxService will now be linked to a ExectionCommand that started it. This also fixes bugs where newly created session in some cases were not being automatically selected and scrolled to, like when adding a named or failsafe session or those which were created for executable intents.
This commit is contained in:
@@ -214,7 +214,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
// Get the session stored in shared preferences stored by {@link #onStop} if its valid,
|
// Get the session stored in shared preferences stored by {@link #onStop} if its valid,
|
||||||
// otherwise get the last session currently running.
|
// otherwise get the last session currently running.
|
||||||
mTermuxSessionClient.setCurrentSession(mTermuxSessionClient.getCurrentStoredSessionOrLast());
|
mTermuxSessionClient.setCurrentSession(mTermuxSessionClient.getCurrentStoredSessionOrLast());
|
||||||
terminalSessionListNotifyUpdated();
|
termuxSessionListNotifyUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
registerReceiver(mTermuxActivityBroadcastReceiever, new IntentFilter(TERMUX_ACTIVITY.ACTION_RELOAD_STYLE));
|
registerReceiver(mTermuxActivityBroadcastReceiever, new IntentFilter(TERMUX_ACTIVITY.ACTION_RELOAD_STYLE));
|
||||||
@@ -242,7 +242,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
|
|
||||||
setTermuxSessionsListView();
|
setTermuxSessionsListView();
|
||||||
|
|
||||||
if (mTermuxService.getSessions().isEmpty()) {
|
if (mTermuxService.isTermuxSessionsEmpty()) {
|
||||||
if (mIsVisible) {
|
if (mIsVisible) {
|
||||||
TermuxInstaller.setupIfNeeded(TermuxActivity.this, () -> {
|
TermuxInstaller.setupIfNeeded(TermuxActivity.this, () -> {
|
||||||
if (mTermuxService == null) return; // Activity might have been destroyed.
|
if (mTermuxService == null) return; // Activity might have been destroyed.
|
||||||
@@ -395,9 +395,10 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
View newSessionButton = findViewById(R.id.new_session_button);
|
View newSessionButton = findViewById(R.id.new_session_button);
|
||||||
newSessionButton.setOnClickListener(v -> mTermuxSessionClient.addNewSession(false, null));
|
newSessionButton.setOnClickListener(v -> mTermuxSessionClient.addNewSession(false, null));
|
||||||
newSessionButton.setOnLongClickListener(v -> {
|
newSessionButton.setOnLongClickListener(v -> {
|
||||||
DialogUtils.textInput(TermuxActivity.this, R.string.title_create_named_session, null, R.string.action_create_named_session_confirm,
|
DialogUtils.textInput(TermuxActivity.this, R.string.title_create_named_session, null,
|
||||||
text -> mTermuxSessionClient.addNewSession(false, text), R.string.action_new_session_failsafe, text -> mTermuxSessionClient.addNewSession(true, text)
|
R.string.action_create_named_session_confirm, text -> mTermuxSessionClient.addNewSession(false, text),
|
||||||
, -1, null, null);
|
R.string.action_new_session_failsafe, text -> mTermuxSessionClient.addNewSession(true, text),
|
||||||
|
-1, null, null);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -439,7 +440,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
|
|
||||||
private void setTermuxSessionsListView() {
|
private void setTermuxSessionsListView() {
|
||||||
ListView termuxSessionsListView = findViewById(R.id.terminal_sessions_list);
|
ListView termuxSessionsListView = findViewById(R.id.terminal_sessions_list);
|
||||||
mTermuxSessionListViewController = new TermuxSessionsListViewController(this, mTermuxService.getSessions());
|
mTermuxSessionListViewController = new TermuxSessionsListViewController(this, mTermuxService.getTermuxSessions());
|
||||||
termuxSessionsListView.setAdapter(mTermuxSessionListViewController);
|
termuxSessionsListView.setAdapter(mTermuxSessionListViewController);
|
||||||
termuxSessionsListView.setOnItemClickListener(mTermuxSessionListViewController);
|
termuxSessionsListView.setOnItemClickListener(mTermuxSessionListViewController);
|
||||||
termuxSessionsListView.setOnItemLongClickListener(mTermuxSessionListViewController);
|
termuxSessionsListView.setOnItemLongClickListener(mTermuxSessionListViewController);
|
||||||
@@ -468,6 +469,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
|
|
||||||
/** Show a toast and dismiss the last one if still visible. */
|
/** Show a toast and dismiss the last one if still visible. */
|
||||||
public void showToast(String text, boolean longDuration) {
|
public void showToast(String text, boolean longDuration) {
|
||||||
|
if(text == null || text.isEmpty()) return;
|
||||||
if (mLastToast != null) mLastToast.cancel();
|
if (mLastToast != null) mLastToast.cancel();
|
||||||
mLastToast = Toast.makeText(TermuxActivity.this, text, longDuration ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT);
|
mLastToast = Toast.makeText(TermuxActivity.this, text, longDuration ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT);
|
||||||
mLastToast.setGravity(Gravity.TOP, 0, 0);
|
mLastToast.setGravity(Gravity.TOP, 0, 0);
|
||||||
@@ -642,7 +644,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
return (DrawerLayout) findViewById(R.id.drawer_layout);
|
return (DrawerLayout) findViewById(R.id.drawer_layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void terminalSessionListNotifyUpdated() {
|
public void termuxSessionListNotifyUpdated() {
|
||||||
mTermuxSessionListViewController.notifyDataSetChanged();
|
mTermuxSessionListViewController.notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -683,6 +685,13 @@ public final class TermuxActivity extends Activity implements ServiceConnection
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static void updateTermuxActivityStyling(Context context) {
|
||||||
|
// Make sure that terminal styling is always applied.
|
||||||
|
Intent stylingIntent = new Intent(TERMUX_ACTIVITY.ACTION_RELOAD_STYLE);
|
||||||
|
stylingIntent.putExtra(TERMUX_ACTIVITY.EXTRA_RELOAD_STYLE, "styling");
|
||||||
|
context.sendBroadcast(stylingIntent);
|
||||||
|
}
|
||||||
|
|
||||||
class TermuxActivityBroadcastReceiever extends BroadcastReceiver {
|
class TermuxActivityBroadcastReceiever extends BroadcastReceiver {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
@@ -24,9 +24,11 @@ import com.termux.R;
|
|||||||
import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_ACTIVITY;
|
import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_ACTIVITY;
|
||||||
import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_SERVICE;
|
import com.termux.app.TermuxConstants.TERMUX_APP.TERMUX_SERVICE;
|
||||||
import com.termux.app.settings.preferences.TermuxAppSharedPreferences;
|
import com.termux.app.settings.preferences.TermuxAppSharedPreferences;
|
||||||
|
import com.termux.app.terminal.TermuxSession;
|
||||||
import com.termux.app.terminal.TermuxSessionClient;
|
import com.termux.app.terminal.TermuxSessionClient;
|
||||||
import com.termux.app.terminal.TermuxSessionClientBase;
|
import com.termux.app.terminal.TermuxSessionClientBase;
|
||||||
import com.termux.app.utils.Logger;
|
import com.termux.app.utils.Logger;
|
||||||
|
import com.termux.app.utils.ShellUtils;
|
||||||
import com.termux.app.utils.TextDataUtils;
|
import com.termux.app.utils.TextDataUtils;
|
||||||
import com.termux.models.ExecutionCommand;
|
import com.termux.models.ExecutionCommand;
|
||||||
import com.termux.models.ExecutionCommand.ExecutionState;
|
import com.termux.models.ExecutionCommand.ExecutionState;
|
||||||
@@ -38,8 +40,10 @@ import java.io.File;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A service holding a list of terminal sessions, {@link #mTerminalSessions}, showing a foreground notification while
|
* A service holding a list of termux sessions, {@link #mTermuxSessions}, showing a foreground notification while
|
||||||
* running so that it is not terminated. The user interacts with the session through {@link TermuxActivity}, but this
|
* 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
|
* 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.
|
* restart {@link TermuxActivity} later to yet again access the sessions.
|
||||||
@@ -67,12 +71,12 @@ public final class TermuxService extends Service {
|
|||||||
private final Handler mHandler = new Handler();
|
private final Handler mHandler = new Handler();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The terminal sessions which this service manages.
|
* The termux sessions which this service manages.
|
||||||
* Note that this list is observed by {@link TermuxActivity#mTermuxSessionListViewController},
|
* 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
|
* so any changes must be made on the UI thread and followed by a call to
|
||||||
* {@link ArrayAdapter#notifyDataSetChanged()} }.
|
* {@link ArrayAdapter#notifyDataSetChanged()} }.
|
||||||
*/
|
*/
|
||||||
final List<TerminalSession> mTerminalSessions = new ArrayList<>();
|
final List<TermuxSession> mTermuxSessions = new ArrayList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The background jobs which this service manages.
|
* The background jobs which this service manages.
|
||||||
@@ -160,7 +164,7 @@ public final class TermuxService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
actionReleaseWakeLock(false);
|
actionReleaseWakeLock(false);
|
||||||
finishAllTerminalSessions();
|
finishAllTermuxSessions();
|
||||||
runStopForeground();
|
runStopForeground();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +209,7 @@ public final class TermuxService extends Service {
|
|||||||
/** Process action to stop service. */
|
/** Process action to stop service. */
|
||||||
private void actionStopService() {
|
private void actionStopService() {
|
||||||
mWantsToStop = true;
|
mWantsToStop = true;
|
||||||
finishAllTerminalSessions();
|
finishAllTermuxSessions();
|
||||||
requestStopService();
|
requestStopService();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,7 +277,8 @@ public final class TermuxService extends Service {
|
|||||||
Logger.logDebug(LOG_TAG, "WakeLocks released successfully");
|
Logger.logDebug(LOG_TAG, "WakeLocks released successfully");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Process action to execute a shell command in a foreground session or in background. */
|
/** Process {@link TERMUX_SERVICE#ACTION_SERVICE_EXECUTE} intent to execute a shell command in
|
||||||
|
* a foreground termux session or in background. */
|
||||||
private void actionServiceExecute(Intent intent) {
|
private void actionServiceExecute(Intent intent) {
|
||||||
if (intent == null){
|
if (intent == null){
|
||||||
Logger.logError(LOG_TAG, "Ignoring null intent to actionServiceExecute");
|
Logger.logError(LOG_TAG, "Ignoring null intent to actionServiceExecute");
|
||||||
@@ -303,15 +308,18 @@ public final class TermuxService extends Service {
|
|||||||
if (executionCommand.inBackground) {
|
if (executionCommand.inBackground) {
|
||||||
executeBackgroundCommand(executionCommand);
|
executeBackgroundCommand(executionCommand);
|
||||||
} else {
|
} else {
|
||||||
executeForegroundCommand(executionCommand);
|
executeTermuxSessionCommand(executionCommand);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Execute a shell command in background with {@link BackgroundJob}. */
|
/** Execute a shell command in background with {@link BackgroundJob}. */
|
||||||
private void executeBackgroundCommand(ExecutionCommand executionCommand) {
|
private void executeBackgroundCommand(ExecutionCommand executionCommand) {
|
||||||
|
if (executionCommand == null) return;
|
||||||
|
|
||||||
Logger.logDebug(LOG_TAG, "Starting background command");
|
Logger.logDebug(LOG_TAG, "Starting background command");
|
||||||
|
|
||||||
Logger.logDebug(LOG_TAG, executionCommand.toString());
|
if(Logger.getLogLevel() >= Logger.LOG_LEVEL_VERBOSE)
|
||||||
|
Logger.logVerbose(LOG_TAG, executionCommand.toString());
|
||||||
|
|
||||||
BackgroundJob task = new BackgroundJob(executionCommand, this);
|
BackgroundJob task = new BackgroundJob(executionCommand, this);
|
||||||
|
|
||||||
@@ -328,65 +336,134 @@ public final class TermuxService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Execute a shell command in a foreground terminal session. */
|
/** Execute a shell command in a foreground terminal session. */
|
||||||
private void executeForegroundCommand(ExecutionCommand executionCommand) {
|
private void executeTermuxSessionCommand(ExecutionCommand executionCommand) {
|
||||||
Logger.logDebug(LOG_TAG, "Starting foreground command");
|
if (executionCommand == null) return;
|
||||||
|
|
||||||
|
Logger.logDebug(LOG_TAG, "Starting foreground termux session command");
|
||||||
|
|
||||||
if(!executionCommand.setState(ExecutionState.EXECUTING))
|
String sessionName = null;
|
||||||
return;
|
|
||||||
|
|
||||||
Logger.logDebug(LOG_TAG, executionCommand.toString());
|
|
||||||
|
|
||||||
TerminalSession newSession = createTerminalSession(executionCommand.executable, executionCommand.arguments, executionCommand.workingDirectory, executionCommand.isFailsafe);
|
|
||||||
|
|
||||||
// Transform executable path to session name, e.g. "/bin/do-something.sh" => "do something.sh".
|
// Transform executable path to session name, e.g. "/bin/do-something.sh" => "do something.sh".
|
||||||
if (executionCommand.executable != null) {
|
if (executionCommand.executable != null) {
|
||||||
int lastSlash = executionCommand.executable.lastIndexOf('/');
|
sessionName = ShellUtils.getExecutableBasename(executionCommand.executable).replace('-', ' ');
|
||||||
String name = (lastSlash == -1) ? executionCommand.executable : executionCommand.executable.substring(lastSlash + 1);
|
|
||||||
name = name.replace('-', ' ');
|
|
||||||
newSession.mSessionName = name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TermuxSession newTermuxSession = createTermuxSession(executionCommand, sessionName);
|
||||||
|
if (newTermuxSession == null) return;
|
||||||
|
|
||||||
|
handleSessionAction(TextDataUtils.getIntFromString(executionCommand.sessionAction,
|
||||||
|
TERMUX_SERVICE.VALUE_EXTRA_SESSION_ACTION_SWITCH_TO_NEW_SESSION_AND_OPEN_ACTIVITY),
|
||||||
|
newTermuxSession.getTerminalSession());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a termux session.
|
||||||
|
* Currently called by {@link TermuxSessionClient#addNewSession(boolean, String)} to add a new termux session.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public TermuxSession createTermuxSession(String executablePath, String[] arguments, String workingDirectory, boolean isFailSafe, String sessionName) {
|
||||||
|
return createTermuxSession(new ExecutionCommand(getNextExecutionId(), executablePath, arguments, workingDirectory, false, isFailSafe), sessionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Create a termux session. */
|
||||||
|
@Nullable
|
||||||
|
public synchronized TermuxSession createTermuxSession(ExecutionCommand executionCommand, String sessionName) {
|
||||||
|
if (executionCommand == null) return null;
|
||||||
|
|
||||||
|
Logger.logDebug(LOG_TAG, "Creating termux session");
|
||||||
|
|
||||||
|
if (executionCommand.inBackground) {
|
||||||
|
Logger.logDebug(LOG_TAG, "Ignoring a background execution command passed to createTermuxSession()");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Logger.getLogLevel() >= Logger.LOG_LEVEL_VERBOSE)
|
||||||
|
Logger.logVerbose(LOG_TAG, executionCommand.toString());
|
||||||
|
|
||||||
|
TermuxSession newTermuxSession = TermuxSession.create(executionCommand, getTermuxSessionClient(), sessionName);
|
||||||
|
if (newTermuxSession == null) {
|
||||||
|
Logger.logError(LOG_TAG, "Failed to execute new termux session command for:\n" + executionCommand.toString());
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
mTermuxSessions.add(newTermuxSession);
|
||||||
|
|
||||||
// Notify {@link TermuxSessionsListViewController} that sessions list has been updated if
|
// Notify {@link TermuxSessionsListViewController} that sessions list has been updated if
|
||||||
// activity in is foreground
|
// activity in is foreground
|
||||||
if(mTermuxSessionClient != null)
|
if(mTermuxSessionClient != null)
|
||||||
mTermuxSessionClient.terminalSessionListNotifyUpdated();
|
mTermuxSessionClient.termuxSessionListNotifyUpdated();
|
||||||
|
|
||||||
handleSessionAction(TextDataUtils.getIntFromString(executionCommand.sessionAction, TERMUX_SERVICE.VALUE_EXTRA_SESSION_ACTION_SWITCH_TO_NEW_SESSION_AND_OPEN_ACTIVITY), newSession);
|
updateNotification();
|
||||||
|
TermuxActivity.updateTermuxActivityStyling(this);
|
||||||
|
|
||||||
|
return newTermuxSession;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setCurrentStoredSession(TerminalSession newSession) {
|
/** Remove a termux session. */
|
||||||
// Make the newly created session the current one to be displayed:
|
public synchronized int removeTermuxSession(TerminalSession sessionToRemove) {
|
||||||
TermuxAppSharedPreferences preferences = new TermuxAppSharedPreferences(this);
|
int index = getIndexOfSession(sessionToRemove);
|
||||||
preferences.setCurrentSession(newSession.mHandle);
|
|
||||||
|
if(index >= 0) {
|
||||||
|
TermuxSession termuxSession = mTermuxSessions.get(index);
|
||||||
|
|
||||||
|
if (termuxSession.getExecutionCommand().setState(ExecutionState.EXECUTED)) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
mTermuxSessions.remove(termuxSession);
|
||||||
|
|
||||||
|
// Notify {@link TermuxSessionsListViewController} that sessions list has been updated if
|
||||||
|
// activity in is foreground
|
||||||
|
if(mTermuxSessionClient != null)
|
||||||
|
mTermuxSessionClient.termuxSessionListNotifyUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mTermuxSessions.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.
|
||||||
|
requestStopService();
|
||||||
|
} else {
|
||||||
|
updateNotification();
|
||||||
|
}
|
||||||
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Finish all termux sessions by sending SIGKILL to their shells. */
|
||||||
|
private synchronized void finishAllTermuxSessions() {
|
||||||
|
for (int i = 0; i < mTermuxSessions.size(); i++)
|
||||||
|
mTermuxSessions.get(i).getTerminalSession().finishIfRunning();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Process session action for new session. */
|
/** Process session action for new session. */
|
||||||
private void handleSessionAction(int sessionAction, TerminalSession newSession) {
|
private void handleSessionAction(int sessionAction, TerminalSession newTerminalSession) {
|
||||||
Logger.logDebug(LOG_TAG, "Processing sessionAction \"" + sessionAction + "\" for session \"" + newSession.mSessionName + "\"");
|
Logger.logDebug(LOG_TAG, "Processing sessionAction \"" + sessionAction + "\" for session \"" + newTerminalSession.mSessionName + "\"");
|
||||||
|
|
||||||
switch (sessionAction) {
|
switch (sessionAction) {
|
||||||
case TERMUX_SERVICE.VALUE_EXTRA_SESSION_ACTION_SWITCH_TO_NEW_SESSION_AND_OPEN_ACTIVITY:
|
case TERMUX_SERVICE.VALUE_EXTRA_SESSION_ACTION_SWITCH_TO_NEW_SESSION_AND_OPEN_ACTIVITY:
|
||||||
setCurrentStoredSession(newSession);
|
setCurrentStoredTerminalSession(newTerminalSession);
|
||||||
if(mTermuxSessionClient != null)
|
if(mTermuxSessionClient != null)
|
||||||
mTermuxSessionClient.setCurrentSession(newSession);
|
mTermuxSessionClient.setCurrentSession(newTerminalSession);
|
||||||
startTermuxActivity();
|
startTermuxActivity();
|
||||||
break;
|
break;
|
||||||
case TERMUX_SERVICE.VALUE_EXTRA_SESSION_ACTION_KEEP_CURRENT_SESSION_AND_OPEN_ACTIVITY:
|
case TERMUX_SERVICE.VALUE_EXTRA_SESSION_ACTION_KEEP_CURRENT_SESSION_AND_OPEN_ACTIVITY:
|
||||||
if(mTerminalSessions.size() == 1)
|
if(getTermuxSessionsSize() == 1)
|
||||||
setCurrentStoredSession(newSession);
|
setCurrentStoredTerminalSession(newTerminalSession);
|
||||||
startTermuxActivity();
|
startTermuxActivity();
|
||||||
break;
|
break;
|
||||||
case TERMUX_SERVICE.VALUE_EXTRA_SESSION_ACTION_SWITCH_TO_NEW_SESSION_AND_DONT_OPEN_ACTIVITY:
|
case TERMUX_SERVICE.VALUE_EXTRA_SESSION_ACTION_SWITCH_TO_NEW_SESSION_AND_DONT_OPEN_ACTIVITY:
|
||||||
setCurrentStoredSession(newSession);
|
setCurrentStoredTerminalSession(newTerminalSession);
|
||||||
if(mTermuxSessionClient != null)
|
if(mTermuxSessionClient != null)
|
||||||
mTermuxSessionClient.setCurrentSession(newSession);
|
mTermuxSessionClient.setCurrentSession(newTerminalSession);
|
||||||
break;
|
break;
|
||||||
case TERMUX_SERVICE.VALUE_EXTRA_SESSION_ACTION_KEEP_CURRENT_SESSION_AND_DONT_OPEN_ACTIVITY:
|
case TERMUX_SERVICE.VALUE_EXTRA_SESSION_ACTION_KEEP_CURRENT_SESSION_AND_DONT_OPEN_ACTIVITY:
|
||||||
if(mTerminalSessions.size() == 1)
|
if(getTermuxSessionsSize() == 1)
|
||||||
setCurrentStoredSession(newSession);
|
setCurrentStoredTerminalSession(newTerminalSession);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Logger.logError(LOG_TAG, "Invalid sessionAction: \"" + sessionAction + "\"");
|
Logger.logError(LOG_TAG, "Invalid sessionAction: \"" + sessionAction + "\". Force using default sessionAction.");
|
||||||
|
handleSessionAction(TERMUX_SERVICE.VALUE_EXTRA_SESSION_ACTION_SWITCH_TO_NEW_SESSION_AND_OPEN_ACTIVITY, newTerminalSession);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -396,78 +473,6 @@ public final class TermuxService extends Service {
|
|||||||
startActivity(new Intent(this, TermuxActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
startActivity(new Intent(this, TermuxActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Create a terminal session. */
|
|
||||||
public TerminalSession createTerminalSession(String executablePath, String[] arguments, String workingDirectory, boolean isFailSafe) {
|
|
||||||
TermuxConstants.TERMUX_HOME_DIR.mkdirs();
|
|
||||||
|
|
||||||
if (workingDirectory == null || workingDirectory.isEmpty()) workingDirectory = TermuxConstants.TERMUX_HOME_DIR_PATH;
|
|
||||||
|
|
||||||
String[] env = BackgroundJob.buildEnvironment(isFailSafe, workingDirectory);
|
|
||||||
boolean isLoginShell = false;
|
|
||||||
|
|
||||||
if (executablePath == null) {
|
|
||||||
if (!isFailSafe) {
|
|
||||||
for (String shellBinary : new String[]{"login", "bash", "zsh"}) {
|
|
||||||
File shellFile = new File(TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH, shellBinary);
|
|
||||||
if (shellFile.canExecute()) {
|
|
||||||
executablePath = shellFile.getAbsolutePath();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (executablePath == null) {
|
|
||||||
// Fall back to system shell as last resort:
|
|
||||||
executablePath = "/system/bin/sh";
|
|
||||||
}
|
|
||||||
isLoginShell = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] processArgs = BackgroundJob.setupProcessArgs(executablePath, arguments);
|
|
||||||
executablePath = processArgs[0];
|
|
||||||
int lastSlashIndex = executablePath.lastIndexOf('/');
|
|
||||||
String processName = (isLoginShell ? "-" : "") +
|
|
||||||
(lastSlashIndex == -1 ? executablePath : executablePath.substring(lastSlashIndex + 1));
|
|
||||||
|
|
||||||
String[] args = new String[processArgs.length];
|
|
||||||
args[0] = processName;
|
|
||||||
if (processArgs.length > 1) System.arraycopy(processArgs, 1, args, 1, processArgs.length - 1);
|
|
||||||
|
|
||||||
TerminalSession session = new TerminalSession(executablePath, workingDirectory, args, env, getTermuxSessionClient());
|
|
||||||
mTerminalSessions.add(session);
|
|
||||||
updateNotification();
|
|
||||||
|
|
||||||
// Make sure that terminal styling is always applied.
|
|
||||||
Intent stylingIntent = new Intent(TERMUX_ACTIVITY.ACTION_RELOAD_STYLE);
|
|
||||||
stylingIntent.putExtra(TERMUX_ACTIVITY.EXTRA_RELOAD_STYLE, "styling");
|
|
||||||
sendBroadcast(stylingIntent);
|
|
||||||
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 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.
|
|
||||||
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
|
/** 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,
|
* interface functions requiring the activity should not be available to the terminal sessions,
|
||||||
* so we just return the {@link #mTermuxSessionClientBase}. Once {@link TermuxActivity} bind
|
* so we just return the {@link #mTermuxSessionClientBase}. Once {@link TermuxActivity} bind
|
||||||
@@ -479,7 +484,7 @@ public final class TermuxService extends Service {
|
|||||||
* @return Returns the {@link TermuxSessionClient} if {@link TermuxActivity} has bound with
|
* @return Returns the {@link TermuxSessionClient} if {@link TermuxActivity} has bound with
|
||||||
* {@link TermuxService}, otherwise {@link TermuxSessionClientBase}.
|
* {@link TermuxService}, otherwise {@link TermuxSessionClientBase}.
|
||||||
*/
|
*/
|
||||||
public TermuxSessionClientBase getTermuxSessionClient() {
|
public synchronized TermuxSessionClientBase getTermuxSessionClient() {
|
||||||
if (mTermuxSessionClient != null)
|
if (mTermuxSessionClient != null)
|
||||||
return mTermuxSessionClient;
|
return mTermuxSessionClient;
|
||||||
else
|
else
|
||||||
@@ -494,20 +499,20 @@ public final class TermuxService extends Service {
|
|||||||
* @param termuxSessionClient The {@link TermuxSessionClient} object that fully
|
* @param termuxSessionClient The {@link TermuxSessionClient} object that fully
|
||||||
* implements the {@link TerminalSessionClient} interface.
|
* implements the {@link TerminalSessionClient} interface.
|
||||||
*/
|
*/
|
||||||
public void setTermuxSessionClient(TermuxSessionClient termuxSessionClient) {
|
public synchronized void setTermuxSessionClient(TermuxSessionClient termuxSessionClient) {
|
||||||
mTermuxSessionClient = termuxSessionClient;
|
mTermuxSessionClient = termuxSessionClient;
|
||||||
|
|
||||||
for (int i = 0; i < mTerminalSessions.size(); i++)
|
for (int i = 0; i < mTermuxSessions.size(); i++)
|
||||||
mTerminalSessions.get(i).updateTerminalSessionClient(mTermuxSessionClient);
|
mTermuxSessions.get(i).getTerminalSession().updateTerminalSessionClient(mTermuxSessionClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** This should be called when {@link TermuxActivity} has been destroyed and in {@link #onUnbind(Intent)}
|
/** 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}
|
* so that the {@link TermuxService} and {@link TerminalSession} and {@link TerminalEmulator}
|
||||||
* clients do not hold an activity references.
|
* clients do not hold an activity references.
|
||||||
*/
|
*/
|
||||||
public void unsetTermuxSessionClient() {
|
public synchronized void unsetTermuxSessionClient() {
|
||||||
for (int i = 0; i < mTerminalSessions.size(); i++)
|
for (int i = 0; i < mTermuxSessions.size(); i++)
|
||||||
mTerminalSessions.get(i).updateTerminalSessionClient(mTermuxSessionClientBase);
|
mTermuxSessions.get(i).getTerminalSession().updateTerminalSessionClient(mTermuxSessionClientBase);
|
||||||
|
|
||||||
mTermuxSessionClient = null;
|
mTermuxSessionClient = null;
|
||||||
}
|
}
|
||||||
@@ -521,7 +526,7 @@ public final class TermuxService extends Service {
|
|||||||
notifyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
notifyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notifyIntent, 0);
|
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notifyIntent, 0);
|
||||||
|
|
||||||
int sessionCount = mTerminalSessions.size();
|
int sessionCount = getTermuxSessionsSize();
|
||||||
int taskCount = mBackgroundTasks.size();
|
int taskCount = mBackgroundTasks.size();
|
||||||
String contentText = sessionCount + " session" + (sessionCount == 1 ? "" : "s");
|
String contentText = sessionCount + " session" + (sessionCount == 1 ? "" : "s");
|
||||||
if (taskCount > 0) {
|
if (taskCount > 0) {
|
||||||
@@ -582,7 +587,7 @@ public final class TermuxService extends Service {
|
|||||||
|
|
||||||
/** Update the shown foreground service notification after making any changes that affect it. */
|
/** Update the shown foreground service notification after making any changes that affect it. */
|
||||||
void updateNotification() {
|
void updateNotification() {
|
||||||
if (mWakeLock == null && mTerminalSessions.isEmpty() && mBackgroundTasks.isEmpty()) {
|
if (mWakeLock == null && mTermuxSessions.isEmpty() && mBackgroundTasks.isEmpty()) {
|
||||||
// Exit if we are updating after the user disabled all locks with no sessions or tasks running.
|
// Exit if we are updating after the user disabled all locks with no sessions or tasks running.
|
||||||
requestStopService();
|
requestStopService();
|
||||||
} else {
|
} else {
|
||||||
@@ -592,16 +597,63 @@ public final class TermuxService extends Service {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void setCurrentStoredTerminalSession(TerminalSession session) {
|
||||||
|
if(session == null) return;
|
||||||
|
// Make the newly created session the current one to be displayed:
|
||||||
|
TermuxAppSharedPreferences preferences = new TermuxAppSharedPreferences(this);
|
||||||
|
preferences.setCurrentSession(session.mHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean isTermuxSessionsEmpty() {
|
||||||
|
return mTermuxSessions.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized int getTermuxSessionsSize() {
|
||||||
|
return mTermuxSessions.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized List<TermuxSession> getTermuxSessions() {
|
||||||
|
return mTermuxSessions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public synchronized TermuxSession getTermuxSession(int index) {
|
||||||
|
if(index >= 0 && index < mTermuxSessions.size())
|
||||||
|
return mTermuxSessions.get(index);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized TermuxSession getLastTermuxSession() {
|
||||||
|
return mTermuxSessions.isEmpty() ? null : mTermuxSessions.get(mTermuxSessions.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized int getIndexOfSession(TerminalSession terminalSession) {
|
||||||
|
for (int i = 0; i < mTermuxSessions.size(); i++) {
|
||||||
|
if (mTermuxSessions.get(i).getTerminalSession().equals(terminalSession))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized TerminalSession getTerminalSessionForHandle(String sessionHandle) {
|
||||||
|
TerminalSession terminalSession;
|
||||||
|
for (int i = 0, len = mTermuxSessions.size(); i < len; i++) {
|
||||||
|
terminalSession = mTermuxSessions.get(i).getTerminalSession();
|
||||||
|
if (terminalSession.mHandle.equals(sessionHandle))
|
||||||
|
return terminalSession;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static synchronized int getNextExecutionId() {
|
||||||
|
return EXECUTION_ID++;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean wantsToStop() {
|
public boolean wantsToStop() {
|
||||||
return mWantsToStop;
|
return mWantsToStop;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<TerminalSession> getSessions() {
|
|
||||||
return mTerminalSessions;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized public static int getNextExecutionId() {
|
|
||||||
return EXECUTION_ID++;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
87
app/src/main/java/com/termux/app/terminal/TermuxSession.java
Normal file
87
app/src/main/java/com/termux/app/terminal/TermuxSession.java
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
package com.termux.app.terminal;
|
||||||
|
|
||||||
|
import com.termux.app.TermuxConstants;
|
||||||
|
import com.termux.app.utils.Logger;
|
||||||
|
import com.termux.app.utils.ShellUtils;
|
||||||
|
import com.termux.models.ExecutionCommand;
|
||||||
|
import com.termux.terminal.TerminalSession;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that maintains info for foreground Termux sessions.
|
||||||
|
* It also provides a way to link each {@link TerminalSession} with the {@link ExecutionCommand}
|
||||||
|
* that started it.
|
||||||
|
*/
|
||||||
|
public class TermuxSession {
|
||||||
|
|
||||||
|
private final TerminalSession mTerminalSession;
|
||||||
|
private final ExecutionCommand mExecutionCommand;
|
||||||
|
|
||||||
|
private static final String LOG_TAG = "TermuxSession";
|
||||||
|
|
||||||
|
private TermuxSession(TerminalSession terminalSession, ExecutionCommand executionCommand) {
|
||||||
|
this.mTerminalSession = terminalSession;
|
||||||
|
this.mExecutionCommand = executionCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TermuxSession create(ExecutionCommand executionCommand, TermuxSessionClientBase termuxSessionClient, String sessionName) {
|
||||||
|
TermuxConstants.TERMUX_HOME_DIR.mkdirs();
|
||||||
|
|
||||||
|
if (executionCommand.workingDirectory == null || executionCommand.workingDirectory.isEmpty()) executionCommand.workingDirectory = TermuxConstants.TERMUX_HOME_DIR_PATH;
|
||||||
|
|
||||||
|
String[] environment = ShellUtils.buildEnvironment(executionCommand.isFailsafe, executionCommand.workingDirectory);
|
||||||
|
boolean isLoginShell = false;
|
||||||
|
|
||||||
|
if (executionCommand.executable == null) {
|
||||||
|
if (!executionCommand.isFailsafe) {
|
||||||
|
for (String shellBinary : new String[]{"login", "bash", "zsh"}) {
|
||||||
|
File shellFile = new File(TermuxConstants.TERMUX_BIN_PREFIX_DIR_PATH, shellBinary);
|
||||||
|
if (shellFile.canExecute()) {
|
||||||
|
executionCommand.executable = shellFile.getAbsolutePath();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (executionCommand.executable == null) {
|
||||||
|
// Fall back to system shell as last resort:
|
||||||
|
executionCommand.executable = "/system/bin/sh";
|
||||||
|
}
|
||||||
|
isLoginShell = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] processArgs = ShellUtils.setupProcessArgs(executionCommand.executable, executionCommand.arguments);
|
||||||
|
|
||||||
|
executionCommand.executable = processArgs[0];
|
||||||
|
String processName = (isLoginShell ? "-" : "") + ShellUtils.getExecutableBasename(executionCommand.executable);
|
||||||
|
|
||||||
|
String[] arguments = new String[processArgs.length];
|
||||||
|
arguments[0] = processName;
|
||||||
|
if (processArgs.length > 1) System.arraycopy(processArgs, 1, arguments, 1, processArgs.length - 1);
|
||||||
|
|
||||||
|
executionCommand.arguments = arguments;
|
||||||
|
|
||||||
|
if(!executionCommand.setState(ExecutionCommand.ExecutionState.EXECUTING))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Logger.logDebug(LOG_TAG, executionCommand.toString());
|
||||||
|
|
||||||
|
TerminalSession terminalSession = new TerminalSession(executionCommand.executable, executionCommand.workingDirectory, executionCommand.arguments, environment, termuxSessionClient);
|
||||||
|
|
||||||
|
if (sessionName != null) {
|
||||||
|
terminalSession.mSessionName = sessionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TermuxSession(terminalSession, executionCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TerminalSession getTerminalSession() {
|
||||||
|
return mTerminalSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExecutionCommand getExecutionCommand() {
|
||||||
|
return mExecutionCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -27,7 +27,6 @@ import com.termux.terminal.TextStyle;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
public class TermuxSessionClient extends TermuxSessionClientBase {
|
public class TermuxSessionClient extends TermuxSessionClientBase {
|
||||||
@@ -68,7 +67,7 @@ public class TermuxSessionClient extends TermuxSessionClientBase {
|
|||||||
mActivity.showToast(toToastTitle(updatedSession), true);
|
mActivity.showToast(toToastTitle(updatedSession), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
terminalSessionListNotifyUpdated();
|
termuxSessionListNotifyUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -81,7 +80,7 @@ public class TermuxSessionClient extends TermuxSessionClientBase {
|
|||||||
|
|
||||||
if (mActivity.isVisible() && finishedSession != mActivity.getCurrentSession()) {
|
if (mActivity.isVisible() && finishedSession != mActivity.getCurrentSession()) {
|
||||||
// Show toast for non-current sessions that exit.
|
// Show toast for non-current sessions that exit.
|
||||||
int indexOfSession = mActivity.getTermuxService().getSessions().indexOf(finishedSession);
|
int indexOfSession = mActivity.getTermuxService().getIndexOfSession(finishedSession);
|
||||||
// Verify that session was not removed before we got told about it finishing:
|
// Verify that session was not removed before we got told about it finishing:
|
||||||
if (indexOfSession >= 0)
|
if (indexOfSession >= 0)
|
||||||
mActivity.showToast(toToastTitle(finishedSession) + " - exited", true);
|
mActivity.showToast(toToastTitle(finishedSession) + " - exited", true);
|
||||||
@@ -90,7 +89,7 @@ public class TermuxSessionClient extends TermuxSessionClientBase {
|
|||||||
if (mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
|
if (mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
|
||||||
// On Android TV devices we need to use older behaviour because we may
|
// On Android TV devices we need to use older behaviour because we may
|
||||||
// not be able to have multiple launcher icons.
|
// not be able to have multiple launcher icons.
|
||||||
if (mActivity.getTermuxService().getSessions().size() > 1) {
|
if (mActivity.getTermuxService().getTermuxSessionsSize() > 1) {
|
||||||
removeFinishedSession(finishedSession);
|
removeFinishedSession(finishedSession);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -100,8 +99,6 @@ public class TermuxSessionClient extends TermuxSessionClientBase {
|
|||||||
removeFinishedSession(finishedSession);
|
removeFinishedSession(finishedSession);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
terminalSessionListNotifyUpdated();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -138,38 +135,49 @@ public class TermuxSessionClient extends TermuxSessionClientBase {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Try switching to session and note about it, but do nothing if already displaying the session. */
|
/** Try switching to session. */
|
||||||
public void setCurrentSession(TerminalSession session) {
|
public void setCurrentSession(TerminalSession session) {
|
||||||
|
if(session == null) return;
|
||||||
|
|
||||||
if (mActivity.getTerminalView().attachSession(session)) {
|
if (mActivity.getTerminalView().attachSession(session)) {
|
||||||
noteSessionInfo();
|
// notify about switched session if not already displaying the session
|
||||||
updateBackgroundColor();
|
notifyOfSessionChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We call the following even when the session is already being displayed since config may
|
||||||
|
// be stale, like current session not selected or scrolled to.
|
||||||
|
checkAndScrollToSession(session);
|
||||||
|
updateBackgroundColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
void noteSessionInfo() {
|
void notifyOfSessionChange() {
|
||||||
if (!mActivity.isVisible()) return;
|
if (!mActivity.isVisible()) return;
|
||||||
|
|
||||||
TerminalSession session = mActivity.getCurrentSession();
|
TerminalSession session = mActivity.getCurrentSession();
|
||||||
final int indexOfSession = mActivity.getTermuxService().getSessions().indexOf(session);
|
|
||||||
mActivity.showToast(toToastTitle(session), false);
|
mActivity.showToast(toToastTitle(session), false);
|
||||||
terminalSessionListNotifyUpdated();
|
|
||||||
|
|
||||||
final ListView termuxSessionsListView = mActivity.findViewById(R.id.terminal_sessions_list);
|
|
||||||
termuxSessionsListView.setItemChecked(indexOfSession, true);
|
|
||||||
termuxSessionsListView.smoothScrollToPosition(indexOfSession);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void switchToSession(boolean forward) {
|
public void switchToSession(boolean forward) {
|
||||||
TermuxService service = mActivity.getTermuxService();
|
TermuxService service = mActivity.getTermuxService();
|
||||||
|
|
||||||
TerminalSession currentSession = mActivity.getCurrentSession();
|
TerminalSession currentTerminalSession = mActivity.getCurrentSession();
|
||||||
int index = service.getSessions().indexOf(currentSession);
|
int index = service.getIndexOfSession(currentTerminalSession);
|
||||||
|
int size = service.getTermuxSessionsSize();
|
||||||
if (forward) {
|
if (forward) {
|
||||||
if (++index >= service.getSessions().size()) index = 0;
|
if (++index >= size) index = 0;
|
||||||
} else {
|
} else {
|
||||||
if (--index < 0) index = service.getSessions().size() - 1;
|
if (--index < 0) index = size - 1;
|
||||||
}
|
}
|
||||||
setCurrentSession(service.getSessions().get(index));
|
|
||||||
|
TermuxSession termuxSession = service.getTermuxSession(index);
|
||||||
|
if(termuxSession != null)
|
||||||
|
setCurrentSession(termuxSession.getTerminalSession());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void switchToSession(int index) {
|
||||||
|
TermuxSession termuxSession = mActivity.getTermuxService().getTermuxSession(index);
|
||||||
|
if(termuxSession != null)
|
||||||
|
setCurrentSession(termuxSession.getTerminalSession());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("InflateParams")
|
@SuppressLint("InflateParams")
|
||||||
@@ -178,12 +186,12 @@ public class TermuxSessionClient extends TermuxSessionClientBase {
|
|||||||
|
|
||||||
DialogUtils.textInput(mActivity, R.string.title_rename_session, sessionToRename.mSessionName, R.string.action_rename_session_confirm, text -> {
|
DialogUtils.textInput(mActivity, R.string.title_rename_session, sessionToRename.mSessionName, R.string.action_rename_session_confirm, text -> {
|
||||||
sessionToRename.mSessionName = text;
|
sessionToRename.mSessionName = text;
|
||||||
terminalSessionListNotifyUpdated();
|
termuxSessionListNotifyUpdated();
|
||||||
}, -1, null, -1, null, null);
|
}, -1, null, -1, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addNewSession(boolean isFailSafe, String sessionName) {
|
public void addNewSession(boolean isFailSafe, String sessionName) {
|
||||||
if (mActivity.getTermuxService().getSessions().size() >= MAX_SESSIONS) {
|
if (mActivity.getTermuxService().getTermuxSessionsSize() >= MAX_SESSIONS) {
|
||||||
new AlertDialog.Builder(mActivity).setTitle(R.string.title_max_terminals_reached).setMessage(R.string.msg_max_terminals_reached)
|
new AlertDialog.Builder(mActivity).setTitle(R.string.title_max_terminals_reached).setMessage(R.string.msg_max_terminals_reached)
|
||||||
.setPositiveButton(android.R.string.ok, null).show();
|
.setPositiveButton(android.R.string.ok, null).show();
|
||||||
} else {
|
} else {
|
||||||
@@ -196,11 +204,12 @@ public class TermuxSessionClient extends TermuxSessionClientBase {
|
|||||||
workingDirectory = currentSession.getCwd();
|
workingDirectory = currentSession.getCwd();
|
||||||
}
|
}
|
||||||
|
|
||||||
TerminalSession newSession = mActivity.getTermuxService().createTerminalSession(null, null, workingDirectory, isFailSafe);
|
TermuxSession newTermuxSession = mActivity.getTermuxService().createTermuxSession(null, null, workingDirectory, isFailSafe, sessionName);
|
||||||
if (sessionName != null) {
|
if (newTermuxSession == null) return;
|
||||||
newSession.mSessionName = sessionName;
|
|
||||||
}
|
TerminalSession newTerminalSession = newTermuxSession.getTerminalSession();
|
||||||
setCurrentSession(newSession);
|
setCurrentSession(newTerminalSession);
|
||||||
|
|
||||||
mActivity.getDrawer().closeDrawers();
|
mActivity.getDrawer().closeDrawers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -222,8 +231,11 @@ public class TermuxSessionClient extends TermuxSessionClientBase {
|
|||||||
return stored;
|
return stored;
|
||||||
} else {
|
} else {
|
||||||
// Else return the last session currently running
|
// Else return the last session currently running
|
||||||
List<TerminalSession> sessions = mActivity.getTermuxService().getSessions();
|
TermuxSession termuxSession = mActivity.getTermuxService().getLastTermuxSession();
|
||||||
return sessions.isEmpty() ? null : sessions.get(sessions.size() - 1);
|
if(termuxSession != null)
|
||||||
|
return termuxSession.getTerminalSession();
|
||||||
|
else
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,41 +247,50 @@ public class TermuxSessionClient extends TermuxSessionClientBase {
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
// Check if the session handle found matches one of the currently running sessions
|
// Check if the session handle found matches one of the currently running sessions
|
||||||
List<TerminalSession> sessions = context.getTermuxService().getSessions();
|
return context.getTermuxService().getTerminalSessionForHandle(sessionHandle);
|
||||||
for (int i = 0, len = sessions.size(); i < len; i++) {
|
|
||||||
TerminalSession session = sessions.get(i);
|
|
||||||
if (session.mHandle.equals(sessionHandle))
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeFinishedSession(TerminalSession finishedSession) {
|
public void removeFinishedSession(TerminalSession finishedSession) {
|
||||||
// Return pressed with finished session - remove it.
|
// Return pressed with finished session - remove it.
|
||||||
TermuxService service = mActivity.getTermuxService();
|
TermuxService service = mActivity.getTermuxService();
|
||||||
|
|
||||||
int index = service.removeTerminalSession(finishedSession);
|
int index = service.removeTermuxSession(finishedSession);
|
||||||
terminalSessionListNotifyUpdated();
|
int size = mActivity.getTermuxService().getTermuxSessionsSize();
|
||||||
if (mActivity.getTermuxService().getSessions().isEmpty()) {
|
if (size == 0) {
|
||||||
// There are no sessions to show, so finish the activity.
|
// There are no sessions to show, so finish the activity.
|
||||||
mActivity.finishActivityIfNotFinishing();
|
mActivity.finishActivityIfNotFinishing();
|
||||||
} else {
|
} else {
|
||||||
if (index >= service.getSessions().size()) {
|
if (index >= size) {
|
||||||
index = service.getSessions().size() - 1;
|
index = size - 1;
|
||||||
}
|
}
|
||||||
setCurrentSession(service.getSessions().get(index));
|
TermuxSession termuxSession = service.getTermuxSession(index);
|
||||||
|
if(termuxSession != null)
|
||||||
|
setCurrentSession(termuxSession.getTerminalSession());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void terminalSessionListNotifyUpdated() {
|
public void termuxSessionListNotifyUpdated() {
|
||||||
mActivity.terminalSessionListNotifyUpdated();
|
mActivity.termuxSessionListNotifyUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkAndScrollToSession(TerminalSession session) {
|
||||||
|
if (!mActivity.isVisible()) return;
|
||||||
|
final int indexOfSession = mActivity.getTermuxService().getIndexOfSession(session);
|
||||||
|
if (indexOfSession < 0) return;
|
||||||
|
final ListView termuxSessionsListView = mActivity.findViewById(R.id.terminal_sessions_list);
|
||||||
|
if(termuxSessionsListView == null) return;
|
||||||
|
|
||||||
|
termuxSessionsListView.setItemChecked(indexOfSession, true);
|
||||||
|
// Delay is necessary otherwise sometimes scroll to newly added session does not happen
|
||||||
|
termuxSessionsListView.postDelayed(() -> termuxSessionsListView.smoothScrollToPosition(indexOfSession), 1000);
|
||||||
|
|
||||||
|
Logger.logError("scrolled to " + indexOfSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
String toToastTitle(TerminalSession session) {
|
String toToastTitle(TerminalSession session) {
|
||||||
final int indexOfSession = mActivity.getTermuxService().getSessions().indexOf(session);
|
final int indexOfSession = mActivity.getTermuxService().getIndexOfSession(session);
|
||||||
|
if (indexOfSession < 0) return null;
|
||||||
StringBuilder toastTitle = new StringBuilder("[" + (indexOfSession + 1) + "]");
|
StringBuilder toastTitle = new StringBuilder("[" + (indexOfSession + 1) + "]");
|
||||||
if (!TextUtils.isEmpty(session.mSessionName)) {
|
if (!TextUtils.isEmpty(session.mSessionName)) {
|
||||||
toastTitle.append(" ").append(session.mSessionName);
|
toastTitle.append(" ").append(session.mSessionName);
|
||||||
@@ -311,6 +332,7 @@ public class TermuxSessionClient extends TermuxSessionClientBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void updateBackgroundColor() {
|
public void updateBackgroundColor() {
|
||||||
|
if (!mActivity.isVisible()) return;
|
||||||
TerminalSession session = mActivity.getCurrentSession();
|
TerminalSession session = mActivity.getCurrentSession();
|
||||||
if (session != null && session.getEmulator() != null) {
|
if (session != null && session.getEmulator() != null) {
|
||||||
mActivity.getWindow().getDecorView().setBackgroundColor(session.getEmulator().mColors.mCurrentColors[TextStyle.COLOR_INDEX_BACKGROUND]);
|
mActivity.getWindow().getDecorView().setBackgroundColor(session.getEmulator().mColors.mCurrentColors[TextStyle.COLOR_INDEX_BACKGROUND]);
|
||||||
|
@@ -24,14 +24,14 @@ import com.termux.terminal.TerminalSession;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class TermuxSessionsListViewController extends ArrayAdapter<TerminalSession> implements AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener {
|
public class TermuxSessionsListViewController extends ArrayAdapter<TermuxSession> implements AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener {
|
||||||
|
|
||||||
final TermuxActivity mActivity;
|
final TermuxActivity mActivity;
|
||||||
|
|
||||||
final StyleSpan boldSpan = new StyleSpan(Typeface.BOLD);
|
final StyleSpan boldSpan = new StyleSpan(Typeface.BOLD);
|
||||||
final StyleSpan italicSpan = new StyleSpan(Typeface.ITALIC);
|
final StyleSpan italicSpan = new StyleSpan(Typeface.ITALIC);
|
||||||
|
|
||||||
public TermuxSessionsListViewController(TermuxActivity activity, List<TerminalSession> sessionList) {
|
public TermuxSessionsListViewController(TermuxActivity activity, List<TermuxSession> sessionList) {
|
||||||
super(activity.getApplicationContext(), R.layout.item_terminal_sessions_list, sessionList);
|
super(activity.getApplicationContext(), R.layout.item_terminal_sessions_list, sessionList);
|
||||||
this.mActivity = activity;
|
this.mActivity = activity;
|
||||||
}
|
}
|
||||||
@@ -48,7 +48,7 @@ public class TermuxSessionsListViewController extends ArrayAdapter<TerminalSessi
|
|||||||
|
|
||||||
TextView sessionTitleView = sessionRowView.findViewById(R.id.session_title);
|
TextView sessionTitleView = sessionRowView.findViewById(R.id.session_title);
|
||||||
|
|
||||||
TerminalSession sessionAtRow = getItem(position);
|
TerminalSession sessionAtRow = getItem(position).getTerminalSession();
|
||||||
if (sessionAtRow == null) {
|
if (sessionAtRow == null) {
|
||||||
sessionTitleView.setText("null session");
|
sessionTitleView.setText("null session");
|
||||||
return sessionRowView;
|
return sessionRowView;
|
||||||
@@ -91,16 +91,16 @@ public class TermuxSessionsListViewController extends ArrayAdapter<TerminalSessi
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||||
TerminalSession clickedSession = getItem(position);
|
TermuxSession clickedSession = getItem(position);
|
||||||
mActivity.getTermuxSessionClient().setCurrentSession(clickedSession);
|
mActivity.getTermuxSessionClient().setCurrentSession(clickedSession.getTerminalSession());
|
||||||
mActivity.getDrawer().closeDrawers();
|
mActivity.getDrawer().closeDrawers();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
|
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
|
||||||
final TerminalSession selectedSession = getItem(position);
|
final TermuxSession selectedSession = getItem(position);
|
||||||
mActivity.getTermuxSessionClient().renameSession(selectedSession);
|
mActivity.getTermuxSessionClient().renameSession(selectedSession.getTerminalSession());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -20,7 +20,6 @@ import android.widget.Toast;
|
|||||||
|
|
||||||
import com.termux.R;
|
import com.termux.R;
|
||||||
import com.termux.app.TermuxActivity;
|
import com.termux.app.TermuxActivity;
|
||||||
import com.termux.app.TermuxService;
|
|
||||||
import com.termux.app.terminal.io.KeyboardShortcut;
|
import com.termux.app.terminal.io.KeyboardShortcut;
|
||||||
import com.termux.app.terminal.io.extrakeys.ExtraKeysView;
|
import com.termux.app.terminal.io.extrakeys.ExtraKeysView;
|
||||||
import com.termux.app.settings.properties.TermuxPropertyConstants;
|
import com.termux.app.settings.properties.TermuxPropertyConstants;
|
||||||
@@ -135,10 +134,8 @@ public class TermuxViewClient implements TerminalViewClient {
|
|||||||
} else if (unicodeChar == '-') {
|
} else if (unicodeChar == '-') {
|
||||||
changeFontSize(false);
|
changeFontSize(false);
|
||||||
} else if (unicodeChar >= '1' && unicodeChar <= '9') {
|
} else if (unicodeChar >= '1' && unicodeChar <= '9') {
|
||||||
int num = unicodeChar - '1';
|
int index = unicodeChar - '1';
|
||||||
TermuxService service = mActivity.getTermuxService();
|
mTermuxSessionClient.switchToSession(index);
|
||||||
if (service.getSessions().size() > num)
|
|
||||||
mTermuxSessionClient.setCurrentSession(service.getSessions().get(num));
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user