I'm trying to open settings on NFC Tap & Pay page with this piece of code:
startActivity(new Intent(Settings.ACTION_NFC_PAYMENT_SETTINGS));
While testing on LG Nexus 5X with Android 7.1.2 I have received this crash:
android.content.ActivityNotFoundException:
No Activity found to handle Intent { act=android.settings.NFC_PAYMENT_SETTINGS }
at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1809)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1523)
at android.app.Activity.startActivityForResult(Activity.java:4228)
at android.support.v4.app.BaseFragmentActivityJB.startActivityForResult(SourceFile:50)
at android.support.v4.app.FragmentActivity.startActivityForResult(SourceFile:79)
at android.app.Activity.startActivityForResult(Activity.java:4186)
at android.support.v4.app.FragmentActivity.startActivityForResult(SourceFile:859)
at android.app.Activity.startActivity(Activity.java:4525)
at android.app.Activity.startActivity(Activity.java:4493)
at ...
Well, this crash can be handled easilly with try-catch but what is wierd, when I open this NFC settings manually, code works like a charm - no crash. Why? Does anyone have an explanation for this behavior?
In documentation[1] is written this:
In some cases, a matching Activity may not exist, so ensure you
safeguard against this.
Is it possible that they meant this sentence like "you have to open settings manually, then it works fine"?
[1] https://developer.android.com/reference/android/provider/Settings.html#ACTION_NFC_PAYMENT_SETTINGS
From: https://developer.android.com/reference/android/provider/Settings.html#ACTION_NFC_PAYMENT_SETTINGS
ACTION_NFC_PAYMENT_SETTINGS
added in API level 19
String ACTION_NFC_PAYMENT_SETTINGS
Activity Action:
Show NFC Tap & Pay settings
This shows UI that allows the user to configure Tap&Pay settings.
In some cases, a matching Activity may not exist, so ensure you safeguard against this.
Input: Nothing
Output: Nothing
Constant Value: "android.settings.NFC_PAYMENT_SETTINGS"
ACTION_NFC_PAYMENT_SETTINGS is not supported by your device or can at least not be handled.
Update 1
Since your minAPILevel is 19, the action should be supported by the android RT. However, it is possible, that the link between the action and the NFC settings-menu, ALTHOUGH the menu exists, is not or can not be established.
Try to use Settings.ACTION_NFC_SETTINGS as the action and see if it starts.
If so, I'd expect an implementation issue.
To guard against the exceptions, I'd recommend using:
PackageManager packageManager = getActivity().getPackageManager();
if (intent.resolveActivity(packageManager) != null) {
startActivity(<your intent>);
} else {
Log.d(TAG, "No application available to handle requested action.");
}
See: How to check if an intent can be handled from some activity? for credit and reference.
Related
My Goal is here is to set my app as default launcher on Huawei devices.
1 - Explanations:
1.1 - Current situation:
I am already able to:
Check if my app is the default launcher
Display the 'launcher picker' (with the 'use once' / 'always' choice)
This all works fine.. except on Huawei devices!
From my point of view, Huawei's Android flavor does not properly 'honor' the "ACTION_MANAGE_DEFAULT_APPS_SETTINGS" intent action contract.
// this displays the list of default apps on all tested devices, except on Huawei devices!
// instead, it does display apps permissions, app links and apps'advanced settings
intent.setAction(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS);
activity.startActivity(intent);
As a B Plan, I am able to display the 'Applications & Notifications' settings 'page' using this:
String packageName = "com.android.settings";
String className = "Settings$AppAndNotificationDashboardActivity";
intent.setClassName(packageName, packageName + "." + className);
activity.startActivity(intent);
So the user can navigate from there, pressing this sequence of menu items:
-> Advanced Parameters ( expandable menu item : not present on tablet, and not sure it's present on phone)
-> Default Apps
-> Default Launcher
This requires 2 or 3 steps that I would like to avoid.
1.2 - This can be improved!
I found out that when the "-> Default Apps" menu item is selected, a (com.android.settings, .SubSettings) Intent (with extra) is launched but I was not able to make this works (permission denial).
But I installed Nova Launcher and it turns out it's able to display the "-> Default Apps" settings page on Huawei devices!
So the user land on a page where she/he only has to tap on "-> Default Launcher" then choose a default launcher: much easier.
2 - Questions:
As I think it's just not possible to display the 'Lancher Picker' on Huawei devices, here is my question:
How can I display the "-> Default Apps" settings page (image down here) on Huawei devices (like Nova Launcher does)?
Are they using another intent action on Huawei devices?
Thanks beforehand your help.
Yes on Huawei devices, Nova uses a different intent to open to the correct screen. I likely found this by using apktool on the Settings.apk pulled from a Huawei device and looking at the AndroidManifest.
Note that "com.android" is always a code smell as it means it's not part of the public API. Also this isn't even really "com.android" as it doesn't exist on AOSP and com.android.settings.PREFERRED_SETTINGS is purely a Huawei invention. It's very likely that some Huawei devices won't have this at all. It's also possible that in the future this intent might continue to work but not do what it currently does. So handle it carefully.
/* Some Huawei devices don't let us reset normally, handle it by opening preferred apps */
Intent preferredApps = new Intent("com.android.settings.PREFERRED_SETTINGS");
preferredApps.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
if (pm.resolveActivity(preferredApps, 0) != null) {
context.startActivity(preferredApps);
} else {
...
}
In fact, the accepted answer is not 100% correct, because it opens a general default apps chooser activity.
It works, but it's better to bring user right to the launcher chooser activity — it's com.google.android.permissioncontroller/com.android.packageinstaller.role.ui.HomeSettingsActivity (at least for the Android 10 Huawei Honors).
So, the correct code snippet is:
Intent()
.apply {
component = ComponentName("com.google.android.permissioncontroller", "com.android.packageinstaller.role.ui.HomeSettingsActivity")
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
}
.takeIf {
packageManager.resolveActivity(it, 0) != null
}
?.let(context::startActivity)
I'm looking for a way to open the battery settings screen from an android app.
So far I found the two intents :
Intent.ACTION_POWER_USAGE_SUMMARY
Settings.ACTION_BATTERY_SAVER_SETTINGS
but none of them open this screen.
I was wondering if anyone knows of such a way. It sounds strange that an intent for something so simple doesn't exist
Settings.ACTION_BATTERY_SAVER_SETTINGS on "plain" Android versions will show the settings page you want to show.
Intent.ACTION_POWER_USAGE_SUMMARY will lead to the overview page showing the battery consumption.
Some manufactures such as Samsung build their own implementation over the system one, e.g. in this the "Battery" page. On Samsung devices, you can call this by calling the SmartManager interface directly. An code example:
if (Build.MANUFACTURER == "samsung") {
val intent = Intent()
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
intent.component = ComponentName("com.samsung.android.lool", "com.samsung.android.sm.ui.battery.BatteryActivity")
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
intent.component = ComponentName("com.samsung.android.sm", "com.samsung.android.sm.ui.battery.BatteryActivity")
}
try {
activity?.startActivity(intent);
} catch (ex: ActivityNotFoundException) {
// Fallback to global settings
startActivity(Intent(Settings.ACTION_SETTINGS))
}
} else {
startActivity(Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS))
}
It can be the case that you need additional cases for Huawei or Xiaomi as well.
Huawei can be "com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity"...
...and the MIU based ones "com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity"
I know this is quite old. But a trick I use is going to the appropriate settings screen in the device settings and then while connected to the phone run:
adb shell
dumpsys window windows | grep -E 'mCurrentFocus'
This returns the package name and Activity name currently in focus.
Using that I can check in code if the intent is callable. If it is, I launch it. If it isnt, I might have better luck with a different screen that is near by or explain to the user he needs to do something manually etc... Obviously the more devices you have, the more Intents you can create and check at run time. Im sure there is a list of Intents for different devices online.
On our application there's a service that is normally started during Application.OnCreate (directly calling context.startService) and also later on via AlarmManager (refactor is in progress to migrate some of its work to JobScheduler).
Our application also have a BroadcastReceiver that gets launched with its direct intent.
Given the new limitations in Android Oreo (https://developer.android.com/about/versions/oreo/android-8.0-changes.html) we're having an issue as follows:
app/process is in background/dead
BroadcastReceiver gets fired by the OS
Application.onCreate() executes before the BroadcastReceiver
Application.onCreate() code tries to run the Service
this leads to crash with "IllegalStateException: Not allowed to start service Intent".
I'm aware of the new recommended ways of launching a Service as answered by CommonsWare here https://stackoverflow.com/a/44505719/906362, but for this specific case, I simply want to have if(process in foreground) { startService }. I'm currently using the following method and it seems to work:
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static boolean isProcessInForeground_V21(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.AppTask> tasks = am.getAppTasks();
return tasks.size() > 0;
}
But I can't find the exact checks Android Oreo is doing (I got as far as here https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ContextImpl.java on the startServiceCommon method, but from there requireForeground flag seems to go to some native implementation)
So my question:
For the specific purpose of Android Oreo new limitations, how to check if my process is foreground before calling startService?
To continue your investigation: (TL;DR: see between horizontal lines at the bottom)
Disclaimer, I don't know too much about Android, I just like digging in the source code.
Note: you can also navigate the code in Android Studio if you jump to file instead of class:
or searching for text in Project and Libraries.
IActivityManager is defined by AIDL, that's why there are no sources for it:
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/app/IActivityManager.aidl#145
Based on how AIDL needs to be implemented I found that ActivityManagerService extends IActivityManager.Stub (God bless Google indexing).
Note I also found this, which might be an interesting read if you're really interested how things work internally.
https://programmer.group/android-9.0-source-app-startup-process.html
ActivityManagerService sources reveal that in Oreo startService is forwarded to ActiveServices which is located in the same package.
Assuming we're looking for an exception like this:
java.lang.IllegalStateException: Not allowed to start service Intent {...}: app is in background uid UidRecord{af72e61 u0a229 CAC bg:+3m52s273ms idle procs:1 seq(0,0,0)}
we have to continue down the rabbit hole: requireForeground gets assigned to fgRequired parameter and the message is here. The condition to allow this depends on the start mode returned by ActivityManagerService.getAppStartModeLocked(packageTargetSdk = 26 or greater, disabledOnly = false, forcedStandby = false).
There are 4 start modes:
APP_START_MODE_NORMAL (needs to be different than this, i.e. !=)
APP_START_MODE_DELAYED (this is ok, i.e. return null)
APP_START_MODE_DELAYED_RIGID
APP_START_MODE_DISABLED
Ephemeral apps will immediately return APP_START_MODE_DISABLED, but assuming this is a normal app, we end up in appServicesRestrictedInBackgroundLocked.
Note: this is where some of the whitelist mentioned in https://stackoverflow.com/a/46445436/253468 is decided.
Since all branches but last return APP_START_MODE_NORMAL, this redirects to appRestrictedInBackgroundLocked where we find our most likely suspect:
int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
// Apps that target O+ are always subject to background check
if (packageTargetSdk >= Build.VERSION_CODES.O) {
return ActivityManager.APP_START_MODE_DELAYED_RIGID;
}
So the reason for denial is simply targeting O. I think the final answer to your question of how the OS decides if your app is foreground or background is this condition in getAppStartModeLocked
UidRecord uidRec = mActiveUids.get(uid);
if (uidRec == null || alwaysRestrict || uidRec.idle) {
My guess is that a missing record means it's not running (but then how is it starting a service?!), and idle means it's backgrounded. Notice that in my exception message the UidRecord is saying that it's idle and has been backgrounded for 3m52s.
I peeked into your getAppTasks and it's based on TaskRecord.effectiveUid, so I'm guessing that's quite close to listing UidRecords for your app.
Not sure if this helps, but I'll post it anyway, so if anyone wants to investigate more, they have more info.
The issue is that I need to install an apk(non market app) and for this, the user need to activate the unknown source setting, so i send him (if he didn't have it activated) to the settings so he can turn on the option, the issue is that i tested it in different phones and in samsung that option is on applications while in htcs phones is on security. i want send the user to that option but i don't know how to do it
I read about this and no one knows exactly how to do it
this is my code
int canInstallFromOtherSources = Settings.Secure.getInt(ctx2,Settings.Secure.INSTALL_NON_MARKET_APPS);
if(canInstallFromOtherSources == 0)
{
Intent intentSettings = new Intent();
intentSettings.setAction(android.provider.Settings.ACTION_APPLICATION_SETTINGS);
startActivity(intentSettings);
}
You can do it with the following line (changing to the corresponding action):
startActivityForResult(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS), REQUEST_CODE_ENABLE_LOCATION_PROVIDERS);
Check Android Settings documentation.
I think you should use ACTION_SECURITY_SETTINGS and one of ACTION_APPLICATION_SETTINGS or ACTION_APPLICATION_DEVELOPMENT_SETTINGS.
And here (line 304), you've got a working example of one of my apps: Tureame
My problem is the next :
When an user hold search's button on an android phone, and this, while a progress bar is in action, the following error message is displayed:
ERROR / AndroidRuntime ( 16794 ): java.lang. SecurityException: Requesting codes from com.google.android.voicesearch (with uid 10028) to be run in process com.xxxx.myApplication (with uid 10088)
Thus, further to this message I tried several things: kill the process ' com.google.android.voicesearch ' :
JAVA code :
if (event.getKeyCode() == KeyEvent.KEYCODE_SEARCH)
{
if (!event.isLongPress() && !Utils.getMyProgress().isShowing())
{
searchProducts();
}
else
{
android.os.Process.killProcess(android.os.Process.getUidForName("com.google.android.voicesearch"));
}
return true;
}
Unsuccessfully! Thus the idea is to prevent the process 'com.google.android.voicesearch' from being started
every time the user of the telephone maintainings for a long time the key(touch) "to look for" of its telephone (example on htc, this key(touch) exists. Rather, a physical and not tactile key(touch)!)
Maybe is it possible to block the launch of this process ('com.google.android.voicesearch') in the in the manifest.xml, while my application is launched :
manifest.xml :
<application
android:debuggable="false"
android:enabled="false"
android:killAfterRestore="false"
android:process="com.google.android.voicesearch">
</application>
Any idea ?
Thanks for answers !
In order to implement search with assistance from the Android system (to deliver search queries to an activity and provide search suggestions), your application must provide a search configuration in the form of an XML file called the searchable configuration. It configures certain UI aspects of the search dialog or widget and defines how features such as suggestions and voice search behave. This file is traditionally named searchable.xml and must be saved in the res/xml/ project directory.
According to this page, you only have to remove the voice entries from that file.