I am writing an Android launcher that does not support widgets, but it does support shortcuts. One of the shortcuts provided by AOSP is Direct dial, and my launcher needs the android.permission.CALL_PHONE permission for that. My question is, are there any other permissions that I need to add, to allow all possible shortcuts, even those provided by third party apps?
FOR NOUGAT SHORTCUTS ( API LEVEL 25+ )
There is not standard permission to add/launch shortcuts. If target api level of your app is 25+, you can use ShortcutManager or static shortcut via .xml meta-data.
https://developer.android.com/guide/topics/ui/shortcuts.html
FOR LEGACY SHORTCUTS ( BELOW API LEVEL 25 )
If you want to install&use Legacy shortcuts without user interaction, you need to declare INSTALL SHORTCUT permission.
Legacy shortcuts use Intent Action:
Create shortcut for Launcher: "android.intent.action.CREATE_SHORTCUT"
Install shortcut on Launcher: "com.android.launcher.action.INSTALL_SHORTCUT"
Required permission on AndroidManifest.xml:
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
You can find more resources when you search for intent actions above.
There is no way to know this in advance. Some apps just assume that the caller of their shortcuts have some permissions (e.g. some system launcher shortcuts often only work in the system launcher itself, as they sometimes require some self defined permission).
In general, any app that offers shortcuts, should run the code in itself instead of the calling app to be sure the required permissions are present, but apparently this is not the case in some apps (especially in launchers e.g.).
I face this problem in an app of mine every now and then as well and catch the exception and tell the user, that the selected shortcut does not support other apps and is implemented in a wrong way.
Example - shortcut to call someone that works and that does not work
E.g. think about a third party app that offers a direct call shortcut. It can handle this in 2 way:
wrong way
It can return an intent like following:
Intent intent = new Intent();
Intent launchIntent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + number);
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, launchIntent);
...
This intent can ONLY be run by an app that has the action call permission
correct way
The app knows, that the caller may not have the call phone permission, so it does not return the direct phone call intent directly, but a custom one that it handles itself like e.g.
Intent.ShortcutIconResource icon = Intent.ShortcutIconResource.fromContext(this, R.mipmap.icon);
Intent intent = new Intent();
Intent launchIntent = new Intent(this, MyPhoneCallActivity.class);
launchIntent.putExtra("number", number);
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, launchIntent);
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, getString(pause != null ? (pause ? R.string.shortcut_pause : R.string.shortcut_resume) : R.string.shortcut_toggle_pause_resume));
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, icon);
If the caller executes the shortcut, the MyPhoneCallActivity will be started - this runs inside the app itself and has all permissions of the shortcut provider. This activity then can simply execute the Intent.ACTION_CALL intent itself and finishes itself afterwards. This way, the calling app does not need any special permissions. The workaround via an activity is one solution for this problem that works.
This is not a definitive answer, as I couldn't find this explicitly stated anywhere, but it seems that only phone call shortcuts require a permission, so the CALL_PHONE permission is the only one you need, to launch shortcuts.
AOSP launcher only checks for the CALL_PHONE permission. Source: https://android.googlesource.com/platform/packages/apps/Launcher3/+/master/src/com/android/launcher3/Launcher.java#1630
I haven't been able to find any other types of shortcuts that require permissions.
Related
I want to enable floating notification using Android Code.Normally users didn't know about the setting. so i need to enable this as default.
Bad news I'm afraid.
As you probably are aware, this requires the permission SYSTEM_ALERT_WINDOW.
Since Android M google has begun locking down this permission to reduce clutter. What is a little unusual about this permission is it requires the user to go to an actual settings screen The ordinary Android M permission flow does not work for this. To quote the API:
If the app targets API level 23 or higher, the app user must explicitly grant this permission to the app through a permission management screen
You use the Settings class to check if you already have the permission and when you don't, you need to explain and direct the user to the relevant settings screen via intent:
Intent i = new Intent();
i.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
i.addCategory(Intent.CATEGORY_DEFAULT);
i.setData(Uri.parse("package:" + context.getPackageName()));
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
i.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
context.startActivity(i);
This should only ever affect devices running 23+ as older devices should get the permission automatically, but don't rely on checking SDK_INT, rely instead on canDrawOverlays, as there are exceptions for some pre-marshmallow devices
I was also facing same issue and need to enable it from settings but after adding permission in manifest file it worked perfectly.
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
Tested on version 9.
I struggled with that and found a way.
(In my case I'm using the OneSignal React-Native SDK)
The solution was to create a "category" (on the OneSignal console) that has the "urgent" importance :)
After that, when you send a push, you have to refer to the channel_id of this category.
Doc: https://documentation.onesignal.com/docs/android-notification-categories
I am looking to start a specific application(Dictionary app) from my app. Using intents, how would I go about launching that specific app and use it to look up the word.
There are implicit intents and explicit intents, you want an explicit intent to get your desirable.
Here is how you can do it.
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.example", "com.example.MyExampleActivity"));
startActivity(intent);
in the setComponent method your Dictionary app information should go in.
CommonsWare addition to this answer,
Using an explicit Intent to talk to a third-party app is rarely the right thing to do. This code will break if the activity is not exported, or requires permissions, or the developer refactors the code and changes the class name or package, etc.
If the author of the app is documenting that your recipe is the correct way to work with that activity on that app, then that is fine, as the developer presumably intends to support this use case.
Background
Android apps can gain some special rights using admin policies, like turning the lock screen on.
As such, those apps cannot be uninstalled till the user has revoked their admin rights.
I wish to have a function that will check if a given app (given a package name of the app) has admin rights, and if so, i will open the admin settings page of this app, to allow the user to revoke its rights.
The problem
As it turns out, there is already a function that does it.
First you get an instance of the DevicePolicyManager :
DevicePolicyManager dpm=(DevicePolicyManager)context.getSystemService(Context.DEVICE_POLICY_SERVICE);
Then you are supposed to call isAdminActive , and get a result of true/false.
However, this function's parameter is ComponentName, meaning it needs not only the package name, but also the path to a specific class of the app.
An alternative would be to query all active admin apps, using getActiveAdmins.
Later, I you can go to the admin page using:
final Intent intent=new Intent();
intent.setComponent(new ComponentName("com.android.settings","com.android.settings.DeviceAdminSettings"));
startActivity(intent);
or even more specifically, to enable/disable the app admin rights :
final Intent intent=new Intent();
intent.setComponent(new ComponentName("com.android.settings","com.android.settings.DeviceAdminAdd"));
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,componentName);
activity.startActivity(intent);
However, both of those ways seems a bit hacky, as they do not use the official API of android, that could change over android versions and roms.
The question
What's the best way to check if a given app (package name) has admin rights?
What's the best way to go to the admin page ? is there a safer way to go there? maybe a fallback mechanism from what i've done to the security screen intent (which for some reason I can't find in the API) ?
The answer
After reading the answer on this post, I've decided to share the findings:
If you wish to be safe, you can tell the user to go to the admin screen by himself, and only guide him to the security screen:
intent=new Intent(Settings.ACTION_SECURITY_SETTINGS);
context.startActivity(intent);
however, i think the next way should work for all devices:
Intent intent=new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
final PackageManager packageManager=context.getPackageManager();
final List<ResolveInfo> resolveInfos=packageManager.queryIntentActivities(intent,0);
if(resolveInfos!=null&&!resolveInfos.isEmpty())
try
{
final ResolveInfo resolveInfo=resolveInfos.get(0);
intent=new Intent();
intent.setComponent(new ComponentName(resolveInfo.activityInfo.packageName,resolveInfo.activityInfo.name));
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,componentNameResult);
context.startActivity(intent);
return true;
}
catch(final Exception e)
{}
and if it fails, you can always use any of the methods i've written, and in the end, call the safe one.
Android apps can gain some special rights using admin policies, like turning on/off the screen.
A DevicePolicyManager can lock the screen with lockNow() and set the lock timeout via setMaximumTimeToLock(), if that's what you mean.
What's the best way to check if a given app (package name) has admin rights?
See if they are in the roster returned by getActiveAdmins(). That method returns null or List of ComponentName objects, and you can call getPackageName() on a ComponentName to compare it to your target package.
What's the best way to go to the admin page ?
There is no documented and supported way to get to this portion of the Settings app. The closest thing is ACTION_SECURITY_SETTINGS.
is it possible to call more packages in single project,without installing the other packages in emulator/phone,want single apk file..I Kept this code...but is possible when the package is available in emulator/phone..Please suggest me...if that package is not available in emulator/phone
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setComponent(new ComponentName(
"com.abc.def.packname",
"com.abc.def.packname.MyActivity"));
startActivity(intent);
To be able to use an Intent to launch any Activity, there must be an Activity available to Handle it. This applies for both direct intents that target one particular Activity, and more general intents which would result in a Chooser dialog, like the share intent.
So in short, yes, the package you are referencing must be installed as well, or else your app will crash. If you have access to the source code of the other project, you could combine them into one. If this isn't possible, you could request that the user installs the other app when your app starts.
I have an application say A that have all the permissions enabled at installation. Another app Say B don't have a permission and want to get that permission. Can B communicate with A, So that A can transfer its permission to B.
PLS reply, I'm stuck here. I want to get some permissions dynamically. Is this the best idea or any other idea?
As far as I know, apps can't necessarily give permissions to other apps, BUT AppB could inherit permissions from AppA IF you are the developer of both apps. If both AppA and AppB declare the same sharedUserId value in their manifest (android:sharedUserId="xyz") AND both AppA and AppB are signed with the same signature, then Android will consider them to be the same app as far as permissions go. So, AppB could exist on the device without permission "perm1" for example. Then, AppA could be installed with "perm1". IF AppA and AppB have the same sharedUserId and signature then, when AppA is installed, AppB will be "granted" "perm1".
I haven't tested this just now, but I know it used to work (as of a year ago or so).
That would be quite in-secure, don't your think, if an application could give any permissions to another application...
Some evil-doer would just have to convince you to install his A application ; and, then, no matter what other B application you'd install, that B application wouldn't have to request any specific permission at installation (those would later be granted by A) -- and B would still be able to do anything on your device ?
I sure hope what you're asking is not possible ;-)
Your application A can provide some Content Providers to access information. Application B could use the content provider of A to gain the information. http://developer.android.com/guide/topics/providers/content-providers.html
But somehow this sounds like you want to do something evil. If you like to have more information please provide more about your need to do that!
Yes this is possible in a roundabout way using PendingIntents. This is not an exact code snippet but should give you the idea:
You cannot transfer the permissions, but you can transfer capabilities to perform certain actions from A to B. Let's say you want to transfer the capability of executing a certain ACTION.
A needs to create a pending intent:
Intent intent = new Intent(ACTION);
PendingIntent pIntent = PendingIntent.getActivity(context, requestCode, intent, flags);
A sends this to B by marshalling the pending intent
Intent intent = new Intent(context, B.class);
intent.putExtra("pendingIntent", pIntent);
startActivity(intent);
At B we deserialize the pending intent and we can use it to perform the restricted ACTION
PendingIntent pIntent = intent.getExtra("pendingIntent");
pIntent.send();