Android M startActivity battery optimization - android

I'm developing an app that should alert an user if is near a place.
and of course have to do that also if the phone is in idle.
With DOZE now I understood that I have to whitelist my app, and to do that I saw that I can start an intent with the action request thanks to Buddy's answer in this post
Intent intent = new Intent();
String packageName = context.getPackageName();
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (pm.isIgnoringBatteryOptimizations(packageName))
intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
else {
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
}
context.startActivity(intent);
well this should be too easy...because google doesn't like this approach and if you do it, your app should be banned from the play store...no comment...
Ok so the way should be to drive the user to the battery settings and manually add your app in the DOZE's white list...yes this should be a big wall to climb...anyway seems to be the only way...now the answer is:
I Can use an intent to go to the power usage summary, in this way (thank you Chris):
Intent powerUsageIntent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
ResolveInfo resolveInfo = getPackageManager().resolveActivity(powerUsageIntent, 0);
// check that the Battery app exists on this device
if(resolveInfo != null){
startActivity(powerUsageIntent);
}
But how to directly go at the list of app for choosing the battery optimization?
Thanks for any answer.

To open list of apps for choosing battery optimization you can use this code sample:
private void openPowerSettings(Context context) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
context.startActivity(intent);
}
It doesn't need to have <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/> permission, so it should be ok to publish it to Google Play (for details please check this thread and comments to this question).
NOTE
Adding this line
intent.setData(Uri.parse("package:" + mContext.getPackageName()));
will cause crash "Fatal Exception: android.content.ActivityNotFoundException No Activity found to handle Intent { act=android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS dat=package:io.demo.example }". User has to find the app in the list, it seems no way to jump directly to our app.

This is what I use:
manifest:
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.PowerManager
import android.provider.Settings
import androidx.annotation.RequiresPermission
import androidx.core.content.ContextCompat
object PowerSaverHelper {
enum class WhiteListedInBatteryOptimizations {
WHITE_LISTED, NOT_WHITE_LISTED, ERROR_GETTING_STATE, IRRELEVANT_OLD_ANDROID_API
}
fun getIfAppIsWhiteListedFromBatteryOptimizations(context: Context, packageName: String = context.packageName): WhiteListedInBatteryOptimizations {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return WhiteListedInBatteryOptimizations.IRRELEVANT_OLD_ANDROID_API
val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager?
?: return WhiteListedInBatteryOptimizations.ERROR_GETTING_STATE
return if (pm.isIgnoringBatteryOptimizations(packageName)) WhiteListedInBatteryOptimizations.WHITE_LISTED else WhiteListedInBatteryOptimizations.NOT_WHITE_LISTED
}
//#TargetApi(VERSION_CODES.M)
#SuppressLint("BatteryLife", "InlinedApi")
#RequiresPermission(Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
fun prepareIntentForWhiteListingOfBatteryOptimization(context: Context, packageName: String = context.packageName, alsoWhenWhiteListed: Boolean = false): Intent? {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
return null
if (ContextCompat.checkSelfPermission(context, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) == PackageManager.PERMISSION_DENIED)
return null
val appIsWhiteListedFromPowerSave: WhiteListedInBatteryOptimizations = getIfAppIsWhiteListedFromBatteryOptimizations(context, packageName)
var intent: Intent? = null
when (appIsWhiteListedFromPowerSave) {
WhiteListedInBatteryOptimizations.WHITE_LISTED -> if (alsoWhenWhiteListed) intent = Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS)
WhiteListedInBatteryOptimizations.NOT_WHITE_LISTED -> intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).setData(Uri.parse("package:$packageName"))
WhiteListedInBatteryOptimizations.ERROR_GETTING_STATE, WhiteListedInBatteryOptimizations.IRRELEVANT_OLD_ANDROID_API -> {
}
}
return intent
}
}
Example:
PowerSaverHelper.prepareIntentForWhiteListingOfBatteryOptimization(this)?.let { startActivity(it) }

Try the below code to open Ignore Battery Optimization Settings page.
private void openPowerSettings() {
startActivityForResult(new Intent(android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS), 0);
}
No extra permissions are required to be added to the manifest file.

Related

Call from foreground service stops working when screen is off

I have the following code that makes a phone call:
public static void CallPhoneNumber(this Context context, string phoneNumber)
{
var uri = Android.Net.Uri.Parse("tel:" + phoneNumber);
var callIntent = new Intent(Intent.ActionCall, uri);
callIntent.AddFlags(ActivityFlags.NewTask);
callIntent.AddFlags(ActivityFlags.FromBackground);
context.StartActivity(callIntent);
}
I make a phone call inside a running foreground service. Basically the service detects conditions (in my case GPS location) and makes a phone call. It worked just fine with my Pixel 2XL and Android 9. But after upgrade to Android 10 I faced to a new problem.
First of all, I was forced to add a new permission FOREGROUND_SERVICE. Added, the foreground service works as expected and makes phone calls - but only when phone is "active", I mean it is not in a "sleep" mode when the screen is turned off.
If the screen is off - the service works, I can track the activity, but it doesn't make a phone call.
The adb logcat shows this warning (first line is Info, the second is Warning):
02-04 20:48:00.923 1315 7951 I ActivityTaskManager: START u0 {act=android.intent.action.CALL dat=tel:xxxxxxxxxxxx flg=0x10000004 cmp=com.android.server.telecom/.components.UserCallActivity} from uid 10174
02-04 20:48:00.924 1315 7951 W ActivityTaskManager: Background activity start [callingPackage: MyApp; callingUid: 10175; isCallingUidForeground: false; isCallingUidPersistentSystemProcess: false; realCallingUid: 10174; isRealCallingUidForeground: false; isRealCallingUidPersistentSystemProcess: false; originatingPendingIntent: null; isBgStartWhitelisted: false; intent: Intent { act=android.intent.action.CALL dat=tel:xxxxxxxxxxxx flg=0x10000004 cmp=com.android.server.telecom/.components.UserCallActivity }; callerApp: ProcessRecord{43f3a72 13957:MyApp/u0a174}]
I think you should take a look at "partial wakelock" to prevent the "sleep mode" of the phone.
https://developer.android.com/training/scheduling/wakelock
in your MainActivity.cs create a WakeLock object and under your OnCreate function configure your wakelock :
private PowerManager.WakeLock _wl = null;
protected override void OnCreate(Bundle savedInstanceState)
{
// ... Your own code here
PowerManager pmanager = (PowerManager)this.GetSystemService("power");
_wl = pmanager.NewWakeLock(WakeLockFlags.Partial, "myapp_wakelock");
_wl?.SetReferenceCounted(false);
_wl?.Acquire();
}
public override void OnDestroy()
{
_wl?.Release();
base.OnDestroy();
}
Don't forget to add a permission in your AndroidManifest.xml:
<uses-permission android:name="android.permission.WAKE_LOCK" />
This solution can work but you're need to be carreful because for some manufacturer, if your app use to many ressources the foreground service responsible of the state "always alive" of the CPU can be killed. In some cases, they add a "battery saver" option mode and you need to disable it directly in settings to run your app without issues.
Hope this can help
Use TelecomManager. Something like this:
Uri uri = Uri.fromParts("tel", phoneNumber, null);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
TelecomManager telecomManager = (TelecomManager) getInstance().getSystemService(Context.TELECOM_SERVICE);
telecomManager.placeCall(uri, new Bundle());
}
else
{
Intent callIntent = new Intent(Intent.ACTION_CALL, uri);
callIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(callIntent);
}
}

Read service name from AndroidManifest

I want to write Firebase's InstanceId service like service in my project. The project is an SDK where the developer who integrates it has the provision to override this service. In this case, I should be able to read the name of the new service specified by the developer with a particular action in their AndroidManifest.xml file.
So the real question here is, how can I read the name of the service declared in the AndroidManifest.xml file with a specific action?
Use below utility method
public static void startService(Context context, String lookupAction) {
Intent serviceIntent = new Intent();
serviceIntent.setAction(lookupAction);
serviceIntent.setPackage("package.of.your.application");
List<ResolveInfo> resInfo = context.getPackageManager().queryIntentServices(serviceIntent, 0);
if (resInfo != null && !resInfo.isEmpty()) {
ServiceInfo service = resInfo.get(0).serviceInfo;
ComponentName cmpService = new ComponentName(service.applicationInfo.packageName, service.name);
Intent serviceToStart = new Intent(lookupAction);
serviceToStart.setComponent(cmpService);
context.startService(serviceToStart);
} else {
// Handle error
}
}
I will add documentation soon

Check if battery optimization is enabled or not for an app

Android 6 and 7 have some power optimizations (doze mode) which restrict app networking when device is not used.
User may disable optimization mode for any app in Battery settings:
Is it possible to check if optimization is enabled for my app or not? I need to ask user to disable optimization for better app functionality, but I don't know how to check it programatically.
Add this permission in your manifest.
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
Request White-list / optimization enabled your app
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Intent intent = new Intent();
String packageName = getPackageName();
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
startActivity(intent);
}
}
This one was a bit tricky to track down: here's what you are looking for
PowerManager.isIgnoringBatteryOptimizations()
How to write this in Kotlin
Check if optimization is enabled
/**
* return true if in App's Battery settings "Not optimized" and false if "Optimizing battery use"
*/
fun isIgnoringBatteryOptimizations(context: Context): Boolean {
val pwrm = context.applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager
val name = context.applicationContext.packageName
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return pwrm.isIgnoringBatteryOptimizations(name)
}
return true
}
Check if Optimization enabled, open optimizations Activity
fun checkBattery() {
if (!isIgnoringBatteryOptimizations() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val name = resources.getString(R.string.app_name)
Toast.makeText(applicationContext, "Battery optimization -> All apps -> $name -> Don't optimize", Toast.LENGTH_LONG).show()
val intent = Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS)
startActivity(intent)
}
}
Sample Code :
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (pm != null && !pm.isIgnoringBatteryOptimizations(getPackageName())) {
askIgnoreOptimization();
} else {
// already ignoring battery optimization code here next you want to do
}
} else {
// already ignoring battery optimization code here next you want to do
}
Declare static variable
private static final int IGNORE_BATTERY_OPTIMIZATION_REQUEST = 1002;
show dialog for BATTERY_OPTIMIZATIONS
private void askIgnoreOptimization() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, IGNORE_BATTERY_OPTIMIZATION_REQUEST);
} else {
openNextActivity();
}
}
also need to add permission to manifest file i.e as below
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
Maybe this code is helpful to you!

Hiding Application Icon programmatically [duplicate]

i had used below code for hide app icon programmatically
try{
PackageManager p = getPackageManager();
p.setComponentEnabledSetting(getComponentName(), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
}catch (Exception e) {
e.printStackTrace();
}
Now I want to make icon visible programmatically
Hide app's icon using below code:
PackageManager p = getPackageManager();
ComponentName componentName = new ComponentName(this, com.apps.MainActivity.class); // activity which is first time open in manifiest file which is declare as <category android:name="android.intent.category.LAUNCHER" />
p.setComponentEnabledSetting(componentName,PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
Here is how to bring back the app's icon.
PackageManager p = getPackageManager();
ComponentName componentName = new ComponentName(this, com.apps.MainActivity.class);
p.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
Important Edit:
According to docs, as of Android Q (API 29) all app icons will be visible in the launcher no matter what unless:
As of Android Q, at least one of the app's activities or synthesized
activities appears in the returned list unless the app satisfies at
least one of the following conditions:
The app is a system app.
The app doesn't request any permissions.
The tag in the app's manifest doesn't contain any child elements that represent app components.
Additionally, the system hides synthesized activities for some or all
apps in the following enterprise-related cases:
If the device is a fully managed device, no synthesized activities for any app appear in the returned list.
If the current user has a work profile, no synthesized activities for the user's work apps appear in the returned list.
Best Way To Hide Application Icon From Launcher You Can Use
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
In Your Manifest MainActivity
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
</intent-filter>
</activity>
also add uses-feature in Manifest Tag
<uses-feature
android:name="android.software.leanback"
android:required="true" />
To hide icon use this:
PackageManager p = getPackageManager();
ComponentName componentName = new ComponentName(this, com.apps.MainActivity.class);
p.setComponentEnabledSetting(componentName,PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
and to unhide icon:
PackageManager p = getPackageManager();
ComponentName componentName = new ComponentName(this, com.apps.MainActivity.class);
p.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
IMPORTANT:
It's somehow tricky if you need to do something with main activity in your app when it's hidden. you will face an ActivityNotFoundException. to make it work, you should unhide icon before doing anything to your main activity and hide it again after you are finished.
simple steps:
1-call received here
2-unhide icon
3-launch main activity
4-do your things on main activity
5-hide icon again
Download source code from here (Hide and Unhide the app icon in android programmatically)
MainActivity.java:
package com.deepshikha.hideappicon;
import android.Manifest;
import android.app.ProgressDialog;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
Button btn_hide;
private static final ComponentName LAUNCHER_COMPONENT_NAME = new ComponentName(
"com.deepshikha.hideappicon", "com.deepshikha.hideappicon.Launcher");
public static int REQUEST_PERMISSIONS = 1;
boolean boolean_permission;
ProgressDialog progressDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
fn_permission();
listener();
}
private void init() {
btn_hide = (Button) findViewById(R.id.btn_hide);
progressDialog = new ProgressDialog(MainActivity.this);
progressDialog.setTitle("Alert");
progressDialog.setMessage("Please wait");
if (isLauncherIconVisible()) {
btn_hide.setText("Hide");
} else {
btn_hide.setText("Unhide");
}
}
private void listener() {
btn_hide.setOnClickListener(this);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_hide:
progressDialog.show();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
progressDialog.dismiss();
if (isLauncherIconVisible()) {
btn_hide.setText("Hide");
} else {
btn_hide.setText("Unhide");
}
}
}, 10000);
if (boolean_permission) {
if (isLauncherIconVisible()) {
fn_hideicon();
} else {
fn_unhide();
}
} else {
Toast.makeText(getApplicationContext(), "Please allow the permission", Toast.LENGTH_LONG).show();
}
break;
}
}
private boolean isLauncherIconVisible() {
int enabledSetting = getPackageManager().getComponentEnabledSetting(LAUNCHER_COMPONENT_NAME);
return enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
}
private void fn_hideicon() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Important!");
builder.setMessage("To launch the app again, dial phone number 1234567890");
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
getPackageManager().setComponentEnabledSetting(LAUNCHER_COMPONENT_NAME,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}
});
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.show();
}
private void fn_unhide() {
PackageManager p = getPackageManager();
ComponentName componentName = new ComponentName(this, com.deepshikha.hideappicon.MainActivity.class);
p.setComponentEnabledSetting(LAUNCHER_COMPONENT_NAME, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
}
private void fn_permission() {
if ((ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.PROCESS_OUTGOING_CALLS) != PackageManager.PERMISSION_GRANTED) ||
(ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.PROCESS_OUTGOING_CALLS) != PackageManager.PERMISSION_GRANTED)) {
if ((ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, android.Manifest.permission.PROCESS_OUTGOING_CALLS))) {
} else {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{android.Manifest.permission.PROCESS_OUTGOING_CALLS},
REQUEST_PERMISSIONS);
}
if ((ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.PROCESS_OUTGOING_CALLS))) {
} else {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.PROCESS_OUTGOING_CALLS},
REQUEST_PERMISSIONS);
}
} else {
boolean_permission = true;
}
}
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_PERMISSIONS) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
boolean_permission = true;
} else {
Toast.makeText(getApplicationContext(), "Please allow the permission", Toast.LENGTH_LONG).show();
}
}
}
}
LaunchAppReceiver.java:
package com.deepshikha.hideappicon;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
/**
* Created by deepshikha on 9/6/17.
*/
public class LaunchAppReceiver extends BroadcastReceiver {
String LAUNCHER_NUMBER = "1234567890";
private static final ComponentName LAUNCHER_COMPONENT_NAME = new ComponentName(
"com.deepshikha.hideappicon", "com.deepshikha.hideappicon.Launcher");
#Override
public void onReceive(Context context, Intent intent) {
String phoneNubmer = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
if (LAUNCHER_NUMBER.equals(phoneNubmer)) {
setResultData(null);
if (isLauncherIconVisible(context)) {
} else {
Intent appIntent = new Intent(context, MainActivity.class);
appIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(appIntent);
}
}
}
private boolean isLauncherIconVisible(Context context) {
int enabledSetting = context.getPackageManager().getComponentEnabledSetting(LAUNCHER_COMPONENT_NAME);
return enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
}
}
Thanks!
this is what I've found so far, unfortunately it's not an answer to the original question, just alternatives
This is the first option, but if your apps require permission and is not useful anymore (at least in Android 10) as #CoronaPintu mentioned here https://stackoverflow.com/a/22754642/1712446 this method works but have many restrictions
private void hideIcon(Context context, Class activityToHide) {
PackageManager packageManager = getPackageManager();
ComponentName componentName = new ComponentName(context, activityToHide);
packageManager.setComponentEnabledSetting(
componentName,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}
Using the same method above plus adb command, even is your app require permission this alternative works, but you must have access to devices and connect to a pc, then run this command
to hide:
$adb shell settings put global show_hidden_icon_apps_enabled 0
to show:
$adb shell settings put global show_hidden_icon_apps_enabled 1
Just in case, you cannot run this command from the app
Another option is DevicePolicyManager
private void hideIcon(Context context, Class activityToHide) {
ComponentName componentName = new ComponentName(context, activityToHide);
DevicePolicyManager devicePolicyManager = (DevicePolicyManager) getSystemService(getApplicationContext().DEVICE_POLICY_SERVICE);
devicePolicyManager.setApplicationHidden(componentName, "your.package.name.here", true);
}
This method works, but again we have some restrictions, you need to
enable Device Owner Mode, you can find more info here
To enable this mode you must run this adb command
adb shell dpm set-device-owner my.package.name/.DevAdminReceiver
However you can this command from the app
Runtime.getRuntime().exec("dpm set-device-owner my.package.name/.DevAdminReceiver");
But, if the phone already have set an account, this method going to failed with the next error:
java.lang.IllegalStateException: Not allowed to set the device owner because there are already several users on the device
This feature is no longer supported as of Android Q (API 29). Details have also been added to a previous answer. Your app's icon will be visible unless it satisfies one of the following conditions stated in the docs:
The app is a system app.
The app doesn't request any permissions.
The tag in the app's manifest doesn't contain any child elements that
represent app components.

Android hide/unhide app icon programmatically

i had used below code for hide app icon programmatically
try{
PackageManager p = getPackageManager();
p.setComponentEnabledSetting(getComponentName(), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
}catch (Exception e) {
e.printStackTrace();
}
Now I want to make icon visible programmatically
Hide app's icon using below code:
PackageManager p = getPackageManager();
ComponentName componentName = new ComponentName(this, com.apps.MainActivity.class); // activity which is first time open in manifiest file which is declare as <category android:name="android.intent.category.LAUNCHER" />
p.setComponentEnabledSetting(componentName,PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
Here is how to bring back the app's icon.
PackageManager p = getPackageManager();
ComponentName componentName = new ComponentName(this, com.apps.MainActivity.class);
p.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
Important Edit:
According to docs, as of Android Q (API 29) all app icons will be visible in the launcher no matter what unless:
As of Android Q, at least one of the app's activities or synthesized
activities appears in the returned list unless the app satisfies at
least one of the following conditions:
The app is a system app.
The app doesn't request any permissions.
The tag in the app's manifest doesn't contain any child elements that represent app components.
Additionally, the system hides synthesized activities for some or all
apps in the following enterprise-related cases:
If the device is a fully managed device, no synthesized activities for any app appear in the returned list.
If the current user has a work profile, no synthesized activities for the user's work apps appear in the returned list.
Best Way To Hide Application Icon From Launcher You Can Use
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
In Your Manifest MainActivity
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
</intent-filter>
</activity>
also add uses-feature in Manifest Tag
<uses-feature
android:name="android.software.leanback"
android:required="true" />
To hide icon use this:
PackageManager p = getPackageManager();
ComponentName componentName = new ComponentName(this, com.apps.MainActivity.class);
p.setComponentEnabledSetting(componentName,PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
and to unhide icon:
PackageManager p = getPackageManager();
ComponentName componentName = new ComponentName(this, com.apps.MainActivity.class);
p.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
IMPORTANT:
It's somehow tricky if you need to do something with main activity in your app when it's hidden. you will face an ActivityNotFoundException. to make it work, you should unhide icon before doing anything to your main activity and hide it again after you are finished.
simple steps:
1-call received here
2-unhide icon
3-launch main activity
4-do your things on main activity
5-hide icon again
Download source code from here (Hide and Unhide the app icon in android programmatically)
MainActivity.java:
package com.deepshikha.hideappicon;
import android.Manifest;
import android.app.ProgressDialog;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
Button btn_hide;
private static final ComponentName LAUNCHER_COMPONENT_NAME = new ComponentName(
"com.deepshikha.hideappicon", "com.deepshikha.hideappicon.Launcher");
public static int REQUEST_PERMISSIONS = 1;
boolean boolean_permission;
ProgressDialog progressDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
fn_permission();
listener();
}
private void init() {
btn_hide = (Button) findViewById(R.id.btn_hide);
progressDialog = new ProgressDialog(MainActivity.this);
progressDialog.setTitle("Alert");
progressDialog.setMessage("Please wait");
if (isLauncherIconVisible()) {
btn_hide.setText("Hide");
} else {
btn_hide.setText("Unhide");
}
}
private void listener() {
btn_hide.setOnClickListener(this);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_hide:
progressDialog.show();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
progressDialog.dismiss();
if (isLauncherIconVisible()) {
btn_hide.setText("Hide");
} else {
btn_hide.setText("Unhide");
}
}
}, 10000);
if (boolean_permission) {
if (isLauncherIconVisible()) {
fn_hideicon();
} else {
fn_unhide();
}
} else {
Toast.makeText(getApplicationContext(), "Please allow the permission", Toast.LENGTH_LONG).show();
}
break;
}
}
private boolean isLauncherIconVisible() {
int enabledSetting = getPackageManager().getComponentEnabledSetting(LAUNCHER_COMPONENT_NAME);
return enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
}
private void fn_hideicon() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Important!");
builder.setMessage("To launch the app again, dial phone number 1234567890");
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
getPackageManager().setComponentEnabledSetting(LAUNCHER_COMPONENT_NAME,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}
});
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.show();
}
private void fn_unhide() {
PackageManager p = getPackageManager();
ComponentName componentName = new ComponentName(this, com.deepshikha.hideappicon.MainActivity.class);
p.setComponentEnabledSetting(LAUNCHER_COMPONENT_NAME, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
}
private void fn_permission() {
if ((ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.PROCESS_OUTGOING_CALLS) != PackageManager.PERMISSION_GRANTED) ||
(ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.PROCESS_OUTGOING_CALLS) != PackageManager.PERMISSION_GRANTED)) {
if ((ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, android.Manifest.permission.PROCESS_OUTGOING_CALLS))) {
} else {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{android.Manifest.permission.PROCESS_OUTGOING_CALLS},
REQUEST_PERMISSIONS);
}
if ((ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.PROCESS_OUTGOING_CALLS))) {
} else {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.PROCESS_OUTGOING_CALLS},
REQUEST_PERMISSIONS);
}
} else {
boolean_permission = true;
}
}
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_PERMISSIONS) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
boolean_permission = true;
} else {
Toast.makeText(getApplicationContext(), "Please allow the permission", Toast.LENGTH_LONG).show();
}
}
}
}
LaunchAppReceiver.java:
package com.deepshikha.hideappicon;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
/**
* Created by deepshikha on 9/6/17.
*/
public class LaunchAppReceiver extends BroadcastReceiver {
String LAUNCHER_NUMBER = "1234567890";
private static final ComponentName LAUNCHER_COMPONENT_NAME = new ComponentName(
"com.deepshikha.hideappicon", "com.deepshikha.hideappicon.Launcher");
#Override
public void onReceive(Context context, Intent intent) {
String phoneNubmer = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
if (LAUNCHER_NUMBER.equals(phoneNubmer)) {
setResultData(null);
if (isLauncherIconVisible(context)) {
} else {
Intent appIntent = new Intent(context, MainActivity.class);
appIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(appIntent);
}
}
}
private boolean isLauncherIconVisible(Context context) {
int enabledSetting = context.getPackageManager().getComponentEnabledSetting(LAUNCHER_COMPONENT_NAME);
return enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
}
}
Thanks!
this is what I've found so far, unfortunately it's not an answer to the original question, just alternatives
This is the first option, but if your apps require permission and is not useful anymore (at least in Android 10) as #CoronaPintu mentioned here https://stackoverflow.com/a/22754642/1712446 this method works but have many restrictions
private void hideIcon(Context context, Class activityToHide) {
PackageManager packageManager = getPackageManager();
ComponentName componentName = new ComponentName(context, activityToHide);
packageManager.setComponentEnabledSetting(
componentName,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}
Using the same method above plus adb command, even is your app require permission this alternative works, but you must have access to devices and connect to a pc, then run this command
to hide:
$adb shell settings put global show_hidden_icon_apps_enabled 0
to show:
$adb shell settings put global show_hidden_icon_apps_enabled 1
Just in case, you cannot run this command from the app
Another option is DevicePolicyManager
private void hideIcon(Context context, Class activityToHide) {
ComponentName componentName = new ComponentName(context, activityToHide);
DevicePolicyManager devicePolicyManager = (DevicePolicyManager) getSystemService(getApplicationContext().DEVICE_POLICY_SERVICE);
devicePolicyManager.setApplicationHidden(componentName, "your.package.name.here", true);
}
This method works, but again we have some restrictions, you need to
enable Device Owner Mode, you can find more info here
To enable this mode you must run this adb command
adb shell dpm set-device-owner my.package.name/.DevAdminReceiver
However you can this command from the app
Runtime.getRuntime().exec("dpm set-device-owner my.package.name/.DevAdminReceiver");
But, if the phone already have set an account, this method going to failed with the next error:
java.lang.IllegalStateException: Not allowed to set the device owner because there are already several users on the device
This feature is no longer supported as of Android Q (API 29). Details have also been added to a previous answer. Your app's icon will be visible unless it satisfies one of the following conditions stated in the docs:
The app is a system app.
The app doesn't request any permissions.
The tag in the app's manifest doesn't contain any child elements that
represent app components.

Categories

Resources