SHA256
feat(android): replace WebView with tested native Compose API slice
Add real native onboarding/auth/dashboard/tasks/notes/shopping, origin-bound HTTPS and encrypted AndroidKeyStore session persistence. Archive the superseded browser renderer and tests. Verify 10 JVM tests, 6 native API-35 emulator tests, lintDebug/lintRelease and debug/unsigned release APK/AAB builds; the real upstream suite passes 7 tests and exports a nonblank native fixture screenshot. Tink host-tool update removes the old generated-protobuf warning. Full module/visual parity remains explicitly incomplete.
This commit is contained in:
1 parent
81973c3c45
commit
8c480ddca3
28 files changed
+2628
-742
No files matched your search
@@ -0,0 +1,71 @@
|
||||
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>()
|
||||
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())
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user