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
This commit is contained in:
agnostic-apollo
2024-08-27 04:42:02 +05:00
parent 661c37501f
commit c2d57f2ed8
4 changed files with 132 additions and 26 deletions

View File

@@ -27,6 +27,7 @@ import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityManager;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
@@ -85,6 +86,29 @@ public final class TerminalView extends View {
/** If non-zero, this is the last unicode code point received if that was a combining character. */
int mCombiningAccent;
/**
* The current AutoFill type returned for {@link View#getAutofillType()} by {@link #getAutofillType()}.
*
* The default is {@link #AUTOFILL_TYPE_NONE} so that AutoFill UI, like toolbar above keyboard
* is not shown automatically, like on Activity starts/View create. This value should be updated
* to required value, like {@link #AUTOFILL_TYPE_TEXT} before calling
* {@link AutofillManager#requestAutofill(View)} so that AutoFill UI shows. The updated value
* set will automatically be restored to {@link #AUTOFILL_TYPE_NONE} in
* {@link #autofill(AutofillValue)} so that AutoFill UI isn't shown anymore by calling
* {@link #resetAutoFill()}.
*/
@RequiresApi(api = Build.VERSION_CODES.O)
private int mAutoFillType = AUTOFILL_TYPE_NONE;
/**
* The current AutoFill hints returned for {@link View#getAutofillHints()} ()} by {@link #getAutofillHints()} ()}.
*
* The default is an empty `string[]`. This value should be updated to required value. The
* updated value set will automatically be restored an empty `string[]` in
* {@link #autofill(AutofillValue)} by calling {@link #resetAutoFill()}.
*/
private String[] mAutoFillHints = new String[0];
private final boolean mAccessibilityEnabled;
/** The {@link KeyEvent} is generated from a virtual keyboard, like manually with the {@link KeyEvent#KeyEvent(int, int)} constructor. */
@@ -609,6 +633,7 @@ public final class TerminalView extends View {
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED)
mClient.logInfo(LOG_TAG, "onKeyPreIme(keyCode=" + keyCode + ", event=" + event + ")");
if (keyCode == KeyEvent.KEYCODE_BACK) {
cancelRequestAutoFill();
if (isSelectingText()) {
stopTextSelectionMode();
return true;
@@ -1028,12 +1053,20 @@ public final class TerminalView extends View {
if (value.isText()) {
mTermSession.write(value.getTextValue().toString());
}
resetAutoFill();
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public int getAutofillType() {
return AUTOFILL_TYPE_TEXT;
return mAutoFillType;
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public String[] getAutofillHints() {
return mAutoFillHints;
}
@RequiresApi(api = Build.VERSION_CODES.O)
@@ -1042,6 +1075,91 @@ public final class TerminalView extends View {
return AutofillValue.forText("");
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public int getImportantForAutofill() {
return IMPORTANT_FOR_AUTOFILL_NO;
}
@RequiresApi(api = Build.VERSION_CODES.O)
private synchronized void resetAutoFill() {
// Restore none type so that AutoFill UI isn't shown anymore.
mAutoFillType = AUTOFILL_TYPE_NONE;
mAutoFillHints = new String[0];
}
public AutofillManager getAutoFillManagerService() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return null;
try {
Context context = getContext();
if (context == null) return null;
return context.getSystemService(AutofillManager.class);
} catch (Exception e) {
mClient.logStackTraceWithMessage(LOG_TAG, "Failed to get AutofillManager service", e);
return null;
}
}
public boolean isAutoFillEnabled() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return false;
try {
AutofillManager autofillManager = getAutoFillManagerService();
return autofillManager != null && autofillManager.isEnabled();
} catch (Exception e) {
mClient.logStackTraceWithMessage(LOG_TAG, "Failed to check if Autofill is enabled", e);
return false;
}
}
public synchronized void requestAutoFillUsername() {
requestAutoFill(
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? new String[]{View.AUTOFILL_HINT_USERNAME} :
null);
}
public synchronized void requestAutoFillPassword() {
requestAutoFill(
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? new String[]{View.AUTOFILL_HINT_PASSWORD} :
null);
}
public synchronized void requestAutoFill(String[] autoFillHints) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
if (autoFillHints == null || autoFillHints.length < 1) return;
try {
AutofillManager autofillManager = getAutoFillManagerService();
if (autofillManager != null && autofillManager.isEnabled()) {
// Update type that will be returned by `getAutofillType()` so that AutoFill UI is shown.
mAutoFillType = AUTOFILL_TYPE_TEXT;
// Update hints that will be returned by `getAutofillHints()` for which to show AutoFill UI.
mAutoFillHints = autoFillHints;
autofillManager.requestAutofill(this);
}
} catch (Exception e) {
mClient.logStackTraceWithMessage(LOG_TAG, "Failed to request Autofill", e);
}
}
public synchronized void cancelRequestAutoFill() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
if (mAutoFillType == AUTOFILL_TYPE_NONE) return;
try {
AutofillManager autofillManager = getAutoFillManagerService();
if (autofillManager != null && autofillManager.isEnabled()) {
resetAutoFill();
autofillManager.cancel();
}
} catch (Exception e) {
mClient.logStackTraceWithMessage(LOG_TAG, "Failed to cancel Autofill request", e);
}
}
/**