Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Typeface;
import android.app.Notification;
import android.app.NotificationManager;
import android.media.AudioAttributes;
import android.media.SoundPool;
import android.text.TextUtils;
Expand Down Expand Up @@ -116,9 +118,12 @@ public void onReloadActivityStyling() {

@Override
public void onTextChanged(@NonNull TerminalSession changedSession) {
if (!mActivity.isVisible()) return;

if (mActivity.getCurrentSession() == changedSession) mActivity.getTerminalView().onScreenUpdated();
// IMPORTANT: Always update the screen, even if Activity is in background
// This prevents character overlay issues when app resumes from background.
// Background updates ensure the display is synchronized when app becomes visible again.
if (mActivity.getCurrentSession() == changedSession) {
mActivity.getTerminalView().onScreenUpdated();
}
}

@Override
Expand Down Expand Up @@ -196,21 +201,84 @@ public void onPasteTextFromClipboard(@Nullable TerminalSession session) {
mActivity.getTerminalView().mEmulator.paste(text);
}


/**
* Send a notification for the bell character event.
* BUGFIX P2: Implement notifications for SSH task completion signals
*
* @param session The terminal session that rang the bell
* @param includeVibration Whether to include vibration in the notification
*/
private void sendBellNotification(@NonNull TerminalSession session, boolean includeVibration) {
try {
NotificationManager nm = (NotificationManager)
mActivity.getSystemService(Context.NOTIFICATION_SERVICE);

if (nm == null) return;

// Get a unique notification ID
int notificationId = com.termux.shared.termux.notification.TermuxNotificationUtils.getNextNotificationId(mActivity);

// Build the notification
Notification.Builder builder = com.termux.shared.termux.notification.TermuxNotificationUtils
.getTermuxOrPluginAppNotificationBuilder(
mActivity,
mActivity,
TermuxConstants.TERMUX_APP_NOTIFICATION_CHANNEL_ID,
Notification.PRIORITY_DEFAULT,
mActivity.getString(R.string.notification_bell_title),
mActivity.getString(R.string.notification_bell_text),
null,
null,
null,
0);

if (builder != null) {
if (includeVibration) {
builder.setVibrate(new long[]{0, 250, 250, 250});
}
nm.notify(notificationId, builder.build());
Logger.logDebug(LOG_TAG, "Bell notification sent with ID: " + notificationId);
}
} catch (Exception e) {
Logger.logError(LOG_TAG, "Error sending bell notification: " + e.getMessage());
}
}

@Override
public void onBell(@NonNull TerminalSession session) {
if (!mActivity.isVisible()) return;

switch (mActivity.getProperties().getBellBehaviour()) {
case TermuxPropertyConstants.IVALUE_BELL_BEHAVIOUR_VIBRATE:
BellHandler.getInstance(mActivity).doBell();
break;
case TermuxPropertyConstants.IVALUE_BELL_BEHAVIOUR_BEEP:
loadBellSoundPool();
if (mBellSoundPool != null)
mBellSoundPool.play(mBellSoundId, 1.f, 1.f, 1, 0, 1.f);
int bellBehaviour = mActivity.getProperties().getBellBehaviour();

// Handle vibrate/beep only when app is visible
if (mActivity.isVisible()) {
switch (bellBehaviour) {
case TermuxPropertyConstants.IVALUE_BELL_BEHAVIOUR_VIBRATE:
BellHandler.getInstance(mActivity).doBell();
break;
case TermuxPropertyConstants.IVALUE_BELL_BEHAVIOUR_BEEP:
loadBellSoundPool();
if (mBellSoundPool != null)
mBellSoundPool.play(mBellSoundId, 1.f, 1.f, 1, 0, 1.f);
break;
case TermuxPropertyConstants.IVALUE_BELL_BEHAVIOUR_VIBRATE_AND_NOTIFICATION:
BellHandler.getInstance(mActivity).doBell();
break;
case TermuxPropertyConstants.IVALUE_BELL_BEHAVIOUR_IGNORE:
// Ignore the bell character.
break;
}
}

// Handle notifications - these work both foreground and background
// BUGFIX P2: Notifications don't have the visibility restriction.
// For vibrate-and-notification, only add vibration to the notification when the app is
// not visible; when visible, BellHandler.doBell() already handles vibration above.
switch (bellBehaviour) {
case TermuxPropertyConstants.IVALUE_BELL_BEHAVIOUR_NOTIFICATION:
sendBellNotification(session, false);
break;
case TermuxPropertyConstants.IVALUE_BELL_BEHAVIOUR_IGNORE:
// Ignore the bell character.
case TermuxPropertyConstants.IVALUE_BELL_BEHAVIOUR_VIBRATE_AND_NOTIFICATION:
sendBellNotification(session, !mActivity.isVisible());
break;
}
}
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@
<string name="notification_action_exit">Exit</string>
<string name="notification_action_wake_lock">Acquire wakelock</string>
<string name="notification_action_wake_unlock">Release wakelock</string>
<string name="notification_bell_title">Command Complete</string>
<string name="notification_bell_text">Remote command finished - Bell signal received</string>



Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,10 +290,18 @@ public void resize(int newColumns, int newRows, int newTotalRows, int[] cursor,
lastNonSpaceIndex = oldLine.getSpaceUsed();
if (cursorAtThisRow) justToCursor = true;
} else {
for (int i = 0; i < oldLine.getSpaceUsed(); i++)
// NEWLY INTRODUCED BUG! Should not index oldLine.mStyle with char indices
if (oldLine.mText[i] != ' '/* || oldLine.mStyle[i] != currentStyle */)
lastNonSpaceIndex = i + 1;
for (int i = 0; i < oldLine.getSpaceUsed(); i++) {
if (oldLine.mText[i] != ' ') {
// BUGFIX P1: mStyle is indexed by columns, not character indices
// Only count non-space characters to find last non-space index
char c = oldLine.mText[i];
int codePoint = (Character.isHighSurrogate(c)) ? Character.toCodePoint(c, oldLine.mText[++i]) : c;
int columnForChar = WcWidth.width(codePoint);
if (columnForChar > 0) {
lastNonSpaceIndex = i + 1;
}
}
}
}

int currentOldCol = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public final class TerminalEmulator {
private final Stack<String> mTitleStack = new Stack<>();

/** The cursor position. Between (0,0) and (mRows-1, mColumns-1). */
private int mCursorRow, mCursorCol;
private volatile int mCursorRow, mCursorCol;

/** The number of character rows and columns in the terminal screen. */
public int mRows, mColumns;
Expand Down Expand Up @@ -421,14 +421,23 @@ private void resizeScreen() {
mCursorRow = cursor[1];
}

public int getCursorRow() {
public synchronized int getCursorRow() {
return mCursorRow;
}

public int getCursorCol() {
public synchronized int getCursorCol() {
return mCursorCol;
}

/**
* Set cursor position safely with thread synchronization.
* BUGFIX P1: Thread-safe cursor updates
*/
public synchronized void setCursorRowCol(int row, int col) {
mCursorRow = row;
mCursorCol = col;
}

/** Get the terminal cursor style. It will be one of {@link #TERMINAL_CURSOR_STYLES_LIST} */
public int getCursorStyle() {
return mCursorStyle;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,15 @@ public final class TermuxPropertyConstants {
public static final String VALUE_BELL_BEHAVIOUR_VIBRATE = "vibrate";
public static final String VALUE_BELL_BEHAVIOUR_BEEP = "beep";
public static final String VALUE_BELL_BEHAVIOUR_IGNORE = "ignore";
public static final String VALUE_BELL_BEHAVIOUR_NOTIFICATION = "notification";
public static final String VALUE_BELL_BEHAVIOUR_VIBRATE_AND_NOTIFICATION = "vibrate-and-notification";
public static final String DEFAULT_VALUE_BELL_BEHAVIOUR = VALUE_BELL_BEHAVIOUR_VIBRATE;

public static final int IVALUE_BELL_BEHAVIOUR_VIBRATE = 1;
public static final int IVALUE_BELL_BEHAVIOUR_BEEP = 2;
public static final int IVALUE_BELL_BEHAVIOUR_IGNORE = 3;
public static final int IVALUE_BELL_BEHAVIOUR_NOTIFICATION = 4;
public static final int IVALUE_BELL_BEHAVIOUR_VIBRATE_AND_NOTIFICATION = 5;
public static final int DEFAULT_IVALUE_BELL_BEHAVIOUR = IVALUE_BELL_BEHAVIOUR_VIBRATE;

/** Defines the bidirectional map for bell behaviour values and their internal values */
Expand All @@ -186,6 +190,8 @@ public final class TermuxPropertyConstants {
.put(VALUE_BELL_BEHAVIOUR_VIBRATE, IVALUE_BELL_BEHAVIOUR_VIBRATE)
.put(VALUE_BELL_BEHAVIOUR_BEEP, IVALUE_BELL_BEHAVIOUR_BEEP)
.put(VALUE_BELL_BEHAVIOUR_IGNORE, IVALUE_BELL_BEHAVIOUR_IGNORE)
.put(VALUE_BELL_BEHAVIOUR_NOTIFICATION, IVALUE_BELL_BEHAVIOUR_NOTIFICATION)
.put(VALUE_BELL_BEHAVIOUR_VIBRATE_AND_NOTIFICATION, IVALUE_BELL_BEHAVIOUR_VIBRATE_AND_NOTIFICATION)
.build();


Expand Down