Android如何通过编程隐藏/显示应用程序图标

74

我曾使用以下代码来在程序中隐藏应用图标

try{
    PackageManager p = getPackageManager();
    p.setComponentEnabledSetting(getComponentName(), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
}catch (Exception e) {
    e.printStackTrace();
}
现在我想以编程方式使图标可见。

我正在寻找这个,但是我需要在哪里编写这段代码? - Asad Raza
如何动态传递包名并在设备中隐藏特定应用程序图标 - Harsha
6个回答

137

使用以下代码隐藏应用程序图标:

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);

以下是如何恢复应用程序图标的步骤。

PackageManager p = getPackageManager();
ComponentName componentName = new ComponentName(this, com.apps.MainActivity.class);
p.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);

重要编辑:

根据文档,自Android Q(API 29)起,所有应用程序图标将在启动器中可见,除非:

从Android Q开始,应用程序的至少一个活动或合成活动出现在返回的列表中,除非应用程序满足以下条件之一:

  • 该应用是系统应用程序。
  • 该应用没有请求任何权限。
  • 应用程序清单中的``标记不包含代表应用程序组件的任何子元素。

此外,系统在以下企业相关情况下隐藏某些或所有应用程序的合成活动:

  • 如果设备是完全管理的设备,则不会在返回的列表中显示任何应用程序的合成活动。
  • 如果当前用户拥有工作配置文件,则该用户的工作应用的所有合成活动都不会出现在返回的列表中。

3
你好,你的代码完美运作,但我需要做一件棘手的事情。在隐藏图标后,如果用户打特定号码(如#007),我想启动应用程序。我已经实现了呼出电话接收器,并匹配号码后尝试启动主活动,但它给出了“ActivityNotFoundException”的错误。如果你有任何想法能帮我吗? - Scorpion
4
针对这个问题,我已经进行了两天的研究。现在我可以给你一个解决方案:隐藏主Activity后,该Activity将被销毁且无法找到,因此您需要创建另一个类似于MainActivity的Activity,例如MainActivity2,并且需要将布尔值存储到共享首选项中以指示图标是否已隐藏。如果该值为真,则打开MainActivity2,否则打开MainActivity。请检查一下。 - CoronaPintu
1
请问您能否提供一个解决方案,使得应用图标在重新启动之前不再显示?谢谢! @CoronaPintu - Muhammad Zeeshan Karamat
1
我知道这个问题已经被问了一段时间,但我认为应该有另一个活动,不使用启动器意图过滤器(因此它不会出现在应用程序启动器中),该活动将具有呼出电话意图过滤器以启用主活动。 - Didi Kohen
1
注意,如果您使用此代码并切换/添加图标,用户从主屏幕中删除了您使用 COMPONENT_ENABLED_STATE_ENABLED 创建的图标,则该应用程序将变得无效,并且除了卸载再安装之外,无法在任何地方使用! - NoBugs
显示剩余7条评论

30

隐藏应用程序图标的最佳方法:您可以使用以下代码:

<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>

在您的清单文件中的主活动(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>

还需要在Manifest标签中添加uses-feature

<uses-feature
    android:name="android.software.leanback"
    android:required="true" />

1
这绝对是与其他先前解决方案相比最好,最干净的解决方案。非常感谢。 - Nwawel A Iroume
3
这只是针对电视应用程序吗? - CrazyMind
有没有办法手动处理这个问题?@Ahmad 是的,它工作得很好,但我没有找到任何手动处理的方法。- Nwawel A Iroume,你实现了吗?请给予建议。我已经卡在这个问题上很多天了。 - RaRa
@RaRa 我正在使用Java代码来隐藏图标,但在Android 10上只有我的代码无法正常工作。 - Ahmad
如何打开它? - Naveen Kumar
显示剩余2条评论

14

要隐藏图标,请使用以下方法:

PackageManager p = getPackageManager();
ComponentName componentName = new ComponentName(this, com.apps.MainActivity.class); 
p.setComponentEnabledSetting(componentName,PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);

并取消隐藏图标:

PackageManager p = getPackageManager();
ComponentName componentName = new ComponentName(this, com.apps.MainActivity.class);
p.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);

重要提示: 如果您需要在应用程序的主活动被隐藏时执行某些操作,这可能会有些棘手。您将面临一个ActivityNotFoundException错误。为了让它正常工作,您应该在对主活动进行任何操作之前取消隐藏图标,并在完成后再次隐藏。
简单的步骤: 1-在此处接收调用
2-取消隐藏图标
3-启动主活动
4-在主活动上进行操作
5-再次隐藏图标


这与一年前发布的代码有何不同?你的建议难道不应该成为对那篇文章的评论吗? - Abandoned Cart
@AbandonedCart 我的回答还提供了如何在禁用MainActivity时使用它的方法。当您按照这种方法隐藏应用程序时,很可能会出现ActivityNotFoundException,但是没有任何答案提供有关此问题的详细信息。这就是为什么我添加了我的答案作为新答案,以便人们可以看到它。 - Amir Oveisi
问题是如何将应用程序从隐藏状态还原。答案唯一的原始部分似乎是一个与问题无关的方法,因为OP并不是在询问在隐藏状态下尝试使用它时会收到什么错误,而是如何取消隐藏(默认情况下解决错误)。这应该是一个评论。 - Abandoned Cart

6

从这里下载源代码 (在Android中以编程方式隐藏和显示应用程序图标)

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;
    }

}

谢谢!


4
这是我迄今为止找到的东西,不幸的是它并不是原问题的答案,只是替代方案。
  1. 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://dev59.com/ZGIk5IYBdhLWcg3wn_VF#22754642 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);
    }
    
  2. 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

仅作为预防措施,你无法从应用程序运行此命令。

  1. 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);
    }
    

这种方法可以生效,不过我们仍然有一些限制,你需要启用设备所有者模式,你可以在这里找到更多信息。

要启用此模式,您必须运行此adb命令。

adb shell dpm set-device-owner my.package.name/.DevAdminReceiver

然而,您可以从应用程序中执行此命令。

Runtime.getRuntime().exec("dpm set-device-owner my.package.name/.DevAdminReceiver");    

但是,如果手机已经设置了一个账户,使用这种方法会失败并出现下一个错误:

java.lang.IllegalStateException: Not allowed to set the device owner because there are already several users on the device

3
Android Q(API 29)已不再支持此功能。相关详细信息已添加到之前的答案中。除非您的应用满足以下条件之一,否则其图标将可见:文档中已经说明:
  • 该应用是系统应用程序。
  • 该应用未请求任何权限。
  • 在应用程序清单文件中的标签中没有包含表示应用组件的任何子元素。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接