Skip to content

Commit 9ec3e4f

Browse files
authored
Merge pull request #3052 from leancodepl/feature/battery-optimization-allowance
Feature/battery optimization allowance on android
2 parents 80000a7 + cbf85ec commit 9ec3e4f

12 files changed

Lines changed: 88 additions & 1 deletion

File tree

dev/e2e_app/android/app/src/main/AndroidManifest.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" tools:ignore="QueryAllPackagesPermission"/>
1616
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
1717
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
18+
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
19+
20+
1821

1922
<application
2023
android:label="example"

dev/e2e_app/lib/keys.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class Keys {
4343
static const microphonePermissionTile = Key('microphone');
4444
static const locationPermissionTile = Key('location');
4545
static const galleryPermissionTile = Key('gallery');
46+
static const batteryPermissionTile = Key('battery');
4647
static const permissionsScreen = Key('permissionsScreen');
4748
static const requestCameraPermissionButton = Key(
4849
'requestCameraPermissionButton',
@@ -56,6 +57,9 @@ class Keys {
5657
static const requestGalleryPermissionButton = Key(
5758
'requestGalleryPermissionButton',
5859
);
60+
static const requestBatteryPermissionButton = Key(
61+
'requestBatteryPermissionButton',
62+
);
5963

6064
// location screen
6165
static const grantLocationPermissionButton = Key(

dev/e2e_app/lib/permissions_screen.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class _PermissionsScreenState extends State<PermissionsScreen> {
1616
var _microphonePermissionGranted = false;
1717
var _locationPermissionGranted = false;
1818
var _galleryPermissionGranted = false;
19+
var _batteryPermissionGranted = false;
1920

2021
Future<void> _requestCameraPermission() async {
2122
await Future<void>.delayed(Duration(seconds: 1));
@@ -49,6 +50,13 @@ class _PermissionsScreenState extends State<PermissionsScreen> {
4950
});
5051
}
5152

53+
Future<void> _requestBatteryPermission() async {
54+
final status = await Permission.ignoreBatteryOptimizations.request();
55+
setState(() {
56+
_batteryPermissionGranted = status == PermissionStatus.granted;
57+
});
58+
}
59+
5260
@override
5361
void initState() {
5462
super.initState();
@@ -83,6 +91,14 @@ class _PermissionsScreenState extends State<PermissionsScreen> {
8391
});
8492
}),
8593
);
94+
95+
unawaited(
96+
Permission.ignoreBatteryOptimizations.status.then((value) {
97+
setState(() {
98+
_batteryPermissionGranted = value == PermissionStatus.granted;
99+
});
100+
}),
101+
);
86102
}
87103

88104
@override
@@ -121,6 +137,13 @@ class _PermissionsScreenState extends State<PermissionsScreen> {
121137
onTap: _requestGalleryPermission,
122138
key: K.galleryPermissionTile,
123139
),
140+
_PermissionTile(
141+
name: 'Battery Optimizations',
142+
icon: Icons.battery_0_bar,
143+
granted: _batteryPermissionGranted,
144+
onTap: _requestBatteryPermission,
145+
key: K.batteryPermissionTile,
146+
),
124147
],
125148
),
126149
),
@@ -148,6 +171,7 @@ class _PermissionTile extends StatelessWidget {
148171
'microphone' => K.requestMicrophonePermissionButton,
149172
'location' => K.requestLocationPermissionButton,
150173
'gallery' => K.requestGalleryPermissionButton,
174+
'battery optimizations' => K.requestBatteryPermissionButton,
151175
_ => Key('request${permissionName}PermissionButton'),
152176
};
153177

dev/e2e_app/patrol_test/permissions/permissions_many_test.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ void main() {
1919
await _requestAndGrantMicrophonePermission($);
2020
await _requestAndDenyLocationPermission($);
2121
await _requestAndDenyGalleryPermission($);
22+
await _requestAndGrantBatteryPermission($);
2223
},
2324
tags: [
2425
'locale_testing_ios',
@@ -92,3 +93,18 @@ Future<void> _requestAndDenyGalleryPermission(PatrolIntegrationTester $) async {
9293
}
9394
expect($(K.galleryPermissionTile).$(#statusText).text, 'Not granted');
9495
}
96+
97+
Future<void> _requestAndGrantBatteryPermission(
98+
PatrolIntegrationTester $,
99+
) async {
100+
if (Platform.isAndroid) {
101+
if (!await Permission.ignoreBatteryOptimizations.isGranted) {
102+
expect($(K.batteryPermissionTile).$(#statusText).text, 'Not granted');
103+
await $(K.requestBatteryPermissionButton).tap();
104+
await $.platform.android.allowPermission();
105+
await $.pump();
106+
}
107+
108+
expect($(K.batteryPermissionTile).$(#statusText).text, 'Granted');
109+
}
110+
}

packages/patrol/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 4.6.0
2+
3+
- Add `allowPermission` method to `AndroidAutomator`.
4+
15
## 4.5.0
26

37
- Fix `appId` not being passed down on `$.platform.mobile.enterText` and `$.platform.mobile.enterTextByIndex` (#2992)

packages/patrol/android/src/main/kotlin/pl/leancode/patrol/Automator.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,8 @@ class Automator private constructor() {
669669
val identifiers = arrayOf(
670670
"com.android.packageinstaller:id/permission_deny_button", // API <= 28
671671
"com.android.permissioncontroller:id/permission_deny_button", // API >= 29 (first invocation)
672-
"com.android.permissioncontroller:id/permission_deny_and_dont_ask_again_button" // API >= 29 (second invocation)
672+
"com.android.permissioncontroller:id/permission_deny_and_dont_ask_again_button", // API >= 29 (second invocation)
673+
"android:id/button2" // for battery permission
673674
)
674675

675676
val uiObject = waitForUiObjectByResourceId(*identifiers, timeout = timeoutMillis)
@@ -678,6 +679,13 @@ class Automator private constructor() {
678679
uiObject.click()
679680
}
680681

682+
fun allowPermission() {
683+
val resourceId = "android:id/button1"
684+
val uiObject = waitForUiObjectByResourceId(resourceId, timeout = timeoutMillis)
685+
?: throw UiObjectNotFoundException("button to allow permission")
686+
uiObject.click()
687+
}
688+
681689
fun selectFineLocation() {
682690
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
683691
Logger.i("Ignoring selectFineLocation() since it's not available on ${Build.VERSION.SDK_INT}")

packages/patrol/android/src/main/kotlin/pl/leancode/patrol/AutomatorServer.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,10 @@ class AutomatorServer(private val automation: Automator) : MobileAutomatorServer
247247
}
248248
}
249249

250+
override fun allowPermission() {
251+
automation.allowPermission()
252+
}
253+
250254
override fun setLocationAccuracy(request: SetLocationAccuracyRequest) {
251255
when (request.locationAccuracy) {
252256
SetLocationAccuracyRequestLocationAccuracy.coarse -> automation.selectCoarseLocation()

packages/patrol/android/src/main/kotlin/pl/leancode/patrol/contracts/AndroidAutomatorServer.kt

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/patrol/lib/src/platform/android/android_automator.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,12 @@ abstract interface class AndroidAutomator implements MobileAutomator {
233233
/// [AndroidAutomatorConfig.findTimeout].
234234
Future<void> waitUntilVisible(AndroidSelector selector, {Duration? timeout});
235235

236+
/// Taps the standard Android system dialog "Allow" button.
237+
///
238+
/// This is useful for Android dialogs that use the platform alert button with
239+
/// `android:id/button1`, for example battery optimization permission dialogs.
240+
Future<void> allowPermission();
241+
236242
/// Returns a list of currently visible native UI controls, specified by
237243
/// [selector], which are currently visible on screen.
238244
///

packages/patrol/lib/src/platform/android/android_automator_native.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,12 @@ class AndroidAutomator extends NativeMobileAutomator
489489
);
490490
}
491491

492+
/// Taps the standard Android system dialog "Allow" button.
493+
@override
494+
Future<void> allowPermission() async {
495+
await wrapRequest('allowPermission', _client.allowPermission);
496+
}
497+
492498
/// Returns a list of currently visible native UI controls, specified by
493499
/// [selector], which are currently visible on screen.
494500
///

0 commit comments

Comments
 (0)