Skip to content

Commit

Permalink
[Android] expose "textShowPassword" to the framework (flutter#30780)
Browse files Browse the repository at this point in the history
  • Loading branch information
LongCatIsLooong authored Feb 1, 2022
1 parent 5a30c66 commit 5d193a2
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 0 deletions.
15 changes: 15 additions & 0 deletions lib/ui/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,16 @@ class PlatformDispatcher {
_onTextScaleFactorChangedZone = Zone.current;
}

/// Whether briefly displaying the characters as you type in obscured text
/// fields is enabled in system settings.
///
/// See also:
///
/// * [EditableText.obscureText], which when set to true hides the text in
/// the text field.
bool get brieflyShowPassword => _brieflyShowPassword;
bool _brieflyShowPassword = true;

/// The setting indicating the current brightness mode of the host platform.
/// If the platform has no preference, [platformBrightness] defaults to
/// [Brightness.light].
Expand Down Expand Up @@ -857,6 +867,11 @@ class PlatformDispatcher {

final double textScaleFactor = (data['textScaleFactor'] as num).toDouble();
final bool alwaysUse24HourFormat = data['alwaysUse24HourFormat'] as bool;
// This field is optional.
final bool? brieflyShowPassword = data['brieflyShowPassword'] as bool?;
if (brieflyShowPassword != null) {
_brieflyShowPassword = brieflyShowPassword;
}
final Brightness platformBrightness =
data['platformBrightness'] as String == 'dark' ? Brightness.dark : Brightness.light;
final PlatformConfiguration previousConfiguration = configuration;
Expand Down
9 changes: 9 additions & 0 deletions lib/ui/window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,15 @@ class SingletonFlutterWindow extends FlutterWindow {
/// observe when this value changes.
double get textScaleFactor => platformDispatcher.textScaleFactor;

/// Whether briefly displaying the characters as you type in obscured text
/// fields is enabled in system settings.
///
/// See also:
///
/// * [EditableText.obscureText], which when set to true hides the text in
/// the text field.
bool get brieflyShowPassword => platformDispatcher.brieflyShowPassword;

/// The setting indicating whether time should always be shown in the 24-hour
/// format.
///
Expand Down
2 changes: 2 additions & 0 deletions lib/web_ui/lib/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ abstract class PlatformDispatcher {

double get textScaleFactor => configuration.textScaleFactor;

bool get brieflyShowPassword => true;

VoidCallback? get onTextScaleFactorChanged;
set onTextScaleFactorChanged(VoidCallback? callback);

Expand Down
3 changes: 3 additions & 0 deletions lib/web_ui/lib/window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ abstract class SingletonFlutterWindow extends FlutterWindow {
String get initialLifecycleState => platformDispatcher.initialLifecycleState;

double get textScaleFactor => platformDispatcher.textScaleFactor;

bool get brieflyShowPassword => platformDispatcher.brieflyShowPassword;

bool get alwaysUse24HourFormat => platformDispatcher.alwaysUse24HourFormat;

VoidCallback? get onTextScaleFactorChanged => platformDispatcher.onTextScaleFactorChanged;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.util.SparseArray;
Expand Down Expand Up @@ -140,6 +144,25 @@ public void onAccessibilityChanged(
}
};

private final ContentObserver systemSettingsObserver =
new ContentObserver(new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
if (flutterEngine == null) {
return;
}
Log.v(TAG, "System settings changed. Sending user settings to Flutter.");
sendUserSettingsToFlutter();
}

@Override
public boolean deliverSelfNotifications() {
// The Flutter app may change system settings.
return true;
}
};

private final FlutterUiDisplayListener flutterUiDisplayListener =
new FlutterUiDisplayListener() {
@Override
Expand Down Expand Up @@ -1149,6 +1172,13 @@ public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) {

// Push View and Context related information from Android to Flutter.
sendUserSettingsToFlutter();
getContext()
.getContentResolver()
.registerContentObserver(
Settings.System.getUriFor(Settings.System.TEXT_SHOW_PASSWORD),
false,
systemSettingsObserver);

localizationPlugin.sendLocalesToFlutter(getResources().getConfiguration());
sendViewportMetricsToFlutter();

Expand Down Expand Up @@ -1190,6 +1220,8 @@ public void detachFromFlutterEngine() {
listener.onFlutterEngineDetachedFromFlutterView();
}

getContext().getContentResolver().unregisterContentObserver(systemSettingsObserver);

flutterEngine.getPlatformViewsController().detachFromView();

// Disconnect the FlutterEngine's PlatformViewsController from the AccessibilityBridge.
Expand Down Expand Up @@ -1394,6 +1426,10 @@ public void removeFlutterEngineAttachmentListener(
.getSettingsChannel()
.startMessage()
.setTextScaleFactor(getResources().getConfiguration().fontScale)
.setBrieflyShowPassword(
Settings.System.getInt(
getContext().getContentResolver(), Settings.System.TEXT_SHOW_PASSWORD, 1)
== 1)
.setUse24HourFormat(DateFormat.is24HourFormat(getContext()))
.setPlatformBrightness(brightness)
.send();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public class SettingsChannel {

public static final String CHANNEL_NAME = "flutter/settings";
private static final String TEXT_SCALE_FACTOR = "textScaleFactor";
private static final String BRIEFLY_SHOW_PASSWORD = "brieflyShowPassword";
private static final String ALWAYS_USE_24_HOUR_FORMAT = "alwaysUse24HourFormat";
private static final String PLATFORM_BRIGHTNESS = "platformBrightness";

Expand Down Expand Up @@ -41,6 +42,12 @@ public MessageBuilder setTextScaleFactor(float textScaleFactor) {
return this;
}

@NonNull
public MessageBuilder setBrieflyShowPassword(@NonNull boolean brieflyShowPassword) {
message.put(BRIEFLY_SHOW_PASSWORD, brieflyShowPassword);
return this;
}

@NonNull
public MessageBuilder setUse24HourFormat(boolean use24HourFormat) {
message.put(ALWAYS_USE_24_HOUR_FORMAT, use24HourFormat);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1042,6 +1042,8 @@ private FlutterEngine mockFlutterEngine() {
when(fakeMessageBuilder.setPlatformBrightness(any(SettingsChannel.PlatformBrightness.class)))
.thenReturn(fakeMessageBuilder);
when(fakeMessageBuilder.setTextScaleFactor(any(Float.class))).thenReturn(fakeMessageBuilder);
when(fakeMessageBuilder.setBrieflyShowPassword(any(Boolean.class)))
.thenReturn(fakeMessageBuilder);
when(fakeMessageBuilder.setUse24HourFormat(any(Boolean.class))).thenReturn(fakeMessageBuilder);
when(fakeSettingsChannel.startMessage()).thenReturn(fakeMessageBuilder);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import android.media.Image.Plane;
import android.media.ImageReader;
import android.os.Build;
import android.provider.Settings;
import android.view.DisplayCutout;
import android.view.Surface;
import android.view.View;
Expand Down Expand Up @@ -213,6 +214,8 @@ public void itSendsLightPlatformBrightnessToFlutter() {
SettingsChannel fakeSettingsChannel = mock(SettingsChannel.class);
SettingsChannel.MessageBuilder fakeMessageBuilder = mock(SettingsChannel.MessageBuilder.class);
when(fakeMessageBuilder.setTextScaleFactor(any(Float.class))).thenReturn(fakeMessageBuilder);
when(fakeMessageBuilder.setBrieflyShowPassword(any(Boolean.class)))
.thenReturn(fakeMessageBuilder);
when(fakeMessageBuilder.setUse24HourFormat(any(Boolean.class))).thenReturn(fakeMessageBuilder);
when(fakeMessageBuilder.setPlatformBrightness(any(SettingsChannel.PlatformBrightness.class)))
.thenAnswer(
Expand Down Expand Up @@ -262,6 +265,8 @@ public void itSendsDarkPlatformBrightnessToFlutter() {
SettingsChannel fakeSettingsChannel = mock(SettingsChannel.class);
SettingsChannel.MessageBuilder fakeMessageBuilder = mock(SettingsChannel.MessageBuilder.class);
when(fakeMessageBuilder.setTextScaleFactor(any(Float.class))).thenReturn(fakeMessageBuilder);
when(fakeMessageBuilder.setBrieflyShowPassword(any(Boolean.class)))
.thenReturn(fakeMessageBuilder);
when(fakeMessageBuilder.setUse24HourFormat(any(Boolean.class))).thenReturn(fakeMessageBuilder);
when(fakeMessageBuilder.setPlatformBrightness(any(SettingsChannel.PlatformBrightness.class)))
.thenAnswer(
Expand All @@ -285,6 +290,77 @@ public SettingsChannel.MessageBuilder answer(InvocationOnMock invocation)
assertEquals(SettingsChannel.PlatformBrightness.dark, reportedBrightness.get());
}

@Test
public void itSendsTextShowPasswordToFrameworkOnAttach() {
// Setup test.
AtomicReference<Boolean> reportedShowPassword = new AtomicReference<>();

FlutterView flutterView = new FlutterView(Robolectric.setupActivity(Activity.class));
FlutterEngine flutterEngine =
spy(new FlutterEngine(RuntimeEnvironment.application, mockFlutterLoader, mockFlutterJni));
Settings.System.putInt(
flutterView.getContext().getContentResolver(), Settings.System.TEXT_SHOW_PASSWORD, 1);

SettingsChannel fakeSettingsChannel = mock(SettingsChannel.class);
SettingsChannel.MessageBuilder fakeMessageBuilder = mock(SettingsChannel.MessageBuilder.class);
when(fakeMessageBuilder.setTextScaleFactor(any(Float.class))).thenReturn(fakeMessageBuilder);
when(fakeMessageBuilder.setPlatformBrightness(any(SettingsChannel.PlatformBrightness.class)))
.thenReturn(fakeMessageBuilder);
when(fakeMessageBuilder.setUse24HourFormat(any(Boolean.class))).thenReturn(fakeMessageBuilder);
when(fakeMessageBuilder.setBrieflyShowPassword(any(Boolean.class)))
.thenAnswer(
new Answer<SettingsChannel.MessageBuilder>() {
@Override
public SettingsChannel.MessageBuilder answer(InvocationOnMock invocation)
throws Throwable {
reportedShowPassword.set((Boolean) invocation.getArguments()[0]);
return fakeMessageBuilder;
}
});
when(fakeSettingsChannel.startMessage()).thenReturn(fakeMessageBuilder);
when(flutterEngine.getSettingsChannel()).thenReturn(fakeSettingsChannel);

flutterView.attachToFlutterEngine(flutterEngine);

// Verify results.
assertTrue(reportedShowPassword.get());
}

public void itSendsTextHidePasswordToFrameworkOnAttach() {
// Setup test.
AtomicReference<Boolean> reportedShowPassword = new AtomicReference<>();

FlutterView flutterView = new FlutterView(Robolectric.setupActivity(Activity.class));
FlutterEngine flutterEngine =
spy(new FlutterEngine(RuntimeEnvironment.application, mockFlutterLoader, mockFlutterJni));
Settings.System.putInt(
flutterView.getContext().getContentResolver(), Settings.System.TEXT_SHOW_PASSWORD, 0);

SettingsChannel fakeSettingsChannel = mock(SettingsChannel.class);
SettingsChannel.MessageBuilder fakeMessageBuilder = mock(SettingsChannel.MessageBuilder.class);
when(fakeMessageBuilder.setTextScaleFactor(any(Float.class))).thenReturn(fakeMessageBuilder);
when(fakeMessageBuilder.setPlatformBrightness(any(SettingsChannel.PlatformBrightness.class)))
.thenReturn(fakeMessageBuilder);
when(fakeMessageBuilder.setUse24HourFormat(any(Boolean.class))).thenReturn(fakeMessageBuilder);
when(fakeMessageBuilder.setBrieflyShowPassword(any(Boolean.class)))
.thenAnswer(
new Answer<SettingsChannel.MessageBuilder>() {
@Override
public SettingsChannel.MessageBuilder answer(InvocationOnMock invocation)
throws Throwable {
reportedShowPassword.set((Boolean) invocation.getArguments()[0]);
return fakeMessageBuilder;
}
});
when(fakeSettingsChannel.startMessage()).thenReturn(fakeMessageBuilder);
when(flutterEngine.getSettingsChannel()).thenReturn(fakeSettingsChannel);

flutterView.attachToFlutterEngine(flutterEngine);

// Verify results.
assertFalse(reportedShowPassword.get());
}

// This test uses the API 30+ Algorithm for window insets. The legacy algorithm is
// set to -1 values, so it is clear if the wrong algorithm is used.
@Test
Expand Down

0 comments on commit 5d193a2

Please sign in to comment.