How to conditionally run a #Rule in Java/Kotlin androidx UIAutomation - android

I am writing UIAutomation for Android. There is a handy #Rule available in the testing libraries that lets me preemptively grant app permissions so I don't have to deal with permission dialogs when running UI tests on the app.
There is a new permission in Android 13 (sdk33) called 'POST_NOTIFICATIONS'. The app I'm testing uses this capability, but devices running Android 12 and below don't recognize it. So, here's my problem: I have a permissions rule that looks like this:
#Rule
#JvmField
var grantPermissionRule: GrantPermissionRule =
grant(
"android.permission.ACCESS_FINE_LOCATION",
"android.permission.POST_NOTIFICATIONS"
)
It works perfectly when I run the test against an Android 13 device. But when I run it on an Android 12 device, the test crashes right away due to the permissions manager attempting to request a permission that doesn't exist. This unhandled exception comes from the built-in test library code from google -- GrantPermissionsRule.java in package androidx.test.rule When I change the code as shown below to remove the new permission, it works fine on Android 12, but now the Notifications dialog will appear when I use this code with Android 13 devices.
#Rule
#JvmField
var grantPermissionRule: GrantPermissionRule =
grant(
"android.permission.ACCESS_FINE_LOCATION"
)
I want to be able to run the tests on both Android 12 and Android 13 devices without changing the permissions code back and forth. So how do I solve this problem? I don't know much about advanced java/kotlin features, but there doesn't appear to be a way to wrap a #Rule inside a try/catch block so I can handle the exception and execute the abridged code block. And there doesn't appear to be a way to make a #Rule conditional on, say, android device version number, so that I can use the right set of permissions at the right time in the first place.
Do I need to override the builtin androidx library? Or maybe there is a fancy Kotlin or Java thing I can use to conditionally add the POST_NOTIFICATIONS permission to the set of permissions I end up using inside the #Rule? I'm open to suggestions.

Try:
#Rule
#JvmField
var grantPermissionRule: GrantPermissionRule =
if (Build.VERSION.SDK_INT >= 33) {
grant(
"android.permission.ACCESS_FINE_LOCATION",
"android.permission.POST_NOTIFICATIONS"
)
} else {
grant(
"android.permission.ACCESS_FINE_LOCATION"
)
}
BTW, unless there's something magic about this rule that requires var, I would use val over var.

Related

Android: How to do instrumented tests with mocked locations?

For instrumented tests of location services I need mocked locations. But the current concept from Android seems to be not very practical.
In order to be allowed to use mocked locations the test-package also needs the permission android.permission.ACCESS_MOCK_LOCATION. When added to a debug-manifest I get the hint, that this permission will only be granted to System-Apps. I have to add tools:ignore="ProtectedPermissions" to the permission to suppress this warning. IMHO it makes no sense to issue such a warning for a property that is necessary for such tests.
Then I need to select this test-package as mock-location provider for the test-device. Acceptable for a manual test. But do I really have to manually select this setting for every device I want to test my code on? (What about this firebase-test on a farm of test devices? There I can not make this setting.)
And what if I have more than one app that tests mocked location? Currently I can not test both packages on the same device.
Or am I missing something?

Android instrumented test manually granting the permissions in the beginning of each test method using GrantPermissionRule

I am developing an Android app using Kotlin. I am writing instrumented tests for my application. I am granting the permissions in my test using GrantPermissionRule as below.
#get:Rule
var permissionRule: GrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION)
However, that does not fit well with my requirements. I am wiping out all the granted permissions after each does. Also, I like to test the scenarios where the permissions are not granted. I am launching the activity explicitly in my test like this.
#Test
fun exampleTest() {
this.eventDetailsActivityRule.launchActivity(intent)
}
I like to explicitly grant the permissions using GrantPermissionRule after or before launching the activity. How can I do that?

Android InstantApp vs Full APK Permission handling issue

Implementing a MediaProjection sample for Screen Recording purposes while having my project setup in InstantApps mode gave me Security Permission error when running this line of code:
val REQUEST_MEDIA_PROJECTION = 101
startActivityForResult(mMediaProjectionManager?.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION)
But prompts you and allows you to continue when not an InstantApp.
Any ideas on a workaround, besides switching back to the regular non InstantApp

How to handle android runtime permission with kivy

I found that kivy is very nice framework to build cross platform application and I am very interested in kivy just to do android application as I think is easy and comfortable in kivy.
After trying few examples, I am interested to know how should handle android run time permission for the kivy app.
Actually I had searched on google, but no single working example out there. Should I go back to android / java or it possible with kivy and some other python libs.
pyjnius is the way to go. You have to port these instructions using pyjnius. This involves the following steps:
Unfortunately the api call to ContextCompat.checkSelfPermission is implemented in the android sdk support library which has to be downloaded seperately,
so get the .aar with the version best matching your android API level for example here.
copy it into your project dir and reference it from your buildozer.spec:
android.add_aars = support-v4-26.0.0-alpha1.aar
make sure jinius is in the requirements in buildozer.spec
use the following code snippet
Note: this is a blocking function which waits until the permissions dialog is answered. If the app already has the permission the function returns immediately. So for example if you want to get the permissions for writing to the SD card and for the camera, which are both "dangerous permissions", call:
perms = ["android.permission.READ_EXTERNAL_STORAGE",
"android.permission.WRITE_EXTERNAL_STORAGE",
"android.permission.CAMERA"]
haveperms = acquire_permissions(perms)
And here the function for acquiring the permissions:
import time
import functools
import jnius
def acquire_permissions(permissions, timeout=30):
"""
blocking function for acquiring storage permission
:param permissions: list of permission strings , e.g. ["android.permission.READ_EXTERNAL_STORAGE",]
:param timeout: timeout in seconds
:return: True if all permissions are granted
"""
PythonActivity = jnius.autoclass('org.kivy.android.PythonActivity')
Compat = jnius.autoclass('android.support.v4.content.ContextCompat')
currentActivity = jnius.cast('android.app.Activity', PythonActivity.mActivity)
checkperm = functools.partial(Compat.checkSelfPermission, currentActivity)
def allgranted(permissions):
"""
helper function checks permissions
:param permissions: list of permission strings
:return: True if all permissions are granted otherwise False
"""
return reduce(lambda a, b: a and b,
[True if p == 0 else False for p in map(checkperm, permissions)]
)
haveperms = allgranted(permissions)
if haveperms:
# we have the permission and are ready
return True
# invoke the permissions dialog
currentActivity.requestPermissions(permissions, 0)
# now poll for the permission (UGLY but we cant use android Activity's onRequestPermissionsResult)
t0 = time.time()
while time.time() - t0 < timeout and not haveperms:
# in the poll loop we could add a short sleep for performance issues?
haveperms = allgranted(permissions)
return haveperms
Probably the cleanest way would be to pimp p4a's PythonActivity.java to do that but this one does it for me for now.
Hi this question is old but you can use
request_permissions([Permission.WRITE_EXTERNAL_STORAGE])
#For requesting permission you can pass a list with all the permissions you need
check_permission('android.permission.WRITE_EXTERNAL_STORAGE')
#returns True if you have the permission
you can check: python-for-android example
you can check the code and the list of permission you can use with this method:
python-for-android code
python-for-android doesn't have any code for handling runtime permissions. I expect to look at it sooner rather than later, but there's no ETA for it.
You can probably add the code for it yourself if you're interested and know how. If you'd like to try it, such contributions would be very welcome.
i know this answer is a little late, but to get permissions you have to specify them before the build. E.g buildozer uses a buildozer.spec. In this file you can specify the permissions you need.

Android Instrumentation Context/Permission Issues with Selendroid

specific question for you. I'm trying to write a feature in a test framework that adds call logs to the device/emulator. This requires android.permission.WRITE_CALL_LOGS.
I am using selendroid, and have added this permission to the AndroidManifest. However, this permission is not in my application under test.
In the Instrumentation class, I inevitably try to run
getContext().getContentResolver().insert(CallLog.Calls.CONTENT_URI, values);
and it returns an error citing permission issues. getContext() is supposed to return the context of the instrumentation, which I assume is the selendroid app installed on the device, which should have the proper WRITE_CALL_LOGS permission.
Where am I going wrong?

Categories

Resources