Compare commits

..

15 Commits

Author SHA1 Message Date
agnostic-apollo
4dfed3320e Bump to v0.114 2021-06-11 02:57:56 +05:00
agnostic-apollo
7ac62c9840 Allow users to disable terminal session change toast
The user can add `disable-terminal-session-change-toast=true` entry to `termux.properties` file to disable terminal session change toast. The default value is `false`. Running `termux-reload-settings` command will also update the behaviour instantaneously if changed.

Closes #2118
2021-06-11 02:54:47 +05:00
agnostic-apollo
fd80cdaf23 Change default extra keys style
If a user does not define a custom value in termux.properties file, then by default 2 rows will be shown with all arrow keys (up/down/left/right) for ease of terminal use.
2021-06-11 02:39:11 +05:00
agnostic-apollo
19c690d02b Fix issue where if termux installer failed with an exception after prefix directory was already created, then try again would load a broken environment. 2021-06-10 08:06:43 +05:00
agnostic-apollo
e119d34bca Fix issue where terminal cursor blinking would not automatically start again if termux activity was restarted after exiting it with double back press 2021-06-10 08:03:12 +05:00
agnostic-apollo
f545ebf0bd Allow users to set terminal cursor style with termux.properties
This `terminal-cursor-style` key can be used to set the terminal cursor style. The user can set a string value to `block` for `■`, `underline` for `_` or `bar` for `|` cursor style. The default value is still `block`. So adding an entry like `terminal-cursor-style=bar` to `termux.properties` file will allow users to change to the `bar` cursor style. After updating the value, termux must be restarted. You can also run `termux-reload-settings` command so that termux loads the updated value, but only new sessions will use the updated value, existing sessions will not be affected unless you Reset them from terminal's long hold options menu `More` -> `Reset` or restart termux activity after double back press to exit.

You can temporarily switch to different cursor styles with (or add to `.bashrc` but resetting will restore default `bar` style):

- block: `echo -e "\033[2 q"`
- underline: `echo -e "\033[4 q"`
- bar: ` echo -e "\033[6 q"`

Closes #2075
2021-06-10 06:14:12 +05:00
agnostic-apollo
0b4bbaf23d Allow users to adjust terminal transcript rows with termux.properties
This `terminal-transcript-rows` key can be used to adjust the terminal transcript rows. The user can set an integer value between `100` and `50000`. The default value is still `2000`. So adding an entry like `terminal-transcript-rows=10000` to `termux.properties` file will allow users to scroll back ~10000 lines of command output. After updating the value, termux must be restarted. You can also run `termux-reload-settings` command so that termux loads the updated value, but only new sessions will use the updated value, existing sessions will not be affected.

You can test this with the following, where `70` is number of `x` characters per line and `10001` is the number of lines to print.
`x="$(printf 'x%.0s' {1..70})"; for i in {1..10001}; do echo "$i:$x"; done`

Be advised that using large values may have a performance impact depending on your device capabilities, so use at your own risk.

Closes #2071
2021-06-10 03:55:31 +05:00
agnostic-apollo
e7dd0eeebe Fix issue where soft keyboard overlaps extra keys or terminal in some cases
Check TermuxActivityRootView javadocs for details.
2021-06-10 03:06:10 +05:00
agnostic-apollo
7ef9255437 Remove hardcoded wiki.termux.com url from HelpActivity 2021-06-06 22:15:21 +05:00
agnostic-apollo
7225e2b379 Merge pull request #2114 from agnostic-apollo/fix-soft-keyboard-not-showing-in-some-case
Fix issue where soft keyboard would not show in some cases
2021-06-06 21:53:33 +05:00
agnostic-apollo
1ad038ece5 Fix issue where soft keyboard would not show in some cases
1. If `soft-keyboard-toggle-behaviour=enable/disable` was set, then pressing keyboard toggle wouldn't show the keyboard after switching back from another app if keyboard was previously disabled by user.
2. If switching back from another app, like when opening url with context menu "Select URL" long press and returning to termux with back button, then soft keyboard wouldn't automatically open like it does on app startup.

Also fixed issue where OnFocusChangeListener wasn't being set up if keyboard had to be hidden or disabled on startup.

Fixes #2111, Fixes #2112
2021-06-06 21:34:12 +05:00
agnostic-apollo
cb8b0225ca Add pluginIntent field to ExecutionCommand 2021-06-06 06:15:47 +05:00
Leonid Pliushch
7620800cd5 bump bootstraps again
Include latest changes to pkg & termux-change-repo
2021-06-04 19:17:19 +03:00
Leonid Pliushch
6837db0015 update bootstrap archives 2021-06-03 20:52:17 +03:00
agnostic-apollo
e08e3b536e Do not close soft keyboard when toolbar text input view is focused on
The TerminalToolbarViewPager EditText was requesting focus when it was selected. This called the TerminalView.onFocusChange() event with hasFocus=false, which closed the soft keyboard. Now soft keyboard will only be closed if both of them don't have focus.

Fixes #2077
2021-05-23 21:14:30 +05:00
28 changed files with 844 additions and 161 deletions

View File

@@ -26,8 +26,8 @@ android {
applicationId "com.termux"
minSdkVersion project.properties.minSdkVersion.toInteger()
targetSdkVersion project.properties.targetSdkVersion.toInteger()
versionCode 113
versionName "0.113"
versionCode 114
versionName "0.114"
manifestPlaceholders.TERMUX_PACKAGE_NAME = "com.termux"
manifestPlaceholders.TERMUX_APP_NAME = "Termux"
@@ -155,11 +155,11 @@ clean {
task downloadBootstraps() {
doLast {
def version = "2021.05.16-r1"
downloadBootstrap("aarch64", "6e340d8ab11d1225b89ee920e0884cbbd944d37765d81c5b06ef34579564fd9a", version)
downloadBootstrap("arm", "3f02bc2b5bd45c2ec5170527e39ee0413246698f11be4799c7bde6d364cfd780", version)
downloadBootstrap("i686", "36a3733fb2d8531d7f8abd989b711919872b9e8a79d7eb2e8b00bef467199187", version)
downloadBootstrap("x86_64", "3885376cc514220c0803e38f70b25f837854029fff2b7fda7a81452623cd9074", version)
def version = "2021.06.04-r1"
downloadBootstrap("aarch64", "115409ec8abe096080f9e6d9b2765e009c818b3e5aff0133792182009388b81e", version)
downloadBootstrap("arm", "c289688d4f9aabd86240a4d3e25bd89c1a96641e0d085d9ee5a471e9f019e16f", version)
downloadBootstrap("i686", "63cfb8e39cac20cecbe9e9890f49a30d7454d4c4dfa2b22b5d0cc6de1dc59726", version)
downloadBootstrap("x86_64", "db47007825e65f8ed15fc46d6b8e3119b01ca4a9856d7c7e5604cf804e753ba2", version)
}
}

View File

@@ -30,6 +30,7 @@ import android.widget.ListView;
import android.widget.Toast;
import com.termux.R;
import com.termux.app.terminal.TermuxActivityRootView;
import com.termux.shared.termux.TermuxConstants;
import com.termux.shared.termux.TermuxConstants.TERMUX_APP.TERMUX_ACTIVITY;
import com.termux.app.activities.HelpActivity;
@@ -68,7 +69,6 @@ import androidx.viewpager.widget.ViewPager;
*/
public final class TermuxActivity extends Activity implements ServiceConnection {
/**
* The connection to the {@link TermuxService}. Requested in {@link #onCreate(Bundle)} with a call to
* {@link #bindService(Intent, ServiceConnection, int)}, and obtained and stored in
@@ -77,7 +77,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
TermuxService mTermuxService;
/**
* The main view of the activity showing the terminal. Initialized in onCreate().
* The {@link TerminalView} shown in {@link TermuxActivity} that displays the terminal.
*/
TerminalView mTerminalView;
@@ -103,6 +103,16 @@ public final class TermuxActivity extends Activity implements ServiceConnection
*/
private TermuxAppSharedProperties mProperties;
/**
* The root view of the {@link TermuxActivity}.
*/
TermuxActivityRootView mTermuxActivityRootView;
/**
* The space at the bottom of {@link @mTermuxActivityRootView} of the {@link TermuxActivity}.
*/
View mTermuxActivityBottomSpaceView;
/**
* The terminal extra keys view.
*/
@@ -129,6 +139,11 @@ public final class TermuxActivity extends Activity implements ServiceConnection
*/
private boolean mIsVisible;
/**
* If onResume() was called after onCreate().
*/
private boolean isOnResumeAfterOnCreate = false;
/**
* The {@link TermuxActivity} is in an invalid state and must not be run.
*/
@@ -160,6 +175,7 @@ public final class TermuxActivity extends Activity implements ServiceConnection
public void onCreate(Bundle savedInstanceState) {
Logger.logDebug(LOG_TAG, "onCreate");
isOnResumeAfterOnCreate = true;
// Check if a crash happened on last run of the app and show a
// notification with the crash details if it did
@@ -183,6 +199,10 @@ public final class TermuxActivity extends Activity implements ServiceConnection
return;
}
mTermuxActivityRootView = findViewById(R.id.activity_termux_root_view);
mTermuxActivityRootView.setActivity(this);
mTermuxActivityBottomSpaceView = findViewById(R.id.activity_termux_bottom_space_view);
View content = findViewById(android.R.id.content);
content.setOnApplyWindowInsetsListener((v, insets) -> {
mNavBarHeight = insets.getSystemWindowInsetBottom();
@@ -235,6 +255,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection
if (mTermuxTerminalViewClient != null)
mTermuxTerminalViewClient.onStart();
addTermuxActivityRootViewGlobalLayoutListener();
registerTermuxActivityBroadcastReceiver();
}
@@ -251,6 +273,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection
if (mTermuxTerminalViewClient != null)
mTermuxTerminalViewClient.onResume();
isOnResumeAfterOnCreate = false;
}
@Override
@@ -269,6 +293,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection
if (mTermuxTerminalViewClient != null)
mTermuxTerminalViewClient.onStop();
removeTermuxActivityRootViewGlobalLayoutListener();
unregisterTermuxActivityBroadcastReceiever();
getDrawer().closeDrawers();
}
@@ -382,6 +408,17 @@ public final class TermuxActivity extends Activity implements ServiceConnection
public void addTermuxActivityRootViewGlobalLayoutListener() {
getTermuxActivityRootView().getViewTreeObserver().addOnGlobalLayoutListener(getTermuxActivityRootView());
}
public void removeTermuxActivityRootViewGlobalLayoutListener() {
if (getTermuxActivityRootView() != null)
getTermuxActivityRootView().getViewTreeObserver().removeOnGlobalLayoutListener(getTermuxActivityRootView());
}
private void setTermuxTerminalViewAndClients() {
// Set termux terminal view and session clients
mTermuxTerminalSessionClient = new TermuxTerminalSessionClient(this);
@@ -430,8 +467,8 @@ public final class TermuxActivity extends Activity implements ServiceConnection
if (terminalToolbarViewPager == null) return;
ViewGroup.LayoutParams layoutParams = terminalToolbarViewPager.getLayoutParams();
layoutParams.height = (int) Math.round(mTerminalToolbarDefaultHeight *
(mProperties.getExtraKeysInfo() == null ? 0 : mProperties.getExtraKeysInfo().getMatrix().length) *
mProperties.getTerminalToolbarHeightScaleFactor());
(mProperties.getExtraKeysInfo() == null ? 0 : mProperties.getExtraKeysInfo().getMatrix().length) *
mProperties.getTerminalToolbarHeightScaleFactor());
terminalToolbarViewPager.setLayoutParams(layoutParams);
}
@@ -671,6 +708,14 @@ public final class TermuxActivity extends Activity implements ServiceConnection
return mNavBarHeight;
}
public TermuxActivityRootView getTermuxActivityRootView() {
return mTermuxActivityRootView;
}
public View getTermuxActivityBottomSpaceView() {
return mTermuxActivityBottomSpaceView;
}
public ExtraKeysView getExtraKeysView() {
return mExtraKeysView;
}
@@ -691,6 +736,10 @@ public final class TermuxActivity extends Activity implements ServiceConnection
return mIsVisible;
}
public boolean isOnResumeAfterOnCreate() {
return isOnResumeAfterOnCreate;
}
public TermuxService getTermuxService() {
@@ -797,6 +846,9 @@ public final class TermuxActivity extends Activity implements ServiceConnection
if (mTermuxTerminalViewClient != null)
mTermuxTerminalViewClient.onReload();
if (mTermuxService != null)
mTermuxService.setTerminalTranscriptRows();
// To change the activity and drawer theme, activity needs to be recreated.
// But this will destroy the activity, and will call the onCreate() again.
// We need to investigate if enabling this is wise, since all stored variables and

View File

@@ -173,6 +173,7 @@ final class TermuxInstaller {
activity.finish();
}).setPositiveButton(R.string.bootstrap_error_try_again, (dialog, which) -> {
dialog.dismiss();
FileUtils.deleteFile(activity, "prefix directory", PREFIX_FILE_PATH, true);
TermuxInstaller.setupBootstrapIfNeeded(activity, whenDone);
}).show();
} catch (WindowManager.BadTokenException e1) {

View File

@@ -20,6 +20,7 @@ import android.provider.Settings;
import android.widget.ArrayAdapter;
import com.termux.R;
import com.termux.app.settings.properties.TermuxAppSharedProperties;
import com.termux.app.terminal.TermuxTerminalSessionClient;
import com.termux.app.utils.PluginUtils;
import com.termux.shared.termux.TermuxConstants;
@@ -106,6 +107,8 @@ public final class TermuxService extends Service implements TermuxTask.TermuxTas
/** If the user has executed the {@link TERMUX_SERVICE#ACTION_STOP_SERVICE} intent. */
boolean mWantsToStop = false;
public Integer mTerminalTranscriptRows;
private static final String LOG_TAG = "TermuxService";
@Override
@@ -503,6 +506,7 @@ public final class TermuxService extends Service implements TermuxTask.TermuxTas
// If the execution command was started for a plugin, only then will the stdout be set
// Otherwise if command was manually started by the user like by adding a new terminal session,
// then no need to set stdout
executionCommand.terminalTranscriptRows = getTerminalTranscriptRows();
TermuxSession newTermuxSession = TermuxSession.execute(this, executionCommand, getTermuxTerminalSessionClient(), this, sessionName, executionCommand.isPluginExecutionCommand);
if (newTermuxSession == null) {
Logger.logError(LOG_TAG, "Failed to execute new TermuxSession command for:\n" + executionCommand.getCommandIdAndLabelLogString());
@@ -560,6 +564,19 @@ public final class TermuxService extends Service implements TermuxTask.TermuxTas
updateNotification();
}
/** Get the terminal transcript rows to be used for new {@link TermuxSession}. */
public Integer getTerminalTranscriptRows() {
if (mTerminalTranscriptRows == null)
setTerminalTranscriptRows();
return mTerminalTranscriptRows;
}
public void setTerminalTranscriptRows() {
// TermuxService only uses this termux property currently, so no need to load them all into
// an internal values map like TermuxActivity does
mTerminalTranscriptRows = TermuxAppSharedProperties.getTerminalTranscriptRows(this);
}

View File

@@ -12,6 +12,8 @@ import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import com.termux.shared.termux.TermuxConstants;
/** Basic embedded browser for viewing help pages. */
public final class HelpActivity extends Activity {
@@ -39,7 +41,7 @@ public final class HelpActivity extends Activity {
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("https://wiki.termux.com")) {
if (url.equals(TermuxConstants.TERMUX_WIKI_URL) || url.startsWith(TermuxConstants.TERMUX_WIKI_URL + "/")) {
// Inline help.
setContentView(progressLayout);
return false;
@@ -60,7 +62,7 @@ public final class HelpActivity extends Activity {
setContentView(mWebView);
}
});
mWebView.loadUrl("https://wiki.termux.com/wiki/Main_Page");
mWebView.loadUrl(TermuxConstants.TERMUX_WIKI_URL);
}
@Override

View File

@@ -5,7 +5,6 @@ import android.content.Context;
import com.termux.app.terminal.io.KeyboardShortcut;
import com.termux.app.terminal.io.extrakeys.ExtraKeysInfo;
import com.termux.shared.logger.Logger;
import com.termux.shared.settings.properties.SharedPropertiesParser;
import com.termux.shared.settings.properties.TermuxPropertyConstants;
import com.termux.shared.settings.properties.TermuxSharedProperties;
@@ -17,7 +16,7 @@ import java.util.Map;
import javax.annotation.Nonnull;
public class TermuxAppSharedProperties extends TermuxSharedProperties implements SharedPropertiesParser {
public class TermuxAppSharedProperties extends TermuxSharedProperties {
private ExtraKeysInfo mExtraKeysInfo;
private List<KeyboardShortcut> mSessionShortcuts = new ArrayList<>();
@@ -96,4 +95,13 @@ public class TermuxAppSharedProperties extends TermuxSharedProperties implements
return mExtraKeysInfo;
}
/**
* Load the {@link TermuxPropertyConstants#KEY_TERMINAL_TRANSCRIPT_ROWS} value from termux properties file on disk.
*/
public static int getTerminalTranscriptRows(Context context) {
return (int) TermuxSharedProperties.getInternalPropertyValue(context, TermuxPropertyConstants.KEY_TERMINAL_TRANSCRIPT_ROWS);
}
}

View File

@@ -0,0 +1,227 @@
package com.termux.app.terminal;
import android.content.Context;
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.inputmethod.EditorInfo;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import com.termux.app.TermuxActivity;
import com.termux.shared.logger.Logger;
import com.termux.shared.view.ViewUtils;
/**
* The {@link TermuxActivity} relies on {@link android.view.WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE)}
* set by {@link TermuxTerminalViewClient#setSoftKeyboardState(boolean, boolean)} to automatically
* resize the view and push the terminal up when soft keyboard is opened. However, this does not
* always work properly. When `enforce-char-based-input=true` is set in `termux.properties`
* and {@link com.termux.view.TerminalView#onCreateInputConnection(EditorInfo)} sets the inputType
* to `InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS`
* instead of the default `InputType.TYPE_NULL` for termux, some keyboards may still show suggestions.
* Gboard does too, but only when text is copied and clipboard suggestions **and** number keys row
* toggles are enabled in its settings. When number keys row toggle is not enabled, Gboard will still
* show the row but will switch it with suggestions if needed. If its enabled, then number keys row
* is always shown and suggestions are shown in an additional row on top of it. This additional row is likely
* part of the candidates view returned by the keyboard app in {@link InputMethodService#onCreateCandidatesView()}.
*
* With the above configuration, the additional clipboard suggestions row partially covers the
* extra keys/terminal. Reopening the keyboard/activity does not fix the issue. This is either a bug
* in the Android OS where it does not consider the candidate's view height in its calculation to push
* up the view or because Gboard does not include the candidate's view height in the height reported
* to android that should be used, hence causing an overlap.
*
* Gboard logs the following entry to `logcat` when its opened with or without the suggestions bar showing:
* I/KeyboardViewUtil: KeyboardViewUtil.calculateMaxKeyboardBodyHeight():62 leave 500 height for app when screen height:2392, header height:176 and isFullscreenMode:false, so the max keyboard body height is:1716
* where `keyboard_height = screen_height - height_for_app - header_height` (62 is a hardcoded value in Gboard source code and may be a version number)
* So this may in fact be due to Gboard but https://stackoverflow.com/questions/57567272 suggests
* otherwise. Another similar report https://stackoverflow.com/questions/66761661.
* Also check https://github.com/termux/termux-app/issues/1539.
*
* This overlap may happen even without `enforce-char-based-input=true` for keyboards with extended layouts
* like number row, etc.
*
* To fix these issues, `activity_termux.xml` has the constant 1sp transparent
* `activity_termux_bottom_space_view` View at the bottom. This will appear as a line matching the
* activity theme. When {@link TermuxActivity} {@link ViewTreeObserver.OnGlobalLayoutListener} is
* called when any of the sub view layouts change, like keyboard opening/closing keyboard,
* extra keys/input view switched, etc, we check if the bottom space view is visible or not.
* If its not, then we add a margin to the bottom of the root view, so that the keyboard does not
* overlap the extra keys/terminal, since the margin will push up the view. By default the margin
* added is equal to the height of the hidden part of extra keys/terminal. For Gboard's case, the
* hidden part equals the `header_height`. The updates to margins may cause a jitter in some cases
* when the view is redrawn if the margin is incorrect, but logic has been implemented to avoid that.
*/
public class TermuxActivityRootView extends LinearLayout implements ViewTreeObserver.OnGlobalLayoutListener {
public TermuxActivity mActivity;
public Integer marginBottom;
public Integer lastMarginBottom;
/** Log root view events. */
private boolean ROOT_VIEW_LOGGING_ENABLED = false;
private static final String LOG_TAG = "TermuxActivityRootView";
public TermuxActivityRootView(Context context) {
super(context);
}
public TermuxActivityRootView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public TermuxActivityRootView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setActivity(TermuxActivity activity) {
mActivity = activity;
}
/**
* Sets whether root view logging is enabled or not.
*
* @param value The boolean value that defines the state.
*/
public void setIsRootViewLoggingEnabled(boolean value) {
ROOT_VIEW_LOGGING_ENABLED = value;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (marginBottom != null) {
if (ROOT_VIEW_LOGGING_ENABLED)
Logger.logVerbose(LOG_TAG, "onMeasure: Setting bottom margin to " + marginBottom);
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) getLayoutParams();
params.setMargins(0, 0, 0, marginBottom);
setLayoutParams(params);
marginBottom = null;
requestLayout();
}
}
@Override
public void onGlobalLayout() {
if (mActivity == null || !mActivity.isVisible()) return;
View bottomSpaceView = mActivity.getTermuxActivityBottomSpaceView();
if (bottomSpaceView == null) return;
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) getLayoutParams();
// Get the position Rects of the bottom space view and the main window holding it
Rect[] windowAndViewRects = ViewUtils.getWindowAndViewRects(bottomSpaceView);
if (windowAndViewRects == null)
return;
Rect windowAvailableRect = windowAndViewRects[0];
Rect bottomSpaceViewRect = windowAndViewRects[1];
// If the bottomSpaceViewRect is inside the windowAvailableRect, then it must be completely visible
boolean isVisible = windowAvailableRect.contains(bottomSpaceViewRect);
boolean isVisibleBecauseMargin = (windowAvailableRect.bottom == bottomSpaceViewRect.bottom) && params.bottomMargin > 0;
boolean isVisibleBecauseExtraMargin = (bottomSpaceViewRect.bottom - windowAvailableRect.bottom) < 0;
if (ROOT_VIEW_LOGGING_ENABLED)
Logger.logVerbose(LOG_TAG, "onGlobalLayout: windowAvailableRect " + windowAvailableRect.bottom + ", bottomSpaceViewRect " + bottomSpaceViewRect.bottom + ", diff " + (bottomSpaceViewRect.bottom - windowAvailableRect.bottom) + ", bottom " + params.bottomMargin + ", isVisible " + isVisible + ", isVisibleBecauseMargin " + isVisibleBecauseMargin + ", isVisibleBecauseExtraMargin " + isVisibleBecauseExtraMargin);
// If the bottomSpaceViewRect is visible, then remove the margin if needed
if (isVisible) {
// If visible because of margin, i.e the bottom of bottomSpaceViewRect equals that of windowAvailableRect
// and a margin has been added
// Necessary so that we don't get stuck in an infinite loop since setting margin
// will call OnGlobalLayoutListener again and next time bottom space view
// will be visible and margin will be set to 0, which again will call
// OnGlobalLayoutListener...
// Calling addTermuxActivityRootViewGlobalLayoutListener with a delay fails to
// set appropriate margins when views are changed quickly since some changes
// may be missed.
if (isVisibleBecauseMargin) {
if (ROOT_VIEW_LOGGING_ENABLED)
Logger.logVerbose(LOG_TAG, "onGlobalLayout: Visible due to margin");
// Once the view has been redrawn with new margin, we set margin back to 0 so that
// when next time onMeasure() is called, margin 0 is used. This is necessary for
// cases when view has been redrawn with new margin because bottom space view was
// hidden by keyboard and then view was redrawn again due to layout change (like
// keyboard symbol view is switched to), android will add margin below its new position
// if its greater than 0, which was already above the keyboard creating x2x margin.
marginBottom = 0;
return;
}
boolean setMargin = params.bottomMargin != 0;
// If visible because of extra margin, i.e the bottom of bottomSpaceViewRect is above that of windowAvailableRect
// onGlobalLayout: windowAvailableRect 1408, bottomSpaceViewRect 1232, diff -176, bottom 0, isVisible true, isVisibleBecauseMargin false, isVisibleBecauseExtraMargin false
// onGlobalLayout: Bottom margin already equals 0
if (isVisibleBecauseExtraMargin) {
if (ROOT_VIEW_LOGGING_ENABLED)
Logger.logVerbose(LOG_TAG, "onGlobalLayout: Resetting margin since visible due to extra margin");
setMargin = true;
// lastMarginBottom must be invalid. May also happen when keyboards are changed.
lastMarginBottom = null;
}
if (setMargin) {
if (ROOT_VIEW_LOGGING_ENABLED)
Logger.logVerbose(LOG_TAG, "onGlobalLayout: Setting bottom margin to 0");
params.setMargins(0, 0, 0, 0);
setLayoutParams(params);
} else {
if (ROOT_VIEW_LOGGING_ENABLED)
Logger.logVerbose(LOG_TAG, "onGlobalLayout: Bottom margin already equals 0");
// This is done so that when next time onMeasure() is called, lastMarginBottom is used.
// This is done since we **expect** the keyboard to have same dimensions next time layout
// changes, so best set margin while view is drawn the first time, otherwise it will
// cause a jitter when OnGlobalLayoutListener is called with margin 0 and it sets the
// likely same lastMarginBottom again and requesting a redraw. Hopefully, this logic
// works fine for all cases.
marginBottom = lastMarginBottom;
}
}
// ELse find the part of the extra keys/terminal that is hidden and add a margin accordingly
else {
int pxHidden = bottomSpaceViewRect.bottom - windowAvailableRect.bottom;
if (ROOT_VIEW_LOGGING_ENABLED)
Logger.logVerbose(LOG_TAG, "onGlobalLayout: pxHidden " + pxHidden + ", bottom " + params.bottomMargin);
boolean setMargin = params.bottomMargin != pxHidden;
// If invisible despite margin, i.e a margin was added, but the bottom of bottomSpaceViewRect
// is still below that of windowAvailableRect, this will trigger OnGlobalLayoutListener
// again, so that margins are set properly. May happen when toolbar/extra keys is disabled
// and enabled from left drawer, just like case for isVisibleBecauseExtraMargin.
// onMeasure: Setting bottom margin to 176
// onGlobalLayout: windowAvailableRect 1232, bottomSpaceViewRect 1408, diff 176, bottom 176, isVisible false, isVisibleBecauseMargin false, isVisibleBecauseExtraMargin false
// onGlobalLayout: Bottom margin already equals 176
if ((bottomSpaceViewRect.bottom - windowAvailableRect.bottom) > 0) {
if (ROOT_VIEW_LOGGING_ENABLED)
Logger.logVerbose(LOG_TAG, "onGlobalLayout: Force setting margin since not visible despite margin");
setMargin = true;
}
if (setMargin) {
if (ROOT_VIEW_LOGGING_ENABLED)
Logger.logVerbose(LOG_TAG, "onGlobalLayout: Setting bottom margin to " + pxHidden);
params.setMargins(0, 0, 0, pxHidden);
setLayoutParams(params);
lastMarginBottom = pxHidden;
} else {
if (ROOT_VIEW_LOGGING_ENABLED)
Logger.logVerbose(LOG_TAG, "onGlobalLayout: Bottom margin already equals " + pxHidden);
}
}
}
}

View File

@@ -207,6 +207,13 @@ public class TermuxTerminalSessionClient extends TermuxTerminalSessionClientBase
@Override
public Integer getTerminalCursorStyle() {
return mActivity.getProperties().getTerminalCursorStyle();
}
/** Initialize and get mBellSoundPool */
private synchronized SoundPool getBellSoundPool() {
if (mBellSoundPool == null) {
@@ -248,8 +255,10 @@ public class TermuxTerminalSessionClient extends TermuxTerminalSessionClientBase
void notifyOfSessionChange() {
if (!mActivity.isVisible()) return;
TerminalSession session = mActivity.getCurrentSession();
mActivity.showToast(toToastTitle(session), false);
if (!mActivity.getProperties().areTerminalSessionChangeToastsDisabled()) {
TerminalSession session = mActivity.getCurrentSession();
mActivity.showToast(toToastTitle(session), false);
}
}
public void switchToSession(boolean forward) {

View File

@@ -15,6 +15,7 @@ import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
@@ -56,6 +57,11 @@ public class TermuxTerminalViewClient extends TermuxTerminalViewClientBase {
private Runnable mShowSoftKeyboardRunnable;
private boolean mShowSoftKeyboardIgnoreOnce;
private boolean mShowSoftKeyboardWithDelayOnce;
private boolean mTerminalCursorBlinkerStateAlreadySet;
private static final String LOG_TAG = "TermuxTerminalViewClient";
public TermuxTerminalViewClient(TermuxActivity activity, TermuxTerminalSessionClient termuxTerminalSessionClient) {
@@ -75,10 +81,13 @@ public class TermuxTerminalViewClient extends TermuxTerminalViewClientBase {
* Should be called when mActivity.onStart() is called
*/
public void onStart() {
// Set {@link TerminalView#TERMINAL_VIEW_KEY_LOGGING_ENABLED} value
// Also required if user changed the preference from {@link TermuxSettings} activity and returns
mActivity.getTerminalView().setIsTerminalViewKeyLoggingEnabled(mActivity.getPreferences().isTerminalViewKeyLoggingEnabled());
boolean isTerminalViewKeyLoggingEnabled = mActivity.getPreferences().isTerminalViewKeyLoggingEnabled();
mActivity.getTerminalView().setIsTerminalViewKeyLoggingEnabled(isTerminalViewKeyLoggingEnabled);
// Piggyback on the terminal view key logging toggle for now, should add a separate toggle in future
mActivity.getTermuxActivityRootView().setIsRootViewLoggingEnabled(isTerminalViewKeyLoggingEnabled);
}
/**
@@ -88,8 +97,7 @@ public class TermuxTerminalViewClient extends TermuxTerminalViewClientBase {
// Show the soft keyboard if required
setSoftKeyboardState(true, false);
// Start terminal cursor blinking if enabled
setTerminalCursorBlinkerState(true);
mTerminalCursorBlinkerStateAlreadySet = false;
}
/**
@@ -111,6 +119,23 @@ public class TermuxTerminalViewClient extends TermuxTerminalViewClientBase {
setTerminalCursorBlinkerState(true);
}
/**
* Should be called when {@link com.termux.view.TerminalView#mEmulator}
*/
@Override
public void onEmulatorSet() {
if (!mTerminalCursorBlinkerStateAlreadySet) {
// Start terminal cursor blinking if enabled
// We need to wait for the first session to be attached that's set in
// TermuxActivity.onServiceConnected() and then the multiple calls to TerminalView.updateSize()
// where the final one eventually sets the mEmulator when width/height is not 0. Otherwise
// blinker will not start again if TermuxActivity is started again after exiting it with
// double back press. Check TerminalView.setTerminalCursorBlinkerState().
setTerminalCursorBlinkerState(true);
mTerminalCursorBlinkerStateAlreadySet = true;
}
}
@Override
@@ -409,10 +434,19 @@ public class TermuxTerminalViewClient extends TermuxTerminalViewClientBase {
mActivity.getPreferences().setSoftKeyboardEnabled(false);
KeyboardUtils.disableSoftKeyboard(mActivity, mActivity.getTerminalView());
} else {
// Show with a delay, otherwise pressing keyboard toggle won't show the keyboard after
// switching back from another app if keyboard was previously disabled by user.
// Also request focus, since it wouldn't have been requested at startup by
// setSoftKeyboardState if keyboard was disabled. #2112
Logger.logVerbose(LOG_TAG, "Enabling soft keyboard on toggle");
mActivity.getPreferences().setSoftKeyboardEnabled(true);
KeyboardUtils.clearDisableSoftKeyboardFlags(mActivity);
KeyboardUtils.showSoftKeyboard(mActivity, mActivity.getTerminalView());
if(mShowSoftKeyboardWithDelayOnce) {
mShowSoftKeyboardWithDelayOnce = false;
mActivity.getTerminalView().postDelayed(getShowSoftKeyboardRunnable(), 500);
mActivity.getTerminalView().requestFocus();
} else
KeyboardUtils.showSoftKeyboard(mActivity, mActivity.getTerminalView());
}
}
// If soft keyboard toggle behaviour is show/hide
@@ -430,15 +464,23 @@ public class TermuxTerminalViewClient extends TermuxTerminalViewClientBase {
}
public void setSoftKeyboardState(boolean isStartup, boolean isReloadTermuxProperties) {
boolean noRequestFocus = false;
// If soft keyboard is disabled by user for Termux (check function docs for Termux behaviour info)
if (KeyboardUtils.shouldSoftKeyboardBeDisabled(mActivity,
mActivity.getPreferences().isSoftKeyboardEnabled(),
mActivity.getPreferences().isSoftKeyboardEnabledOnlyIfNoHardware())) {
mActivity.getPreferences().isSoftKeyboardEnabled(),
mActivity.getPreferences().isSoftKeyboardEnabledOnlyIfNoHardware())) {
Logger.logVerbose(LOG_TAG, "Maintaining disabled soft keyboard");
KeyboardUtils.disableSoftKeyboard(mActivity, mActivity.getTerminalView());
noRequestFocus = true;
// Delay is only required if onCreate() is called like when Termux app is exited with
// double back press, not when Termux app is switched back from another app and keyboard
// toggle is pressed to enable keyboard
if (isStartup && mActivity.isOnResumeAfterOnCreate())
mShowSoftKeyboardWithDelayOnce = true;
} else {
// Set flag to automatically push up TerminalView when keyboard is opened instead of showing over it
KeyboardUtils.setResizeTerminalViewForSoftKeyboardFlags(mActivity);
KeyboardUtils.setSoftInputModeAdjustResize(mActivity);
// Clear any previous flags to disable soft keyboard in case setting updated
KeyboardUtils.clearDisableSoftKeyboardFlags(mActivity);
@@ -449,30 +491,55 @@ public class TermuxTerminalViewClient extends TermuxTerminalViewClientBase {
KeyboardUtils.hideSoftKeyboard(mActivity, mActivity.getTerminalView());
// Required to keep keyboard hidden when Termux app is switched back from another app
KeyboardUtils.setSoftKeyboardAlwaysHiddenFlags(mActivity);
} else {
// Do not force show soft keyboard if termux-reload-settings command was run with hardware keyboard
if (isReloadTermuxProperties)
return;
if (mShowSoftKeyboardRunnable == null) {
mShowSoftKeyboardRunnable = () -> {
Logger.logVerbose(LOG_TAG, "Showing soft keyboard on focus change");
KeyboardUtils.showSoftKeyboard(mActivity, mActivity.getTerminalView());
};
}
mActivity.getTerminalView().setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean hasFocus) {
// Force show soft keyboard if TerminalView has focus and close it if it doesn't
KeyboardUtils.setSoftKeyboardVisibility(mShowSoftKeyboardRunnable, mActivity, mActivity.getTerminalView(), hasFocus);
}
});
// Request focus for TerminalView
mActivity.getTerminalView().requestFocus();
noRequestFocus = true;
// Required to keep keyboard hidden on app startup
mShowSoftKeyboardIgnoreOnce = true;
}
}
mActivity.getTerminalView().setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean hasFocus) {
// Force show soft keyboard if TerminalView or toolbar text input view has
// focus and close it if they don't
boolean textInputViewHasFocus = false;
final EditText textInputView = mActivity.findViewById(R.id.terminal_toolbar_text_input);
if (textInputView != null) textInputViewHasFocus = textInputView.hasFocus();
if (hasFocus || textInputViewHasFocus) {
if (mShowSoftKeyboardIgnoreOnce) {
mShowSoftKeyboardIgnoreOnce = false; return;
}
Logger.logVerbose(LOG_TAG, "Showing soft keyboard on focus change");
} else {
Logger.logVerbose(LOG_TAG, "Hiding soft keyboard on focus change");
}
KeyboardUtils.setSoftKeyboardVisibility(getShowSoftKeyboardRunnable(), mActivity, mActivity.getTerminalView(), hasFocus || textInputViewHasFocus);
}
});
// Do not force show soft keyboard if termux-reload-settings command was run with hardware keyboard
// or soft keyboard is to be hidden or is disabled
if (!isReloadTermuxProperties && !noRequestFocus) {
// Request focus for TerminalView
// Also show the keyboard, since onFocusChange will not be called if TerminalView already
// had focus on startup to show the keyboard, like when opening url with context menu
// "Select URL" long press and returning to Termux app with back button. This
// will also show keyboard even if it was closed before opening url. #2111
Logger.logVerbose(LOG_TAG, "Requesting TerminalView focus and showing soft keyboard");
mActivity.getTerminalView().requestFocus();
mActivity.getTerminalView().postDelayed(getShowSoftKeyboardRunnable(), 300);
}
}
private Runnable getShowSoftKeyboardRunnable() {
if (mShowSoftKeyboardRunnable == null) {
mShowSoftKeyboardRunnable = () -> {
KeyboardUtils.showSoftKeyboard(mActivity, mActivity.getTerminalView());
};
}
return mShowSoftKeyboardRunnable;
}

View File

@@ -1,80 +1,96 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<com.termux.app.terminal.TermuxActivityRootView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_termux_root_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:fitsSystemWindows="true">
<androidx.drawerlayout.widget.DrawerLayout
android:id="@+id/drawer_layout"
<RelativeLayout
android:layout_width="match_parent"
android:layout_alignParentTop="true"
android:layout_above="@+id/terminal_toolbar_view_pager"
android:layout_height="match_parent">
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical">
<com.termux.view.TerminalView
android:id="@+id/terminal_view"
<androidx.drawerlayout.widget.DrawerLayout
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginRight="3dp"
android:layout_marginLeft="3dp"
android:focusableInTouchMode="true"
android:scrollbarThumbVertical="@drawable/terminal_scroll_shape"
android:scrollbars="vertical"
android:importantForAutofill="no"
android:autofillHints="password" />
android:layout_alignParentTop="true"
android:layout_above="@+id/terminal_toolbar_view_pager"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/left_drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="@android:color/white"
android:choiceMode="singleChoice"
android:divider="@android:color/transparent"
android:dividerHeight="0dp"
android:descendantFocusability="blocksDescendants"
android:orientation="vertical">
<ListView
android:id="@+id/terminal_sessions_list"
<com.termux.view.TerminalView
android:id="@+id/terminal_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="top"
android:layout_weight="1"
android:choiceMode="singleChoice"
android:longClickable="true" />
android:layout_height="match_parent"
android:layout_marginRight="3dp"
android:layout_marginLeft="3dp"
android:focusableInTouchMode="true"
android:scrollbarThumbVertical="@drawable/terminal_scroll_shape"
android:scrollbars="vertical"
android:importantForAutofill="no"
android:autofillHints="password" />
<LinearLayout
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:id="@+id/left_drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="@android:color/white"
android:choiceMode="singleChoice"
android:divider="@android:color/transparent"
android:dividerHeight="0dp"
android:descendantFocusability="blocksDescendants"
android:orientation="vertical">
<Button
android:id="@+id/toggle_keyboard_button"
style="?android:attr/buttonBarButtonStyle"
<ListView
android:id="@+id/terminal_sessions_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="top"
android:layout_weight="1"
android:choiceMode="singleChoice"
android:longClickable="true" />
<LinearLayout
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/action_toggle_soft_keyboard" />
android:orientation="horizontal">
<Button
android:id="@+id/new_session_button"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/action_new_session" />
<Button
android:id="@+id/toggle_keyboard_button"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/action_toggle_soft_keyboard" />
<Button
android:id="@+id/new_session_button"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/action_new_session" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.drawerlayout.widget.DrawerLayout>
</androidx.drawerlayout.widget.DrawerLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/terminal_toolbar_view_pager"
android:visibility="gone"
<androidx.viewpager.widget.ViewPager
android:id="@+id/terminal_toolbar_view_pager"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="37.5dp"
android:background="@android:drawable/screen_background_dark_transparent"
android:layout_alignParentBottom="true" />
</RelativeLayout>
<View
android:id="@+id/activity_termux_bottom_space_view"
android:layout_width="match_parent"
android:layout_height="37.5dp"
android:background="@android:drawable/screen_background_dark_transparent"
android:layout_alignParentBottom="true" />
</RelativeLayout>
android:layout_height="1dp"
android:background="@android:color/transparent" />
</com.termux.app.terminal.TermuxActivityRootView>

View File

@@ -63,7 +63,7 @@ publishing {
bar(MavenPublication) {
groupId 'com.termux'
artifactId 'terminal-emulator'
version "0.113"
version "0.114"
artifact(sourceJar)
artifact("$buildDir/outputs/aar/terminal-emulator-release.aar")
}

View File

@@ -38,10 +38,6 @@ public final class TerminalEmulator {
public static final int MOUSE_WHEELUP_BUTTON = 64;
public static final int MOUSE_WHEELDOWN_BUTTON = 65;
public static final int CURSOR_STYLE_BLOCK = 0;
public static final int CURSOR_STYLE_UNDERLINE = 1;
public static final int CURSOR_STYLE_BAR = 2;
/** Used for invalid data - http://en.wikipedia.org/wiki/Replacement_character#Replacement_character */
public static final int UNICODE_REPLACEMENT_CHAR = 0xFFFD;
@@ -126,17 +122,35 @@ public final class TerminalEmulator {
/** Not really DECSET bit... - http://www.vt100.net/docs/vt510-rm/DECSACE */
private static final int DECSET_BIT_RECTANGULAR_CHANGEATTRIBUTE = 1 << 12;
private String mTitle;
private final Stack<String> mTitleStack = new Stack<>();
/** The cursor position. Between (0,0) and (mRows-1, mColumns-1). */
private int mCursorRow, mCursorCol;
private int mCursorStyle = CURSOR_STYLE_BLOCK;
/** The number of character rows and columns in the terminal screen. */
public int mRows, mColumns;
/** The number of terminal transcript rows that can be scrolled back to. */
public static final int TERMINAL_TRANSCRIPT_ROWS_MIN = 100;
public static final int TERMINAL_TRANSCRIPT_ROWS_MAX = 50000;
public static final int DEFAULT_TERMINAL_TRANSCRIPT_ROWS = 2000;
/* The supported terminal cursor styles. */
public static final int TERMINAL_CURSOR_STYLE_BLOCK = 0;
public static final int TERMINAL_CURSOR_STYLE_UNDERLINE = 1;
public static final int TERMINAL_CURSOR_STYLE_BAR = 2;
public static final int DEFAULT_TERMINAL_CURSOR_STYLE = TERMINAL_CURSOR_STYLE_BLOCK;
public static final Integer[] TERMINAL_CURSOR_STYLES_LIST = new Integer[]{TERMINAL_CURSOR_STYLE_BLOCK, TERMINAL_CURSOR_STYLE_UNDERLINE, TERMINAL_CURSOR_STYLE_BAR};
/** The terminal cursor styles. */
private int mCursorStyle = DEFAULT_TERMINAL_CURSOR_STYLE;
/** The normal screen buffer. Stores the characters that appear on the screen of the emulated terminal. */
private final TerminalBuffer mMainBuffer;
/**
@@ -294,9 +308,9 @@ public final class TerminalEmulator {
}
}
public TerminalEmulator(TerminalOutput session, int columns, int rows, int transcriptRows, TerminalSessionClient client) {
public TerminalEmulator(TerminalOutput session, int columns, int rows, Integer transcriptRows, TerminalSessionClient client) {
mSession = session;
mScreen = mMainBuffer = new TerminalBuffer(columns, transcriptRows, rows);
mScreen = mMainBuffer = new TerminalBuffer(columns, getTerminalTranscriptRows(transcriptRows), rows);
mAltBuffer = new TerminalBuffer(columns, rows, rows);
mClient = client;
mRows = rows;
@@ -307,6 +321,8 @@ public final class TerminalEmulator {
public void updateTerminalSessionClient(TerminalSessionClient client) {
mClient = client;
setCursorStyle();
setCursorBlinkState(true);
}
public TerminalBuffer getScreen() {
@@ -317,6 +333,13 @@ public final class TerminalEmulator {
return mScreen == mAltBuffer;
}
private int getTerminalTranscriptRows(Integer transcriptRows) {
if (transcriptRows == null || transcriptRows < TERMINAL_TRANSCRIPT_ROWS_MIN || transcriptRows > TERMINAL_TRANSCRIPT_ROWS_MAX)
return DEFAULT_TERMINAL_TRANSCRIPT_ROWS;
else
return transcriptRows;
}
/**
* @param mouseButton one of the MOUSE_* constants of this class.
*/
@@ -384,11 +407,24 @@ public final class TerminalEmulator {
return mCursorCol;
}
/** {@link #CURSOR_STYLE_BAR}, {@link #CURSOR_STYLE_BLOCK} or {@link #CURSOR_STYLE_UNDERLINE} */
/** Get the terminal cursor style. It will be one of {@link #TERMINAL_CURSOR_STYLES_LIST} */
public int getCursorStyle() {
return mCursorStyle;
}
/** Set the terminal cursor style. */
public void setCursorStyle() {
Integer cursorStyle = null;
if (mClient != null)
cursorStyle = mClient.getTerminalCursorStyle();
if (cursorStyle == null || !Arrays.asList(TERMINAL_CURSOR_STYLES_LIST).contains(cursorStyle))
mCursorStyle = DEFAULT_TERMINAL_CURSOR_STYLE;
else
mCursorStyle = cursorStyle;
}
public boolean isReverseVideo() {
return isDecsetInternalBitSet(DECSET_BIT_REVERSE_VIDEO);
}
@@ -806,15 +842,15 @@ public final class TerminalEmulator {
case 0: // Blinking block.
case 1: // Blinking block.
case 2: // Steady block.
mCursorStyle = CURSOR_STYLE_BLOCK;
mCursorStyle = TERMINAL_CURSOR_STYLE_BLOCK;
break;
case 3: // Blinking underline.
case 4: // Steady underline.
mCursorStyle = CURSOR_STYLE_UNDERLINE;
mCursorStyle = TERMINAL_CURSOR_STYLE_UNDERLINE;
break;
case 5: // Blinking bar (xterm addition).
case 6: // Steady bar (xterm addition).
mCursorStyle = CURSOR_STYLE_BAR;
mCursorStyle = TERMINAL_CURSOR_STYLE_BAR;
break;
}
break;
@@ -2330,7 +2366,7 @@ public final class TerminalEmulator {
/** Reset terminal state so user can interact with it regardless of present state. */
public void reset() {
mCursorStyle = CURSOR_STYLE_BLOCK;
setCursorStyle();
mArgIndex = 0;
mContinueSequence = false;
mEscapeState = ESC_NONE;

View File

@@ -74,14 +74,17 @@ public final class TerminalSession extends TerminalOutput {
private final String mCwd;
private final String[] mArgs;
private final String[] mEnv;
private final Integer mTranscriptRows;
private static final String LOG_TAG = "TerminalSession";
public TerminalSession(String shellPath, String cwd, String[] args, String[] env, TerminalSessionClient client) {
public TerminalSession(String shellPath, String cwd, String[] args, String[] env, Integer transcriptRows, TerminalSessionClient client) {
this.mShellPath = shellPath;
this.mCwd = cwd;
this.mArgs = args;
this.mEnv = env;
this.mTranscriptRows = transcriptRows;
this.mClient = client;
}
@@ -118,7 +121,7 @@ public final class TerminalSession extends TerminalOutput {
* @param rows The number of rows in the terminal window.
*/
public void initializeEmulator(int columns, int rows) {
mEmulator = new TerminalEmulator(this, columns, rows, /* transcript= */2000, mClient);
mEmulator = new TerminalEmulator(this, columns, rows, mTranscriptRows, mClient);
int[] processId = new int[1];
mTerminalFileDescriptor = JNI.createSubprocess(mShellPath, mCwd, mArgs, mEnv, processId, rows, columns);

View File

@@ -22,6 +22,11 @@ public interface TerminalSessionClient {
void onTerminalCursorStateChange(boolean state);
Integer getTerminalCursorStyle();
void logError(String tag, String message);
void logWarn(String tag, String message);

View File

@@ -103,23 +103,23 @@ public class TerminalTest extends TerminalTestCase {
/** Test the cursor shape changes using DECSCUSR. */
public void testSetCursorStyle() throws Exception {
withTerminalSized(5, 5);
assertEquals(TerminalEmulator.CURSOR_STYLE_BLOCK, mTerminal.getCursorStyle());
assertEquals(TerminalEmulator.TERMINAL_CURSOR_STYLE_BLOCK, mTerminal.getCursorStyle());
enterString("\033[3 q");
assertEquals(TerminalEmulator.CURSOR_STYLE_UNDERLINE, mTerminal.getCursorStyle());
assertEquals(TerminalEmulator.TERMINAL_CURSOR_STYLE_UNDERLINE, mTerminal.getCursorStyle());
enterString("\033[5 q");
assertEquals(TerminalEmulator.CURSOR_STYLE_BAR, mTerminal.getCursorStyle());
assertEquals(TerminalEmulator.TERMINAL_CURSOR_STYLE_BAR, mTerminal.getCursorStyle());
enterString("\033[0 q");
assertEquals(TerminalEmulator.CURSOR_STYLE_BLOCK, mTerminal.getCursorStyle());
assertEquals(TerminalEmulator.TERMINAL_CURSOR_STYLE_BLOCK, mTerminal.getCursorStyle());
enterString("\033[6 q");
assertEquals(TerminalEmulator.CURSOR_STYLE_BAR, mTerminal.getCursorStyle());
assertEquals(TerminalEmulator.TERMINAL_CURSOR_STYLE_BAR, mTerminal.getCursorStyle());
enterString("\033[4 q");
assertEquals(TerminalEmulator.CURSOR_STYLE_UNDERLINE, mTerminal.getCursorStyle());
assertEquals(TerminalEmulator.TERMINAL_CURSOR_STYLE_UNDERLINE, mTerminal.getCursorStyle());
enterString("\033[1 q");
assertEquals(TerminalEmulator.CURSOR_STYLE_BLOCK, mTerminal.getCursorStyle());
assertEquals(TerminalEmulator.TERMINAL_CURSOR_STYLE_BLOCK, mTerminal.getCursorStyle());
enterString("\033[4 q");
assertEquals(TerminalEmulator.CURSOR_STYLE_UNDERLINE, mTerminal.getCursorStyle());
assertEquals(TerminalEmulator.TERMINAL_CURSOR_STYLE_UNDERLINE, mTerminal.getCursorStyle());
enterString("\033[2 q");
assertEquals(TerminalEmulator.CURSOR_STYLE_BLOCK, mTerminal.getCursorStyle());
assertEquals(TerminalEmulator.TERMINAL_CURSOR_STYLE_BLOCK, mTerminal.getCursorStyle());
}
public void testPaste() {

View File

@@ -42,7 +42,7 @@ publishing {
bar(MavenPublication) {
groupId 'com.termux'
artifactId 'terminal-view'
version "0.113"
version "0.114"
artifact(sourceJar)
artifact("$buildDir/outputs/aar/terminal-view-release.aar")
}

View File

@@ -200,8 +200,8 @@ public final class TerminalRenderer {
if (cursor != 0) {
mTextPaint.setColor(cursor);
float cursorHeight = mFontLineSpacingAndAscent - mFontAscent;
if (cursorStyle == TerminalEmulator.CURSOR_STYLE_UNDERLINE) cursorHeight /= 4.;
else if (cursorStyle == TerminalEmulator.CURSOR_STYLE_BAR) right -= ((right - left) * 3) / 4.;
if (cursorStyle == TerminalEmulator.TERMINAL_CURSOR_STYLE_UNDERLINE) cursorHeight /= 4.;
else if (cursorStyle == TerminalEmulator.TERMINAL_CURSOR_STYLE_BAR) right -= ((right - left) * 3) / 4.;
canvas.drawRect(left, y - cursorHeight, right, y, mTextPaint);
}

View File

@@ -755,6 +755,7 @@ public final class TerminalView extends View {
if (mEmulator == null || (newColumns != mEmulator.mColumns || newRows != mEmulator.mRows)) {
mTermSession.updateSize(newColumns, newRows);
mEmulator = mTermSession.getEmulator();
mClient.onEmulatorSet();
mTopRow = 0;
scrollTo(0, 0);
@@ -880,7 +881,13 @@ public final class TerminalView extends View {
* {@link #TERMINAL_CURSOR_BLINK_RATE_MIN} and {@link #TERMINAL_CURSOR_BLINK_RATE_MAX}.
*
* This should be called when the view holding this activity is resumed or stopped so that
* cursor blinker does not run when activity is not visible.
* cursor blinker does not run when activity is not visible. Ensure that {@link #mEmulator}
* is set when you call this to start cursor blinking by waiting for {@link TerminalViewClient#onEmulatorSet()}
* event after calling {@link #attachSession(TerminalSession)} for the first session added in the
* activity, otherwise blinking will not start. Do not call this directly after
* {@link #attachSession(TerminalSession)} since {@link #updateSize()} may return without
* setting {@link #mEmulator} since width/height may be 0. Its called again in
* {@link #onSizeChanged(int, int, int, int)}.
*
* It should also be called on the
* {@link com.termux.terminal.TerminalSessionClient#onTerminalCursorStateChange(boolean)}

View File

@@ -56,6 +56,8 @@ public interface TerminalViewClient {
boolean onCodePoint(int codePoint, boolean ctrlDown, TerminalSession session);
void onEmulatorSet();
void logError(String tag, String message);

View File

@@ -57,7 +57,7 @@ publishing {
bar(MavenPublication) {
groupId 'com.termux'
artifactId 'termux-shared'
version "0.113"
version "0.114"
artifact(sourceJar)
artifact("$buildDir/outputs/aar/termux-shared-release.aar")
}

View File

@@ -2,6 +2,7 @@ package com.termux.shared.models;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.net.Uri;
import androidx.annotation.NonNull;
@@ -80,6 +81,10 @@ public class ExecutionCommand {
public String workingDirectory;
/** The terminal transcript rows for the {@link ExecutionCommand}. */
public Integer terminalTranscriptRows;
/** If the {@link ExecutionCommand} is a background or a foreground terminal session command. */
public boolean inBackground;
/** If the {@link ExecutionCommand} is meant to start a failsafe terminal session. */
@@ -106,9 +111,10 @@ public class ExecutionCommand {
/** Defines if {@link ExecutionCommand} was started because of an external plugin request
* like {@link TERMUX_SERVICE#ACTION_SERVICE_EXECUTE} intent or from within Termux app itself.
*/
* like {@link TERMUX_SERVICE#ACTION_SERVICE_EXECUTE} intent or from within Termux app itself. */
public boolean isPluginExecutionCommand;
/** Defines the {@link Intent} received from the external plugin which started the {@link ExecutionCommand}. */
public Intent pluginIntent;
/** Defines {@link PendingIntent} that should be sent if an external plugin requested the execution. */
public PendingIntent pluginPendingIntent;

View File

@@ -3,6 +3,7 @@ package com.termux.shared.settings.properties;
import com.google.common.collect.ImmutableBiMap;
import com.termux.shared.termux.TermuxConstants;
import com.termux.shared.logger.Logger;
import com.termux.terminal.TerminalEmulator;
import com.termux.view.TerminalView;
import java.io.File;
@@ -11,7 +12,7 @@ import java.util.HashSet;
import java.util.Set;
/*
* Version: v0.10.0
* Version: v0.12.0
*
* Changelog
*
@@ -49,6 +50,11 @@ import java.util.Set;
* - 0.10.0 (2021-05-15)
* - Add `MAP_BACK_KEY_BEHAVIOUR`, `MAP_SOFT_KEYBOARD_TOGGLE_BEHAVIOUR`, `MAP_VOLUME_KEYS_BEHAVIOUR`.
*
* - 0.11.0 (2021-06-10)
* - Add `*KEY_TERMINAL_TRANSCRIPT_ROWS*`.
*
* - 0.12.0 (2021-06-10)
* - Add `*KEY_TERMINAL_CURSOR_STYLE*`.
*/
/**
@@ -66,6 +72,11 @@ public final class TermuxPropertyConstants {
/* boolean */
/** Defines the key for whether a toast will be shown when user changes the terminal session */
public static final String KEY_DISABLE_TERMINAL_SESSION_CHANGE_TOAST = "disable-terminal-session-change-toast"; // Default: "disable-terminal-session-change-toast"
/** Defines the key for whether to enforce character based input to fix the issue where for some devices like Samsung, the letters might not appear until enter is pressed */
public static final String KEY_ENFORCE_CHAR_BASED_INPUT = "enforce-char-based-input"; // Default: "enforce-char-based-input"
@@ -131,6 +142,36 @@ public final class TermuxPropertyConstants {
/** Defines the key for the terminal cursor style */
public static final String KEY_TERMINAL_CURSOR_STYLE = "terminal-cursor-style"; // Default: "terminal-cursor-style"
public static final String VALUE_TERMINAL_CURSOR_STYLE_BLOCK = "block";
public static final String VALUE_TERMINAL_CURSOR_STYLE_UNDERLINE = "underline";
public static final String VALUE_TERMINAL_CURSOR_STYLE_BAR = "bar";
public static final int IVALUE_TERMINAL_CURSOR_STYLE_BLOCK = TerminalEmulator.TERMINAL_CURSOR_STYLE_BLOCK;
public static final int IVALUE_TERMINAL_CURSOR_STYLE_UNDERLINE = TerminalEmulator.TERMINAL_CURSOR_STYLE_UNDERLINE;
public static final int IVALUE_TERMINAL_CURSOR_STYLE_BAR = TerminalEmulator.TERMINAL_CURSOR_STYLE_BAR;
public static final int DEFAULT_IVALUE_TERMINAL_CURSOR_STYLE = TerminalEmulator.DEFAULT_TERMINAL_CURSOR_STYLE;
/** Defines the bidirectional map for terminal cursor styles and their internal values */
public static final ImmutableBiMap<String, Integer> MAP_TERMINAL_CURSOR_STYLE =
new ImmutableBiMap.Builder<String, Integer>()
.put(VALUE_TERMINAL_CURSOR_STYLE_BLOCK, IVALUE_TERMINAL_CURSOR_STYLE_BLOCK)
.put(VALUE_TERMINAL_CURSOR_STYLE_UNDERLINE, IVALUE_TERMINAL_CURSOR_STYLE_UNDERLINE)
.put(VALUE_TERMINAL_CURSOR_STYLE_BAR, IVALUE_TERMINAL_CURSOR_STYLE_BAR)
.build();
/** Defines the key for the terminal transcript rows */
public static final String KEY_TERMINAL_TRANSCRIPT_ROWS = "terminal-transcript-rows"; // Default: "terminal-transcript-rows"
public static final int IVALUE_TERMINAL_TRANSCRIPT_ROWS_MIN = TerminalEmulator.TERMINAL_TRANSCRIPT_ROWS_MIN;
public static final int IVALUE_TERMINAL_TRANSCRIPT_ROWS_MAX = TerminalEmulator.TERMINAL_TRANSCRIPT_ROWS_MAX;
public static final int DEFAULT_IVALUE_TERMINAL_TRANSCRIPT_ROWS = TerminalEmulator.DEFAULT_TERMINAL_TRANSCRIPT_ROWS;
/* float */
@@ -201,7 +242,8 @@ public final class TermuxPropertyConstants {
/** Defines the key for extra keys */
public static final String KEY_EXTRA_KEYS = "extra-keys"; // Default: "extra-keys"
public static final String DEFAULT_IVALUE_EXTRA_KEYS = "[[ESC, TAB, CTRL, ALT, {key: '-', popup: '|'}, DOWN, UP]]";
//public static final String DEFAULT_IVALUE_EXTRA_KEYS = "[[ESC, TAB, CTRL, ALT, {key: '-', popup: '|'}, DOWN, UP]]"; // Single row
public static final String DEFAULT_IVALUE_EXTRA_KEYS = "[['ESC','/',{key: '-', popup: '|'},'HOME','UP','END','PGUP'], ['TAB','CTRL','ALT','LEFT','DOWN','RIGHT','PGDN']]"; // Double row
/** Defines the key for extra keys style */
public static final String KEY_EXTRA_KEYS_STYLE = "extra-keys-style"; // Default: "extra-keys-style"
@@ -248,6 +290,7 @@ public final class TermuxPropertyConstants {
* */
public static final Set<String> TERMUX_PROPERTIES_LIST = new HashSet<>(Arrays.asList(
/* boolean */
KEY_DISABLE_TERMINAL_SESSION_CHANGE_TOAST,
KEY_ENFORCE_CHAR_BASED_INPUT,
KEY_HIDE_SOFT_KEYBOARD_ON_STARTUP,
KEY_USE_BLACK_UI,
@@ -259,6 +302,8 @@ public final class TermuxPropertyConstants {
/* int */
KEY_BELL_BEHAVIOUR,
KEY_TERMINAL_CURSOR_BLINK_RATE,
KEY_TERMINAL_CURSOR_STYLE,
KEY_TERMINAL_TRANSCRIPT_ROWS,
/* float */
KEY_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR,
@@ -284,6 +329,7 @@ public final class TermuxPropertyConstants {
* default: false
* */
public static final Set<String> TERMUX_DEFAULT_BOOLEAN_BEHAVIOUR_PROPERTIES_LIST = new HashSet<>(Arrays.asList(
KEY_DISABLE_TERMINAL_SESSION_CHANGE_TOAST,
KEY_ENFORCE_CHAR_BASED_INPUT,
KEY_HIDE_SOFT_KEYBOARD_ON_STARTUP,
KEY_USE_CTRL_SPACE_WORKAROUND,

View File

@@ -13,7 +13,7 @@ import java.util.Properties;
import javax.annotation.Nonnull;
public class TermuxSharedProperties implements SharedPropertiesParser {
public class TermuxSharedProperties {
protected final Context mContext;
protected final SharedProperties mSharedProperties;
@@ -24,7 +24,7 @@ public class TermuxSharedProperties implements SharedPropertiesParser {
public TermuxSharedProperties(@Nonnull Context context) {
mContext = context;
mPropertiesFile = TermuxPropertyConstants.getTermuxPropertiesFile();
mSharedProperties = new SharedProperties(context, mPropertiesFile, TermuxPropertyConstants.TERMUX_PROPERTIES_LIST, this);
mSharedProperties = new SharedProperties(context, mPropertiesFile, TermuxPropertyConstants.TERMUX_PROPERTIES_LIST, new SharedPropertiesParserClient());
loadTermuxPropertiesFromDisk();
}
@@ -146,24 +146,47 @@ public class TermuxSharedProperties implements SharedPropertiesParser {
// {@link #loadTermuxPropertiesFromDisk()} call
// A null value can still be returned by
// {@link #getInternalPropertyValueFromValue(Context,String,String)} for some keys
value = getInternalPropertyValueFromValue(mContext, key, null);
value = getInternalTermuxPropertyValueFromValue(mContext, key, null);
Logger.logWarn(LOG_TAG, "The value for \"" + key + "\" not found in SharedProperties cache, force returning default value: `" + value + "`");
return value;
}
} else {
// We get the property value directly from file and return its internal value
return getInternalPropertyValueFromValue(mContext, key, mSharedProperties.getProperty(key, false));
return getInternalTermuxPropertyValueFromValue(mContext, key, mSharedProperties.getProperty(key, false));
}
}
/**
* Override the
* {@link SharedPropertiesParser#getInternalPropertyValueFromValue(Context,String,String)}
* interface function.
* Get the internal {@link Object} value for the key passed from the file returned by
* {@link TermuxPropertyConstants#getTermuxPropertiesFile()}. The {@link Properties} object is
* read directly from the file and internal value is returned for the property value against the key.
*
* @param context The context for operations.
* @param key The key for which the internal object is required.
* @return Returns the {@link Object} object. This will be {@code null} if key is not found or
* the object stored against the key is {@code null}.
*/
@Override
public Object getInternalPropertyValueFromValue(Context context, String key, String value) {
return getInternalTermuxPropertyValueFromValue(context, key, value);
public static Object getInternalPropertyValue(Context context, String key) {
return SharedProperties.getInternalProperty(context, TermuxPropertyConstants.getTermuxPropertiesFile(), key, new SharedPropertiesParserClient());
}
/**
* The class that implements the {@link SharedPropertiesParser} interface.
*/
public static class SharedPropertiesParserClient implements SharedPropertiesParser {
/**
* Override the
* {@link SharedPropertiesParser#getInternalPropertyValueFromValue(Context,String,String)}
* interface function.
*/
@Override
public Object getInternalPropertyValueFromValue(Context context, String key, String value) {
return getInternalTermuxPropertyValueFromValue(context, key, value);
}
}
/**
@@ -194,6 +217,10 @@ public class TermuxSharedProperties implements SharedPropertiesParser {
return (int) getBellBehaviourInternalPropertyValueFromValue(value);
case TermuxPropertyConstants.KEY_TERMINAL_CURSOR_BLINK_RATE:
return (int) getTerminalCursorBlinkRateInternalPropertyValueFromValue(value);
case TermuxPropertyConstants.KEY_TERMINAL_CURSOR_STYLE:
return (int) getTerminalCursorStyleInternalPropertyValueFromValue(value);
case TermuxPropertyConstants.KEY_TERMINAL_TRANSCRIPT_ROWS:
return (int) getTerminalTranscriptRowsInternalPropertyValueFromValue(value);
/* float */
case TermuxPropertyConstants.KEY_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR:
@@ -252,7 +279,7 @@ public class TermuxSharedProperties implements SharedPropertiesParser {
/**
* Returns the internal value after mapping it based on
* {@code TermuxPropertyConstants#MAP_BELL_BEHAVIOUR} if the value is not {@code null}
* and is valid, otherwise returns {@code TermuxPropertyConstants#DEFAULT_IVALUE_BELL_BEHAVIOUR}.
* and is valid, otherwise returns {@link TermuxPropertyConstants#DEFAULT_IVALUE_BELL_BEHAVIOUR}.
*
* @param value The {@link String} value to convert.
* @return Returns the internal value for value.
@@ -263,14 +290,14 @@ public class TermuxSharedProperties implements SharedPropertiesParser {
/**
* Returns the int for the value if its not null and is between
* {@code TermuxPropertyConstants#IVALUE_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR_MIN} and
* {@code TermuxPropertyConstants#IVALUE_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR_MAX},
* otherwise returns {@code TermuxPropertyConstants#DEFAULT_IVALUE_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR}.
* {@link TermuxPropertyConstants#IVALUE_TERMINAL_CURSOR_BLINK_RATE_MIN} and
* {@link TermuxPropertyConstants#IVALUE_TERMINAL_CURSOR_BLINK_RATE_MAX},
* otherwise returns {@link TermuxPropertyConstants#DEFAULT_IVALUE_TERMINAL_CURSOR_BLINK_RATE}.
*
* @param value The {@link String} value to convert.
* @return Returns the internal value for value.
*/
public static float getTerminalCursorBlinkRateInternalPropertyValueFromValue(String value) {
public static int getTerminalCursorBlinkRateInternalPropertyValueFromValue(String value) {
return SharedProperties.getDefaultIfNotInRange(TermuxPropertyConstants.KEY_TERMINAL_CURSOR_BLINK_RATE,
DataUtils.getIntFromString(value, TermuxPropertyConstants.DEFAULT_IVALUE_TERMINAL_CURSOR_BLINK_RATE),
TermuxPropertyConstants.DEFAULT_IVALUE_TERMINAL_CURSOR_BLINK_RATE,
@@ -279,11 +306,41 @@ public class TermuxSharedProperties implements SharedPropertiesParser {
true, true, LOG_TAG);
}
/**
* Returns the internal value after mapping it based on
* {@link TermuxPropertyConstants#MAP_TERMINAL_CURSOR_STYLE} if the value is not {@code null}
* and is valid, otherwise returns {@link TermuxPropertyConstants#DEFAULT_IVALUE_TERMINAL_CURSOR_STYLE}.
*
* @param value The {@link String} value to convert.
* @return Returns the internal value for value.
*/
public static int getTerminalCursorStyleInternalPropertyValueFromValue(String value) {
return (int) SharedProperties.getDefaultIfNotInMap(TermuxPropertyConstants.KEY_TERMINAL_CURSOR_STYLE, TermuxPropertyConstants.MAP_TERMINAL_CURSOR_STYLE, SharedProperties.toLowerCase(value), TermuxPropertyConstants.DEFAULT_IVALUE_TERMINAL_CURSOR_STYLE, true, LOG_TAG);
}
/**
* Returns the int for the value if its not null and is between
* {@code TermuxPropertyConstants#IVALUE_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR_MIN} and
* {@code TermuxPropertyConstants#IVALUE_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR_MAX},
* otherwise returns {@code TermuxPropertyConstants#DEFAULT_IVALUE_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR}.
* {@link TermuxPropertyConstants#IVALUE_TERMINAL_TRANSCRIPT_ROWS_MIN} and
* {@link TermuxPropertyConstants#IVALUE_TERMINAL_TRANSCRIPT_ROWS_MAX},
* otherwise returns {@link TermuxPropertyConstants#DEFAULT_IVALUE_TERMINAL_TRANSCRIPT_ROWS}.
*
* @param value The {@link String} value to convert.
* @return Returns the internal value for value.
*/
public static int getTerminalTranscriptRowsInternalPropertyValueFromValue(String value) {
return SharedProperties.getDefaultIfNotInRange(TermuxPropertyConstants.KEY_TERMINAL_TRANSCRIPT_ROWS,
DataUtils.getIntFromString(value, TermuxPropertyConstants.DEFAULT_IVALUE_TERMINAL_TRANSCRIPT_ROWS),
TermuxPropertyConstants.DEFAULT_IVALUE_TERMINAL_TRANSCRIPT_ROWS,
TermuxPropertyConstants.IVALUE_TERMINAL_TRANSCRIPT_ROWS_MIN,
TermuxPropertyConstants.IVALUE_TERMINAL_TRANSCRIPT_ROWS_MAX,
true, true, LOG_TAG);
}
/**
* Returns the int for the value if its not null and is between
* {@link TermuxPropertyConstants#IVALUE_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR_MIN} and
* {@link TermuxPropertyConstants#IVALUE_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR_MAX},
* otherwise returns {@link TermuxPropertyConstants#DEFAULT_IVALUE_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR}.
*
* @param value The {@link String} value to convert.
* @return Returns the internal value for value.
@@ -403,6 +460,10 @@ public class TermuxSharedProperties implements SharedPropertiesParser {
public boolean areTerminalSessionChangeToastsDisabled() {
return (boolean) getInternalPropertyValue(TermuxPropertyConstants.KEY_DISABLE_TERMINAL_SESSION_CHANGE_TOAST, true);
}
public boolean isEnforcingCharBasedInput() {
return (boolean) getInternalPropertyValue(TermuxPropertyConstants.KEY_ENFORCE_CHAR_BASED_INPUT, true);
}
@@ -435,6 +496,14 @@ public class TermuxSharedProperties implements SharedPropertiesParser {
return (int) getInternalPropertyValue(TermuxPropertyConstants.KEY_TERMINAL_CURSOR_BLINK_RATE, true);
}
public int getTerminalCursorStyle() {
return (int) getInternalPropertyValue(TermuxPropertyConstants.KEY_TERMINAL_CURSOR_STYLE, true);
}
public int getTerminalTranscriptRows() {
return (int) getInternalPropertyValue(TermuxPropertyConstants.KEY_TERMINAL_TRANSCRIPT_ROWS, true);
}
public float getTerminalToolbarHeightScaleFactor() {
return (float) getInternalPropertyValue(TermuxPropertyConstants.KEY_TERMINAL_TOOLBAR_HEIGHT_SCALE_FACTOR, true);
}

View File

@@ -109,7 +109,7 @@ public class TermuxSession {
Logger.logDebug(LOG_TAG, executionCommand.toString());
Logger.logDebug(LOG_TAG, "Running \"" + executionCommand.getCommandIdAndLabelLogString() + "\" TermuxSession");
TerminalSession terminalSession = new TerminalSession(executionCommand.executable, executionCommand.workingDirectory, executionCommand.arguments, environment, terminalSessionClient);
TerminalSession terminalSession = new TerminalSession(executionCommand.executable, executionCommand.workingDirectory, executionCommand.arguments, environment, executionCommand.terminalTranscriptRows, terminalSessionClient);
if (sessionName != null) {
terminalSession.mSessionName = sessionName;

View File

@@ -39,6 +39,13 @@ public class TermuxTerminalSessionClientBase implements TerminalSessionClient {
@Override
public Integer getTerminalCursorStyle() {
return null;
}
@Override
public void logError(String tag, String message) {
Logger.logError(tag, message);

View File

@@ -67,6 +67,11 @@ public class TermuxTerminalViewClientBase implements TerminalViewClient {
return false;
}
@Override
public void onEmulatorSet() {
}
@Override
public void logError(String tag, String message) {
Logger.logError(tag, message);

View File

@@ -102,7 +102,7 @@ public class KeyboardUtils {
activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
}
public static void setResizeTerminalViewForSoftKeyboardFlags(final Activity activity) {
public static void setSoftInputModeAdjustResize(final Activity activity) {
// TODO: The flag is deprecated for API 30 and WindowInset API should be used
// https://developer.android.com/reference/android/view/WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE
// https://medium.com/androiddevelopers/animating-your-keyboard-fb776a8fb66d

View File

@@ -0,0 +1,98 @@
package com.termux.shared.view;
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.graphics.Rect;
import android.util.TypedValue;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
public class ViewUtils {
/**
* Check if a {@link View} is fully visible and not hidden or partially covered by another view.
*
* https://stackoverflow.com/a/51078418/14686958
*
* @param view The {@link View} to check.
* @return Returns {@code true} if view is fully visible.
*/
public static boolean isViewFullyVisible(View view) {
Rect[] windowAndViewRects = getWindowAndViewRects(view);
if (windowAndViewRects == null)
return false;
return windowAndViewRects[0].contains(windowAndViewRects[1]);
}
/**
* Get the {@link Rect} of a {@link View} and the {@link Rect} of the window inside which it
* exists.
*
* https://stackoverflow.com/a/51078418/14686958
*
* @param view The {@link View} inside the window whose {@link Rect} to get.
* @return Returns {@link Rect[]} if view is visible where Rect[0] will contain window
* {@link Rect} and Rect[1] will contain view {@link Rect}. This will be {@code null}
* if view is not visible.
*/
@Nullable
public static Rect[] getWindowAndViewRects(View view) {
if (view == null || !view.isShown())
return null;
// windowRect - will hold available area where content remain visible to users
// Takes into account screen decorations (e.g. statusbar)
Rect windowRect = new Rect();
view.getWindowVisibleDisplayFrame(windowRect);
// If there is actionbar, get his height
int actionBarHeight = 0;
Context context = view.getContext();
if (context instanceof AppCompatActivity) {
androidx.appcompat.app.ActionBar actionBar = ((AppCompatActivity) context).getSupportActionBar();
if (actionBar != null) actionBarHeight = actionBar.getHeight();
} else if (context instanceof Activity) {
android.app.ActionBar actionBar = ((Activity) context).getActionBar();
if (actionBar != null) actionBarHeight = actionBar.getHeight();
}
// windowAvailableRect - takes into account actionbar and statusbar height
Rect windowAvailableRect;
windowAvailableRect = new Rect(windowRect.left, windowRect.top + actionBarHeight, windowRect.right, windowRect.bottom);
// viewRect - holds position of the view in window
// (methods as getGlobalVisibleRect, getHitRect, getDrawingRect can return different result,
// when partialy visible)
Rect viewRect;
final int[] viewsLocationInWindow = new int[2];
view.getLocationInWindow(viewsLocationInWindow);
int viewLeft = viewsLocationInWindow[0];
int viewTop = viewsLocationInWindow[1];
int viewRight = viewLeft + view.getWidth();
int viewBottom = viewTop + view.getHeight();
viewRect = new Rect(viewLeft, viewTop, viewRight, viewBottom);
return new Rect[]{windowAvailableRect, viewRect};
}
/** Get the {@link Activity} associated with the {@link Context} if available. */
@Nullable
public static Activity getActivity(Context context) {
while (context instanceof ContextWrapper) {
if (context instanceof Activity) {
return (Activity)context;
}
context = ((ContextWrapper)context).getBaseContext();
}
return null;
}
/** Convert value in device independent pixels (dp) to pixels (px) units. */
public static int dpToPx(Context context, int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
}
}