package community.yuvomi.mobile import android.Manifest import android.accessibilityservice.AccessibilityServiceInfo import android.content.Context import android.content.pm.PackageManager import android.os.Build import android.view.accessibility.AccessibilityNodeInfo import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class LocalNetworkPermissionTest { @Test fun permissionIsOptionalAndHonorsSystemDenialAndGrant() { val context = ApplicationProvider.getApplicationContext() context.getSharedPreferences("server", Context.MODE_PRIVATE).edit().clear().commit() ActivityScenario.launch(MainActivity::class.java).use { if (Build.VERSION.SDK_INT < 37 || context.checkSelfPermission(Manifest.permission.ACCESS_LOCAL_NETWORK) == PackageManager.PERMISSION_GRANTED ) { onView(withText("Allow private-network servers")).check(doesNotExist()) return@use } val automation = InstrumentationRegistry.getInstrumentation().uiAutomation automation.serviceInfo = automation.serviceInfo.apply { flags = flags or AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS } onView(withText("Allow private-network servers")).perform(click()) onView(withText("Cancel")).perform(click()) assertEquals(PackageManager.PERMISSION_DENIED, context.checkSelfPermission(Manifest.permission.ACCESS_LOCAL_NETWORK)) onView(withText("Allow private-network servers")).perform(click()) onView(withText("Continue")).perform(click()) chooseSystemButton("permission_deny_button") await { context.checkSelfPermission(Manifest.permission.ACCESS_LOCAL_NETWORK) == PackageManager.PERMISSION_DENIED } onView(withText("Allow private-network servers")).perform(click()) onView(withText("Continue")).perform(click()) chooseSystemButton("permission_allow_button") await { context.checkSelfPermission(Manifest.permission.ACCESS_LOCAL_NETWORK) == PackageManager.PERMISSION_GRANTED } } } private fun chooseSystemButton(id: String) { val automation = InstrumentationRegistry.getInstrumentation().uiAutomation await { val node = find(automation.rootInActiveWindow, id) ?: return@await false node.performAction(AccessibilityNodeInfo.ACTION_CLICK) } } private fun find(node: AccessibilityNodeInfo?, id: String): AccessibilityNodeInfo? { if (node == null) return null if (node.viewIdResourceName?.endsWith("/$id") == true) return node for (index in 0 until node.childCount) find(node.getChild(index), id)?.let { return it } return null } private fun await(condition: () -> Boolean) { val deadline = System.nanoTime() + 10_000_000_000L while (System.nanoTime() < deadline) { if (condition()) return Thread.sleep(100) } assertTrue("System permission response timed out", condition()) } }