Android Lollipop know if app as Usage Stats access - android

Since Android Lollipop, we have now an API for accessing apps usage stats. However, your app must be granted those permissions by the user.
I know that for redirecting the user to those settings using Settings.ACTION_USAGE_ACCESS_SETTINGS.
Now, my question is how do you know the user has granted you those permissions so that you can stop redirecting him to the settings.
Thanks!

you can simply query usagestats with daily interval and end time the current time and if nothing is returned this means the user hasn’t granted permissions
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public boolean doIHavePermission(){
final UsageStatsManager usageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
final List<UsageStats> queryUsageStats = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, 0, System.currentTimeMillis());
return !queryUsageStats.isEmpty();
}
Daily interval with start date 0 and end date the current time must at least return todays usage.So it will be empty only if permissions are not granted.

Check this answer:
Tomik's answer
If you hurry, here's the solution ;)
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static boolean usageAccessGranted(Context context) {
AppOpsManager appOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(), context.getPackageName());
return mode == AppOpsManager.MODE_ALLOWED;
}

I stumbled on the same problem. On Samsung S5 Lollipop usage stats did not work with the following code:
Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
However usage stats actually exist. With the following code one can open the security settings:
Intent intent = new Intent(Settings.ACTION_SECURITY_SETTINGS);
intent.setComponent(new ComponentName("com.android.settings","com.android.settings.Settings$SecuritySettingsActivity"));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
An the scroll to the bottom and there is usage stats. I also inspeced logs and by pressing usage stats, you are directed to SubActivity which contains UsageStats fragment. I tried the following code:
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.settings", "com.android.settings.SubSettings"));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
But got security exception. The problem is that they didnt mark SubActivity as exported, so as far as I know its not possible to directly start SubActivity (usage stats window). The only solution is to take user to Securiy settings and them tell him to manually go to usage stats view and enable app.
If someone finds better solution it would be great!

See ActivityNotFoundException in Lollipop when trying to launch activity with intent android.settings.USAGE_ACCESS_SETTINGS
for a better way, since with method we cannot discern whether there are simple no stats for the time interval

Related

Android Studio - showing new activity over other apps results in a blackscreen

I'm trying to develop an android app for the SDK version 30 that (when a button is clicked) starts listening to what apps are opened on the phone. If it detects the user opening Whatsapp, it is supposed to show a LockScreen activity over Whatsapp that makes you answer a math question before being able to use Whatsapp.
I know this can be done as their are apps like QualityTime or Forest that have similar features to restrict you from using certain apps, but I am a newbie when it comes to programming (probably obvious from my code) and feel totally stuck.
I have already figured out how to detect what app the user opened in the last second with code from stack overflow:
public String getCurrentApp() {
String topPackageName = "None";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
UsageStatsManager mUsageStatsManager = (UsageStatsManager) getSystemService("usagestats");
long time = System.currentTimeMillis();
List<UsageStats> stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 1, time);
// Sort the stats by the last time used
if (stats != null) {
SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
for (UsageStats usageStats : stats) {
mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
}
if (!mySortedMap.isEmpty())
{
topPackageName = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
}
}
}
return topPackageName;
}
I have another function that is started when the user clicks the button in my app to "activate" the listening process. This function keeps checking if the user opens Whatsapp and is then supposed to display the Lockscreen activity on top:
public void startListening(View view)
{
System.out.println("Lock activated.");
while (activated) {
String currentlyRunningApp;
currentlyRunningApp = getCurrentApp();
if (currentlyRunningApp.equals("com.whatsapp"))
{
System.out.println("Whatsapp detected. Showing Lockscreen...");
Intent i = new Intent(this,LockScreen.class);
startActivity(i);
}
try {
Thread.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
(All of the code I have shown is in my MainActivity btw.)
I have the following permissions granted to my app:
android.permission.PACKAGE_USAGE_STATS (for the getCurrentApp() function)
android.permission.SYSTEM_ALERT_WINDOW (as suggested here)
android.permission.ACTION_MANAGE_OVERLAY_PERMISSION (although I am not sure I even need this one)
My problem is, that instead of showing the Lockscreen activity I created, it only shows a blackscreen for the user. The Lockscreen activity itself works fine if I let the user open it through a button on the mainActivity, so the issue seems to really be that I can not properly show an activity if my app is running in the background and I want to display it on top of Whatsapp.
I have tried to look through similar questions, but all of the posts on here with similar use cases seem to be very old and outdated (i.e. this or this), as the newer versions seem to have way tighter security restrictions.
I also tried to do it with a screen overlay instead of an activity (using this source), but this doesn't even give me a blackscreen - just does nothing...
I am also aware that there are probably better ways to program the whole "listening and checking for whatsapp" part - i.e. with a service instead of a while-loop or something, but I only found out about services while researching this problem and I'd like to fix the blackscreen issue first.
After lots of trial and error I figured out that the issues was indeed caused by a missing permission, but one that I could not find on any stack overflow answer related to black screen problems. On top of that, I believe it's an issue that only occurred because I used a Xiaomi device for testing.
There are currently two separate permissions for displaying screens over other apps that you will need to grant:
Display over other apps, also called Display pop-up window. This is the android.permission.ACTION_MANAGE_OVERLAY_PERMISSION that I wasn't sure was even needed. So to emphasize, I definetly do need this permission.
Display pop-up windows while running in the background. This is the permission I was missing.
After I allowed them both (which you can do under Settings > Apps > Manage Apps > Your App > Other Permissions) everything worked fine.
To direct the user directly to the settings menu where they can allow these permissions, I used the code from this stack overflow answer. This is also where I got the info that it's a xiaomi-specific "issue".

How to Allow notification sound programmatically in Xiomi devices

Notification sound settings always disable in Xiomi devices. Check below image.
I want to enable sound programatically. Found similar stackoverflow questions but nothing helped.
Device : Redmi Note 5 pro, Redmi Note 9 pro
OS : MIUI 11 , MIUI 12
Note: But it works fine in all other devices. Problem only in Xiomi devices
I was looking for the same kind of issue 2 months ago, due to restrictions by Xiaomi it has become a lot harder, basically you have to have your app running without restriction in save battery settings and to bring the user to autostart where he will have to click on the toggle button to add your app.
Prompt the user to disable battery optimization for your app:
public class MainActivity extends AppCompatActivity {
#Override
protected void onStart()
{
String packageName = "com.example.myapp";
PowerManager powerManager = (PowerManager)getApplicationContext().getSystemService(POWER_SERVICE);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Intent i = new Intent();
if (!powerManager.isIgnoringBatteryOptimizations(packageName)) {
i.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
i.setData(Uri.parse("package:" + packageName));
startActivity(i);
}
}
}
}
For an app to use this, it also must hold the Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS permission
You also have to prompt the user to add your app to autostart (original credit) :
String manufacturer = "xiaomi";
if (manufacturer.equalsIgnoreCase(android.os.Build.MANUFACTURER)) {
//this will open auto start screen where user can enable permission for your app
Intent intent1 = new Intent();
intent1.setComponent(new ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity"));
startActivity(intent1);
}
It should now be working, let me know if it helped!
You may try to use NotificationManager.IMPORTANCE_MAX in new NotificationChannel

Change Default SMS App handler Android Q RoleManger

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
RoleManager roleManager = context.getSystemService(RoleManager.class);
// check if the app is having permission to be as default SMS app
boolean isRoleAvailable = roleManager.isRoleAvailable(RoleManager.ROLE_SMS);
if (isRoleAvailable){
// check whether your app is already holding the default SMS app role.
boolean isRoleHeld = roleManager.isRoleHeld(RoleManager.ROLE_SMS);
if (isRoleHeld){
Intent roleRequestIntent = roleManager.createRequestRoleIntent(RoleManager.ROLE_SMS);
((AppCompatActivity)context).startActivityForResult(roleRequestIntent, ConstantsValues.REQUEST_CODES.REQUEST_RESET_SMS_HANDLER);
}
}
}else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
Intent intent = new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT);
intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, Custom_SharedPreference.getInstance(context).getDefaultSMSPackage());
((AppCompatActivity)context).startActivityForResult(intent, ConstantsValues.REQUEST_CODES.REQUEST_RESET_SMS_HANDLER);
}
This is working fine when Change SMS App to MyApp default handler but When I change MyApp to again Default SMS App then its not working and activityResult return 0 Activity.Cancle.
This is happend only Android Q other Versions working fine.
In short, it's not possible to revert default SMS app.
As a work-around I decided to open the previous default SMS app using this code:
runIntent = getPackageManager().getLaunchIntentForPackage(newDefaultSmsApp);
Having the same issue, I tried various workaround, like using old method to request a change of default SMS app, however the OS doesn't care at all. Using RoleManager, when requesting to "revert" the role back to default/previous SMS app, the logs shows something like this:
2021-06-18 08:23:06.246 1770-5101/? I/ActivityTaskManager: START u0 {act=android.provider.Telephony.ACTION_CHANGE_DEFAULT cmp=com.google.android.permissioncontroller/com.android.permissioncontroller.role.ui.RequestRoleActivity (has extras)} from uid 10043
2021-06-18 08:23:06.246 1770-5101/? W/PermissionPolicyService: Action Removed: starting Intent { act=android.provider.Telephony.ACTION_CHANGE_DEFAULT cmp=com.google.android.permissioncontroller/com.android.permissioncontroller.role.ui.RequestRoleActivity (has extras) } from ccc71.sb (uid=10043)
Note the Action Removed on second line.
So in summary, it's only possible to request the role for self, but impossible to revert. A real bummer for backup apps that Google is making look bad, like so many other apps these days.

DevicePolicyManager.enableSystemApp() method does not work as expected

I'm trying to use enableSystemApp method to activate default system apps after provisioning device with the app that is set to device owner mode.
There are two methods to do this:
1) void enableSystemApp (ComponentName admin, String packageName) - in this case you need to pass package name explicitly as String. It works fine, the app gets enabled.
For example, calling this
devicePolicyManager.enableSystemApp(deviceAdminComponent, "com.google.android.gm");
enables default Gmail client, which is disabled after provisioning.
2) int enableSystemApp (ComponentName admin, Intent intent) - in this case, you need to pass an implicit intent and Android should enable all system apps that match this intent. In addition, this method returns int number of apps that match the intent. And here's the problem - I can't get this method to work, it always returns 0 and doesn't enable anything.
Here's the snippet I'm trying to use:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_APP_EMAIL);
int i = devicePolicyManager.enableSystemApp(deviceAdminComponent, intent);
It does not work and i == 0 in this case. What am I doing wrong?
Any help is appreciated!
Under the hood, the method that accepts an intent queries to get the list of activities that respond to that intent and then loops through the list passing in the package name string to enable the package. It's similar to doing this:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_APP_EMAIL);
List<ResolveInfo> infoes = getPackageManager()
.queryIntentActivities(intent, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
for (ResolveInfo info in infoes) {
devicePolicyManager.enableSystemApp(deviceAdminComponent, info.activityInfo.packageName);
}
Since you are able to enable the app using the package name string, the fault most likely lies in the way the intent is being resolved - which is supported by the fact that it always returns 0.
It is counter-intuitive, but my suspicion is that the application does not resolve the ACTION_MAIN intent because the app is disabled. Have you tried a less generic intent? I would try the following
Intent i;
// #1
// This goes full circle, but I expect it should work
i = getPackageManager().getLaunchIntentForPackage("com.google.an‌​droid.gm")
// #2
i = new Intent(Intent.ACTION_SEND).setPackageName("com.google.android.gm");
// #3
// Generic, but should resolve _all_ email apps - not just the default one.
// The mailto schema filters out non-email apps
i = new Intent(Intent.ACTION_VIEW , Uri.parse("mailto:"));
Option #1 and #2 are more academic. Both require the package name at which point you may as well use the string overload of enableSystemApp. Option #3 is my best guess for something generic that might still work, but it's possible that it still won't work because the app is disabled.
Note: I find it interesting that enableSystemApp only passes the MATCH_DIRECT_BOOT_AWARE and MATCH_DIRECT_BOOT_UNAWARE flags when querying activities that can resolve the intent, because the MATCH_DISABLED_COMPONENTS and MATCH_SYSTEM_ONLY flags seem much more relevant in this situation.

How can I change the Audio Mode (Silent, Normal) in Android 7.0 without Opening Setting Intent?

I want to change my android device audio mode from Normal to silent and silent to normal. `
AudioManager audio = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
if(!silent) {
audio.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
silent = true;
}
else {
audio.setRingerMode( AudioManager.RINGER_MODE_VIBRATE );
silent = false;
}
`
This is my code that i'm using. Perfectly running until Android 6 all versions.
But on Android 7+ it crashes.
I found a solution on web that opens a settings intent and get some permission from user then this code works fine. It is the code i used then
Intent intent = new Intent(
android.provider.Settings
.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
startActivity(intent);
But my project requirement is don't request to open a setting intent to allow it form there. Is should be work like getting user permission to change mode on same activity just like all other permissions i.e. location, read contacts etc.
I tried to find out the find how they implement the permission in the code file but did not find it. Please let me know if anyone know.
This is the only way to change the Audio Mode of Android with api level > 23
Intent intent = new Intent(
android.provider.Settings
.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
startActivity(intent);
Now officially its released by Android to get user permission to access Notification Policy of device to change the audio Mode.

Categories

Resources