Skip to content

Commit f6e2300

Browse files
Added|Fixed: Do not show AutoFill UI on Termux start and add support for usernames
- The AutoFill type and hints are no longer hardcoded in `TerminalView` class and `TermuxActivity` layout xml. They are dynamically set to required values before making a manual AutoFill request and reverted back afterwards to default values. The hardcoded value `AUTOFILL_TYPE_TEXT` returned by `getAutofillType()` was causing the AutoFill UI to show on Activity starts, this will return `AUTOFILL_TYPE_NONE` by default now so that AutoFill UI isn't shown automatically. - The AutoFill importance is no longer hardcoded in `TermuxActivity` layout xml and is returned by `TerminalView` class itself by `getImportantForAutofill()`. - The AutoFill function in `TermuxActivity` for making a manual AutoFill request is moved to `TerminalView` class. This and moving of hardcoded values to `TerminalView` class mentioned above is done as complete logic of AutoFill should be handled by `TerminalView` class itself and not scattered in various places. - The Terminal context menu now supports AutoFilling a username. Note that GBoard/Google Password Manager seems to have a bug where it will still show `Pick a saved password` instead of username, even though `AUTOFILL_HINT_USERNAME` is being requested, however it will still AutoFill a username of selected entry correctly. - Pressing the back button to close the keyboard will also cancel the current manually requested AutoFill request and UI will not show when keyboard is opened again. Closes #3909
1 parent 661c375 commit f6e2300

File tree

4 files changed

+116
-26
lines changed

4 files changed

+116
-26
lines changed

app/src/main/java/com/termux/app/TermuxActivity.java

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import android.content.IntentFilter;
1111
import android.content.ServiceConnection;
1212
import android.net.Uri;
13-
import android.os.Build;
1413
import android.os.Bundle;
1514
import android.os.IBinder;
1615
import android.view.ContextMenu;
@@ -21,7 +20,6 @@
2120
import android.view.View;
2221
import android.view.ViewGroup;
2322
import android.view.WindowManager;
24-
import android.view.autofill.AutofillManager;
2523
import android.widget.EditText;
2624
import android.widget.ImageButton;
2725
import android.widget.ListView;
@@ -181,7 +179,8 @@ public final class TermuxActivity extends AppCompatActivity implements ServiceCo
181179
private static final int CONTEXT_MENU_SELECT_URL_ID = 0;
182180
private static final int CONTEXT_MENU_SHARE_TRANSCRIPT_ID = 1;
183181
private static final int CONTEXT_MENU_SHARE_SELECTED_TEXT = 10;
184-
private static final int CONTEXT_MENU_AUTOFILL_ID = 2;
182+
private static final int CONTEXT_MENU_AUTOFILL_USERNAME = 11;
183+
private static final int CONTEXT_MENU_AUTOFILL_PASSWORD = 2;
185184
private static final int CONTEXT_MENU_RESET_TERMINAL_ID = 3;
186185
private static final int CONTEXT_MENU_KILL_PROCESS_ID = 4;
187186
private static final int CONTEXT_MENU_STYLING_ID = 5;
@@ -632,20 +631,16 @@ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuIn
632631
TerminalSession currentSession = getCurrentSession();
633632
if (currentSession == null) return;
634633

635-
boolean addAutoFillMenu = false;
636-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
637-
AutofillManager autofillManager = getSystemService(AutofillManager.class);
638-
if (autofillManager != null && autofillManager.isEnabled()) {
639-
addAutoFillMenu = true;
640-
}
641-
}
634+
boolean autoFillEnabled = mTerminalView.isAutoFillEnabled();
642635

643636
menu.add(Menu.NONE, CONTEXT_MENU_SELECT_URL_ID, Menu.NONE, R.string.action_select_url);
644637
menu.add(Menu.NONE, CONTEXT_MENU_SHARE_TRANSCRIPT_ID, Menu.NONE, R.string.action_share_transcript);
645638
if (!DataUtils.isNullOrEmpty(mTerminalView.getStoredSelectedText()))
646639
menu.add(Menu.NONE, CONTEXT_MENU_SHARE_SELECTED_TEXT, Menu.NONE, R.string.action_share_selected_text);
647-
if (addAutoFillMenu)
648-
menu.add(Menu.NONE, CONTEXT_MENU_AUTOFILL_ID, Menu.NONE, R.string.action_autofill_password);
640+
if (autoFillEnabled)
641+
menu.add(Menu.NONE, CONTEXT_MENU_AUTOFILL_USERNAME, Menu.NONE, R.string.action_autofill_username);
642+
if (autoFillEnabled)
643+
menu.add(Menu.NONE, CONTEXT_MENU_AUTOFILL_PASSWORD, Menu.NONE, R.string.action_autofill_password);
649644
menu.add(Menu.NONE, CONTEXT_MENU_RESET_TERMINAL_ID, Menu.NONE, R.string.action_reset_terminal);
650645
menu.add(Menu.NONE, CONTEXT_MENU_KILL_PROCESS_ID, Menu.NONE, getResources().getString(R.string.action_kill_process, getCurrentSession().getPid())).setEnabled(currentSession.isRunning());
651646
menu.add(Menu.NONE, CONTEXT_MENU_STYLING_ID, Menu.NONE, R.string.action_style_terminal);
@@ -676,8 +671,11 @@ public boolean onContextItemSelected(MenuItem item) {
676671
case CONTEXT_MENU_SHARE_SELECTED_TEXT:
677672
mTermuxTerminalViewClient.shareSelectedText();
678673
return true;
679-
case CONTEXT_MENU_AUTOFILL_ID:
680-
requestAutoFill();
674+
case CONTEXT_MENU_AUTOFILL_USERNAME:
675+
mTerminalView.requestAutoFillUsername();
676+
return true;
677+
case CONTEXT_MENU_AUTOFILL_PASSWORD:
678+
mTerminalView.requestAutoFillPassword();
681679
return true;
682680
case CONTEXT_MENU_RESET_TERMINAL_ID:
683681
onResetTerminalSession(session);
@@ -760,15 +758,6 @@ private void toggleKeepScreenOn() {
760758
}
761759
}
762760

763-
private void requestAutoFill() {
764-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
765-
AutofillManager autofillManager = getSystemService(AutofillManager.class);
766-
if (autofillManager != null && autofillManager.isEnabled()) {
767-
autofillManager.requestAutofill(mTerminalView);
768-
}
769-
}
770-
}
771-
772761

773762

774763
/**

app/src/main/res/layout/activity_termux.xml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@
3131
android:focusableInTouchMode="true"
3232
android:scrollbarThumbVertical="@drawable/terminal_scroll_shape"
3333
android:scrollbars="vertical"
34-
android:importantForAutofill="no"
35-
android:autofillHints="password"
3634
tools:ignore="UnusedAttribute" />
3735

3836
<LinearLayout

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
<string name="title_share_selected_text">Terminal Text</string>
7474
<string name="title_share_selected_text_with">Send selected text to:</string>
7575

76+
<string name="action_autofill_username">Autofill username</string>
7677
<string name="action_autofill_password">Autofill password</string>
7778

7879
<string name="action_reset_terminal">Reset</string>

terminal-view/src/main/java/com/termux/view/TerminalView.java

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@
2727
import android.view.ViewConfiguration;
2828
import android.view.ViewTreeObserver;
2929
import android.view.accessibility.AccessibilityManager;
30+
import android.view.autofill.AutofillManager;
3031
import android.view.autofill.AutofillValue;
3132
import android.view.inputmethod.BaseInputConnection;
3233
import android.view.inputmethod.EditorInfo;
3334
import android.view.inputmethod.InputConnection;
3435
import android.widget.Scroller;
3536

37+
import androidx.annotation.NonNull;
3638
import androidx.annotation.Nullable;
3739
import androidx.annotation.RequiresApi;
3840

@@ -85,6 +87,29 @@ public final class TerminalView extends View {
8587
/** If non-zero, this is the last unicode code point received if that was a combining character. */
8688
int mCombiningAccent;
8789

90+
/**
91+
* The current AutoFill type returned for {@link View#getAutofillType()} by {@link #getAutofillType()}.
92+
*
93+
* The default is {@link #AUTOFILL_TYPE_NONE} so that AutoFill UI, like toolbar above keyboard
94+
* is not shown automatically, like on Activity starts/View create. This value should be updated
95+
* to required value, like {@link #AUTOFILL_TYPE_TEXT} before calling
96+
* {@link AutofillManager#requestAutofill(View)} so that AutoFill UI shows. The updated value
97+
* set will automatically be restored to {@link #AUTOFILL_TYPE_NONE} in
98+
* {@link #autofill(AutofillValue)} so that AutoFill UI isn't shown anymore by calling
99+
* {@link #resetAutoFill()}.
100+
*/
101+
@RequiresApi(api = Build.VERSION_CODES.O)
102+
private int mAutoFillType = AUTOFILL_TYPE_NONE;
103+
104+
/**
105+
* The current AutoFill hints returned for {@link View#getAutofillHints()} ()} by {@link #getAutofillHints()} ()}.
106+
*
107+
* The default is an empty `string[]`. This value should be updated to required value. The
108+
* updated value set will automatically be restored an empty `string[]` in
109+
* {@link #autofill(AutofillValue)} by calling {@link #resetAutoFill()}.
110+
*/
111+
private String[] mAutoFillHints = new String[0];
112+
88113
private final boolean mAccessibilityEnabled;
89114

90115
/** The {@link KeyEvent} is generated from a virtual keyboard, like manually with the {@link KeyEvent#KeyEvent(int, int)} constructor. */
@@ -609,6 +634,7 @@ public boolean onKeyPreIme(int keyCode, KeyEvent event) {
609634
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED)
610635
mClient.logInfo(LOG_TAG, "onKeyPreIme(keyCode=" + keyCode + ", event=" + event + ")");
611636
if (keyCode == KeyEvent.KEYCODE_BACK) {
637+
cancelRequestAutoFill();
612638
if (isSelectingText()) {
613639
stopTextSelectionMode();
614640
return true;
@@ -1028,12 +1054,20 @@ public void autofill(AutofillValue value) {
10281054
if (value.isText()) {
10291055
mTermSession.write(value.getTextValue().toString());
10301056
}
1057+
1058+
resetAutoFill();
10311059
}
10321060

10331061
@RequiresApi(api = Build.VERSION_CODES.O)
10341062
@Override
10351063
public int getAutofillType() {
1036-
return AUTOFILL_TYPE_TEXT;
1064+
return mAutoFillType;
1065+
}
1066+
1067+
@RequiresApi(api = Build.VERSION_CODES.O)
1068+
@Override
1069+
public String[] getAutofillHints() {
1070+
return mAutoFillHints;
10371071
}
10381072

10391073
@RequiresApi(api = Build.VERSION_CODES.O)
@@ -1042,6 +1076,74 @@ public AutofillValue getAutofillValue() {
10421076
return AutofillValue.forText("");
10431077
}
10441078

1079+
@RequiresApi(api = Build.VERSION_CODES.O)
1080+
@Override
1081+
public int getImportantForAutofill() {
1082+
return IMPORTANT_FOR_AUTOFILL_NO;
1083+
}
1084+
1085+
@RequiresApi(api = Build.VERSION_CODES.O)
1086+
private synchronized void resetAutoFill() {
1087+
// Restore none type so that AutoFill UI isn't shown anymore.
1088+
mAutoFillType = AUTOFILL_TYPE_NONE;
1089+
mAutoFillHints = new String[0];
1090+
}
1091+
1092+
public boolean isAutoFillEnabled() {
1093+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return false;
1094+
1095+
Context context = getContext();
1096+
if (context == null) return false;
1097+
1098+
AutofillManager autofillManager = context.getSystemService(AutofillManager.class);
1099+
return autofillManager != null && autofillManager.isEnabled();
1100+
}
1101+
1102+
public synchronized void requestAutoFillUsername() {
1103+
requestAutoFill(
1104+
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? new String[]{View.AUTOFILL_HINT_USERNAME} :
1105+
null);
1106+
}
1107+
1108+
public synchronized void requestAutoFillPassword() {
1109+
requestAutoFill(
1110+
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? new String[]{View.AUTOFILL_HINT_PASSWORD} :
1111+
null);
1112+
}
1113+
1114+
public synchronized void requestAutoFill(String[] autoFillHints) {
1115+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
1116+
if (autoFillHints == null || autoFillHints.length < 1) return;
1117+
1118+
Context context = getContext();
1119+
if (context == null) return;
1120+
1121+
AutofillManager autofillManager = context.getSystemService(AutofillManager.class);
1122+
if (autofillManager != null && autofillManager.isEnabled()) {
1123+
// Update type that will be returned by `getAutofillType()` so that AutoFill UI is shown.
1124+
mAutoFillType = AUTOFILL_TYPE_TEXT;
1125+
// Update hints that will be returned by `getAutofillHints()` for which to show AutoFill UI.
1126+
mAutoFillHints = autoFillHints;
1127+
autofillManager.requestAutofill(this);
1128+
}
1129+
}
1130+
1131+
public synchronized void cancelRequestAutoFill() {
1132+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
1133+
if (mAutoFillType == AUTOFILL_TYPE_NONE) return;
1134+
1135+
Context context = getContext();
1136+
if (context == null) return;
1137+
1138+
AutofillManager autofillManager = context.getSystemService(AutofillManager.class);
1139+
if (autofillManager != null && autofillManager.isEnabled()) {
1140+
resetAutoFill();
1141+
autofillManager.cancel();
1142+
}
1143+
}
1144+
1145+
1146+
10451147

10461148

10471149
/**

0 commit comments

Comments
 (0)