Skip to content

Commit

Permalink
Merge pull request #2259 from leancodepl/283-toggle-location-android
Browse files Browse the repository at this point in the history
Implement toggling location on Android
  • Loading branch information
piotruela authored Jul 24, 2024
2 parents de22937 + ef10547 commit 7113e33
Show file tree
Hide file tree
Showing 17 changed files with 206 additions and 1 deletion.
1 change: 1 addition & 0 deletions .github/workflows/test-ios-device.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ jobs:
TESTS_TO_EXCLUDE="android_app_test,\
permissions/permissions_location_test,\
service_dark_mode_test,\
service_location_test,\
permissions/notifications_test"
target_paths=""
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test-ios-simulator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ jobs:
service_bluetooth_test,\
service_cellular_test,\
service_wifi_test,\
service_location_test,\
webview_stackoverflow_test,\
webview_leancode_test,\
webview_hackernews_test"
Expand Down
12 changes: 12 additions & 0 deletions dev/e2e_app/integration_test/service_location_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import 'common.dart';

void main() {
patrol('disables and enables location twice', ($) async {
await createApp($);

await $.native.disableLocation();
await $.native.enableLocation();
await $.native.disableLocation();
await $.native.enableLocation();
});
}
2 changes: 1 addition & 1 deletion docs/native/feature-parity.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ impossible to reach 100%. macOS support is still in alpha, so it has no native f
| [Toggle cellular] ||||
| [Toggle Wi-Fi] ||||
| [Toggle Bluetooth] ||||
| Toggle location |see [#283] | ✅ see [#326] ||
| Toggle location | | ✅ see [#326] ||
| [Tap] ||||
| [Double tap] ||||
| [Tap at coordinate] ||||
Expand Down
1 change: 1 addition & 0 deletions packages/patrol/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- Implement `enableBluetooth` and `disableBluetooth` methods for Android > 11. (#2254)
- Implement `enableAirplaneMode` and `disableAirplaneMode` methods for Android. (#2254)
- Implement `enableLocation` and `disableLocation` methods for Android. (#2259)

## 3.9.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.app.Instrumentation
import android.app.UiAutomation
import android.content.Context
import android.content.Intent
import android.location.LocationManager
import android.net.Uri
import android.os.Build
import android.os.SystemClock
Expand Down Expand Up @@ -223,6 +224,42 @@ class Automator private constructor() {
}
}

fun enableLocation() {
val enabled = isLocationEnabled()
if (enabled) {
Logger.d("Location already enabled")
return
} else {
toggleLocation()
}
}

fun disableLocation() {
val enabled = isLocationEnabled()
if (!enabled) {
Logger.d("Location already disabled")
return
} else {
toggleLocation()
}
}

private fun isLocationEnabled(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
// This is a new method provided in API 28
val lm = targetContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
lm.isLocationEnabled
} else {
// This was deprecated in API 28
val mode = Settings.Secure.getInt(
targetContext.contentResolver,
Settings.Secure.LOCATION_MODE,
Settings.Secure.LOCATION_MODE_OFF
)
mode != Settings.Secure.LOCATION_MODE_OFF
}
}

fun getNativeViews(selector: BySelector): List<NativeView> {
Logger.d("getNativeViews()")

Expand Down Expand Up @@ -714,6 +751,23 @@ class Automator private constructor() {
}
}

private fun toggleLocation() {
val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
targetContext.startActivity(intent)

var uiSelector = UiSelector()
uiSelector = uiSelector.text("Use location")
val uiObject = uiDevice.findObject(uiSelector)
if (uiObject != null) {
uiObject.click()
pressBack()
delay()
} else {
throw PatrolException("Could not find location toggle")
}
}

companion object {
val instance = Automator()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,14 @@ class AutomatorServer(private val automation: Automator) : NativeAutomatorServer
automation.disableBluetooth()
}

override fun enableLocation() {
automation.enableLocation()
}

override fun disableLocation() {
automation.disableLocation()
}

override fun getNativeViews(request: GetNativeViewsRequest): GetNativeViewsResponse {
if (request.selector != null) {
val views = automation.getNativeViews(request.selector.toBySelector())
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ extension Selector {
// MARK: Services
func enableDarkMode(_ bundleId: String) throws
func disableDarkMode(_ bundleId: String) throws
func enableLocation() throws
func disableLocation() throws
func enableAirplaneMode() throws
func disableAirplaneMode() throws
func enableCellular() throws
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,18 @@
}
}

func enableLocation() throws {
try runAction("enableLocation") {
throw PatrolError.methodNotImplemented("enableLocation")
}
}

func disableLocation() throws {
try runAction("disableLocation") {
throw PatrolError.methodNotImplemented("disableLocation")
}
}

func enableAirplaneMode() throws {
try runControlCenterAction("enabling airplane mode") {
let toggle = self.springboard.switches["airplane-mode-button"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,18 @@
}
}

func enableLocation() throws {
try runAction("enableLocation") {
throw PatrolError.methodNotImplemented("enableLocation")
}
}

func disableLocation() throws {
try runAction("disableLocation") {
throw PatrolError.methodNotImplemented("disableLocation")
}
}

func enableAirplaneMode() throws {
try runAction("enableAirplaneMode") {
throw PatrolError.methodNotImplemented("enableAirplaneMode")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,18 @@
}
}

func enableBluetooth() throws {
return try runCatching {
try automator.enableLocation()
}
}

func disableBluetooth() throws {
return try runCatching {
try automator.disableLocation()
}
}

// MARK: Notifications

func openNotifications() throws {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ protocol NativeAutomatorServer {
func disableBluetooth() throws
func enableDarkMode(request: DarkModeRequest) throws
func disableDarkMode(request: DarkModeRequest) throws
func enableLocation() throws
func disableLocation() throws
func openNotifications() throws
func closeNotifications() throws
func closeHeadsUpNotification() throws
Expand Down Expand Up @@ -209,6 +211,16 @@ extension NativeAutomatorServer {
return HTTPResponse(.ok)
}

private func enableLocationHandler(request: HTTPRequest) throws -> HTTPResponse {
try enableLocation()
return HTTPResponse(.ok)
}

private func disableLocationHandler(request: HTTPRequest) throws -> HTTPResponse {
try disableLocation()
return HTTPResponse(.ok)
}

private func openNotificationsHandler(request: HTTPRequest) throws -> HTTPResponse {
try openNotifications()
return HTTPResponse(.ok)
Expand Down Expand Up @@ -414,6 +426,16 @@ extension NativeAutomatorServer {
request: request,
handler: disableDarkModeHandler)
}
server.route(.POST, "enableLocation") {
request in handleRequest(
request: request,
handler: enableLocationHandler)
}
server.route(.POST, "disableLocation") {
request in handleRequest(
request: request,
handler: disableLocationHandler)
}
server.route(.POST, "openNotifications") {
request in handleRequest(
request: request,
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions packages/patrol/lib/src/native/native_automator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,28 @@ class NativeAutomator {
await _wrapRequest('disableBluetooth', _client.disableBluetooth);
}

/// Enables location.
///
/// On Android, opens the location settings screen and toggles the location
/// switch to enable location.
/// If the location already enabled, it does nothing.
///
/// Doesn't work for iOS.
Future<void> enableLocation() async {
await _wrapRequest('enableLocation', _client.enableLocation);
}

/// Disables location.
///
/// On Android, opens the location settings screen and toggles the location
/// switch to disable location.
/// If the location already enabled, it does nothing.
///
/// Doesn't work for iOS.
Future<void> disableLocation() async {
await _wrapRequest('disableLocation', _client.disableLocation);
}

/// Taps on the native view specified by [selector].
///
/// It waits for the view to become visible for [timeout] duration. If
Expand Down
22 changes: 22 additions & 0 deletions packages/patrol/lib/src/native/native_automator2.dart
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,28 @@ class NativeAutomator2 {
await _wrapRequest('disableBluetooth', _client.disableBluetooth);
}

/// Enables location.
///
/// On Android, opens the location settings screen and toggles the location
/// switch to enable location.
/// If the location already enabled, it does nothing.
///
/// Doesn't work for iOS.
Future<void> enableLocation() async {
await _wrapRequest('enableLocation', _client.enableLocation);
}

/// Disables location.
///
/// On Android, opens the location settings screen and toggles the location
/// switch to disable location.
/// If the location already enabled, it does nothing.
///
/// Doesn't work for iOS.
Future<void> disableLocation() async {
await _wrapRequest('disableLocation', _client.disableLocation);
}

/// Taps on the native view specified by [selector].
///
/// It waits for the view to become visible for [timeout] duration. If
Expand Down
2 changes: 2 additions & 0 deletions schema.dart
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,8 @@ abstract class NativeAutomator<IOSServer, AndroidServer, DartClient> {
void disableBluetooth();
void enableDarkMode(DarkModeRequest request);
void disableDarkMode(DarkModeRequest request);
void enableLocation();
void disableLocation();

// notifications
void openNotifications();
Expand Down

0 comments on commit 7113e33

Please sign in to comment.