I've an extension function for opening an intent for my activities:
fun Activity.openIntent(action: String?, type: String?, uri: Uri?) {
Intent()
.apply {
action?.let { this.action = it }
uri?.let { this.data = it }
type?.let { this.type = it }
}
.also { intent ->
packageManager?.let {
if (intent.resolveActivity(it) != null)
startActivity(intent)
else
showToast(R.string.application_not_found)
}
}
}
My targetSdkVersion is 30. It gives me a warning in intent.resolveActivity(it):
Consider adding a queries declaration to your manifest when calling this method.
So What should I do to solve this warning?
The simplest solution is to get rid of resolveActivity(). Replace:
packageManager?.let {
if (intent.resolveActivity(it) != null)
startActivity(intent)
else
showToast(R.string.application_not_found)
}
with:
try {
startActivity(intent)
} catch (ex: ActivityNotFoundException) {
showToast(R.string.application_not_found)
}
This gives you the same result with a bit better performance, and it will get rid of the warning.
Another option would be to add the QUERY_ALL_PACKAGES permission. This may get you banned from the Play Store.
Otherwise, you will need to:
Build a list of every possible openIntent() call that your app may make
Add a <queries> element to your manifest that lists all of those possible Intent structures that you want to be able to use with resolveActivity()
Repeat this process periodically, in case you add new scenarios for openIntent()
So starting Android 11 (i.e, if your app targets Android 11) not all applications will be visible to your application. Some apps are visible by default but in order to access other applications through your application, you will have to declare queries in your manifest else your application will not be able to access them. You can read about that here.
So if your application targets Android 11 and is to access an application that may not be visible by default you will want to add queries for them in the manifest file.
In your case, this warning is not applicable as I believe you are using implicit intents to open other applications. Using implicit intents, other applications can be accessed irrespective of app visibility. If your app target Android 10 or lower you can suppress the warning as all apps are visible by default.
To suppress the lint warning you can either:
Add the suppress annotation, like so:
#SuppressLint("QueryPermissionsNeeded")
fun Activity.openIntent(action: String?, type: String?, uri: Uri?): Activity {
Add the following to your android block in your app module build gradle file:
lintOptions {
ignore "QueryPermissionsNeeded"
}
Replace
if (intent.resolveActivity(it) != null)
with
if (it.resolveActivity(intent, 0) != null)
and the warning will be gone.
From API level 30 package visibility is restricted. So add appropriate query in your AndroidManifest file outside <application> tag.
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="http" />
</intent>
</queries>
Related
I am currently developing an android app and needed a function that starts a phone call so I added this code.
public void dialPhoneNumber(String phoneNumber) {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:" + phoneNumber));
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
.. it seems to work perfectly in older android versions but when I test it in android 11 it doesn't function at all I tried action_call and added the permission <uses-permission android:name="android.permission.CALL_PHONE" /> still doesn't work.
Your problem lies in the line of code intent.resolveActivity(getPackageManager()). When you call resolveActivity, you will get a warning like this:
Consider adding a declaration to your manifest when calling this method; see https://g.co/dev/packagevisibility for details
Check the document under PackageManager, you will see this note:
Note: If your app targets Android 11 (API level 30) or higher, the methods in this class each return a filtered list of apps. Learn more about how to manage package visibility.
So what does that mean?
In android 11, Google added package visibility policy. Apps now have tighter control over viewing other apps. Your application will not be able to view or access applications outside of your application.
What do you need to do?
All you need to do is add below line of code to AndroidManifest.xml:
<manifest>
<queries>
<!-- Specific intents you query for -->
<intent>
<action android:name="android.intent.action.DIAL" />
</intent>
</queries>
</manifest>
More information:
Package visibility in Android 11
Package visibility filtering on Android
I've been trying to get my app to be able to delete an audio file. However, after trying many possible solutions, I couldn't really find one that works.
Here is my solution so far:
public static void deleteFiles(List<Track> tracks, Context context,
final MutableLiveData<IntentSender> deletionIntentSenderLD){
final Uri AUDIO_URI = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
for(Track t : tracks){
try {
context.getContentResolver().delete(ContentUris
.withAppendedId(AUDIO_URI, t.getUriId()), null, null);
}catch (SecurityException securityException) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (securityException instanceof RecoverableSecurityException) {
deletionIntentSenderLD
.postValue(((RecoverableSecurityException) securityException)
.getUserAction().getActionIntent().getIntentSender());
} else
throw securityException;
} else
throw securityException;
}
}
}
When the try block fails a SecurityException is catch then the IntentSender is passed to the live data that is observed in a fragment:
audioViewModel.getDeletionIntentSenderLD().observe(getViewLifecycleOwner(),
intentSender -> {
try {
startIntentSenderForResult(intentSender, DELETE_PERMISSION_REQUEST,
null, 0 ,0, 0,
null);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
});
I've tried implementing the onRequestPermissionResult() method but that doesn't do anything. I've also tried deleting the files using File file = new File(), however, due to the changes made to Android 10, I didn't expect it to work.
So after many Google searches, I've come to the conclusion that the best approach (to my knowledge) is to simply turn off scoped storage for Android Q (10).
Here, I'll provide two solutions. The first is the one where I turn it off and the second is the one where scope storage is still enable. However, a thing you should note is that the second solution is a little buggy, at times it actually does delete both the actual media file and updates the Media Store, but most times it simply deletes from the Media Store only. Obviously, this isn't a very good solution as on reboot your application would then load those files back in because the Media Store would scan for them.
Solution 1 - Turn off Scoped Storage
For this solution you can still target Android 11. All you have to do is go to the build.gradle file at the Module Level and set the compileSdkVersion and targetSdkVersion to 30.
After that, you go into the AndroidManifest.xml and have the uses-permission and application tag set up like this:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29"
tools:ignore="ScopedStorage"/>
<application
android:requestLegacyExternalStorage="true"
...
After having done that, you could use the Content Resolver to delete the media file (and update the Media Store) and you do not have to worry about catching a security exception like its said in the Android docs. Your implementation for Android 11s delete operation should not be affected.
Solution-ish 2 - Turn on Scoped Storage
Firstly, in your manifest ensure that the WRITE_EXTERNAL_STORAGE permissions maxSdkVersion is set to 28. Also ensure that requestLegacyExternalStorage is set to false (don't think this is required). Then simply copy the code in my original post. You do not require a Live Data if you are doing the delete operation from your activity/fragment. But you should note that startIntentSenderForResult() requires an activity.
But as I mentioned before, I did experience some bugs with this. The most frustrating thing about this solution though is that it does not delete the actual file but instead deletes the entry from the Media Store. Maybe this has something to do with the fact that #blackapps mentioned, which is that you cannot bulk delete and I might have implemented it slightly wrong. Nevertheless, this is horrible for user experience if bulk deletion is impossible in Android 10.
The tutorials I followed for this are:
https://developer.android.com/training/data-storage/shared/media#remove-item
https://www.raywenderlich.com/9577211-scoped-storage-in-android-10-getting-started#toc-anchor-007
https://www.solutionanalysts.com/blog/scoped-storage-in-android-10/
Side Note - Delete on Android 11
To delete on Android 11 you just need to call createDeleteRequest() which should return a PendingIntent. From this PendingIntent you could get the IntentSender by using getIntentSender. Pass this intent sender to the activity/fragment then call startIntentSenderForResult() in your activity/fragment. This pops up a dialog to the user asking them if the application can delete a file. If the user gives permission the system goes ahead and deletes the file and updates the Media Store.
Side Side Note - Scoped Storage, Android 10 and Future
From everything I've seen, it seems to suggest that scoped storage is only enforced in Android 11 but I'm not entirely sure if the legacy option would still be available in Android 10 indefinitely. But I would have to do more research on this...
I have been using the following code to open my other app from within my app. This code works upto Android 29 devices but this code does not work in Android 30 devices. Can somebody please help me make it work for Android 30 as well. Thanks
case R.id.btAudio:
Intent launchIntent = getPackageManager().getLaunchIntentForPackage(getString("com.android.audioapp"));
Intent uin = new Intent(MainActivity.this, AudioInstall.class);
if (launchIntent != null) {
startActivity(launchIntent);
} else {
startActivity(uin);
}
this.finish();
break;
Android 11 added restrictions regarding the visibility of other apps. Apps that have targetSdk set to >= 30 can't interact with and open other apps without specifying this in the manifest.
To specify that your app interacts with another specific app, you need to add a <queries> element to your manifest file:
<manifest package="com.example.game">
<queries>
<package android:name="com.android.audioapp" />
</queries>
...
</manifest>
The link below contains other examples in case you need to specify a broader range of apps which you want to interact with.
Source: https://developer.android.com/training/basics/intents/package-visibility
Hello I am still fairly new to this, so please help me understand why I am having this error. I have tried many solutions, so I'm just going to list everything I've done since I can't seem to understand why this is happening.
I created a project that integrates GoogleMaps at min SDK 21 to target/compile at SDK 28. I did call the permissions needed inside the Manifesto.
I created a file that extends the MapFragment class and everything seems to be working fine. I am able to check and request permission for the user's location (the box does show up), but when I called the onRequestPermissionResult method it is shown differently and gives me an error saying "error: cannot find symbol class NonNull":
#Override
public void onRequestPermissionsResult(int requestCode, #androidx.annotation.NonNull String[] permissions, #androidx.annotation.NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
In my other Fragment (android.support.v4.app) classes the #androidx.annotation.NonNull is #NonNull instead. I first thought maybe I needed to add implementation 'com.android.support:support-annotations:28.0.0' to the build.gradle, but that wasn't the case. I then tried to just replace the #androidx.annotation.NonNull with #NonNull which made the error go away, but whenever I clicked allow or deny it wasn't hitting the onRequestPermissionResult method.
I created a method that checks for a permission, but it won't let me use requestPermission on its own without checking if the build is greater or equal to SDK 23, but my min SDK is 21. So instead I just checked if the build is greater or equal to 21 and used ActivityCompat to get the requestPermission method and it works. It will check and ask for permission, so I'm thinking maybe the onRequestPermissionResult only works in the MainActivity which is what I don't want. I want to be able to call a method after checking if the request was granted inside the MapFragment. Is it because MapFragment isn't supported with android.support.v4.app? It looks like this:
private boolean checkAskPermission(int requestCode, String permissionString){
if(Build.VERSION.SDK_INT >= 21){
int permission = ContextCompat.checkSelfPermission(mContext, permissionString);
if(permission!=PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(getActivity(), new String[]{permissionString}, requestCode);
return false;
}
}
return true;
}
At this point I don't know what else to try. I thought maybe I wasn't checking the permission correctly inside onRequestPermissionResult when I change #androidx.annotation.NonNull to #NonNull to be able to use it, but the method doesn't hit when I use a break on it.
Please leave detail responses, so I can fully understand my problem. I have been stuck on this for a day.
Edit: Solution
private boolean checkAskPermission(int requestCode, String permissionString){
if(Build.VERSION.SDK_INT >= 23){
int permission = ContextCompat.checkSelfPermission(mContext, permissionString);
if(permission!=PackageManager.PERMISSION_GRANTED){
requestPermissions(new String[]{permissionString}, requestCode);
return false;
}
}
return true;
}
and just changed the #androidx.annotation.NonNull to #NonNull and now it hits the method.
Thanks to Eugene for clearing up SDK permissions. Only SDK 23 and higher require permission.
import that annotation class:
import androidx.annotation.NonNull;
which is either coming from this dependency:
implementation "androidx.annotation:annotation:1.0.0"
or from this dependency, in case not yet using androidx:
implementation "com.android.support:support-annotations:28.0.0"
then you can use it as usual:
#NonNull
The only work-around that I found useful was to fore-go the react-native run-android command and manually go into the react-native library (for me: ProjectHome/node_modules/ModuleIWantToChange/android/src/main/java/FileToChange.java) that was importing androidx.annotation.NonNull, changing that dependency to android.support.annotation.NonNull and then making sure to compile with the com.android.support:support-annotations under "dependencies" in that node_module's "android/build.gradle" file. In my case, I'm using version "com.android.support:support-annotations:25.3.1". You'll have to make sure that you have the version you call out here. Look at what you have installed with Android Studio under $ANDROID_HOME/extras/android/m2repository/com/android/support/support-annotations.
Then, instead of react-native run-android, I moved to the android directory under the project's home directory and ran sudo ./gradlew --stacktrace installDebug (stacktrace is optional) to build and install the apk on my emulator/device.
This might not work for everyone, but it was a tolerable fix in my case.
[...] but the method doesn't hit when I use a break on it.
[...] used ActivityCompat to get the requestPermission [...]
You're in a fragment. Don't ask the activity for permissions. If you ask the fragment you also get callback in the fragment.
requestPermissions(new String[]{permissionString}, requestCode);
Runtime permissions were introduced in API 23 (that's why the method is only available since API 23). Fix your condition. Before that having the permission declared in manifest is enough.
Off-topic: Platform fragments have been deprecated. Use support fragments instead. Extend SupportMapFragment.
#androidx.annotation.NonNull cannot be found because you don't use AndroidX in your project. You can use whatever other #NonNull annotation is available (either from the SDK or from the support library).
In android i want to open android notification quick setting in android program after a lot of searching from all resources found code for opening notification bar only.
Note that this code relies on non-public API's. It is not guaranteed to run correctly on all Android devices.
try {
Object service = getSystemService("statusbar");
Class<?> statusBarManager = Class.forName("android.app.StatusBarManager");
// expands the notification bar into the quick settings mode
// - replace expandSettingsPanel with expandNotificationsPanel
// if you just want the normal notifications panel shown
Method expand = statusBarManager.getMethod("expandSettingsPanel");
expand.invoke(service);
} catch (Exception e) {
// do something else
}
Don't forget the required manifest permission:
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />