如何像锁屏应用程序一样在Android中禁用主页按钮?

17

我知道这个问题被问了很多次,但是我发现没有一个解决方案有效。 我尝试了下面给出的代码...

   protected void onPause() {
   super.onPause();
    Intent intent = new Intent(this,LockActivity.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT |Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(intent);
    }
它的作用是在Android主屏幕启动时将当前活动再次带到前台,但当主屏幕启动时,它需要将活动再次带到前台,这需要几乎3-4秒钟的时间。我使用了一些锁屏应用程序,这些应用程序甚至在单击主页按钮时都不会启动主屏幕。我想实现类似的功能。我还尝试了onUserLeavesHint方法、onKeyDown方法和onKeyDispatch方法,但都没有起作用。请不要回答或评论说在Android中禁用主页按钮不可能。对于这样的回答或评论,我建议您查看PlayStore上的一些锁屏应用程序。此外,我在github上找到了一个可工作的应用程序及源代码。它可在我的手机上运行,该应用程序使用了disableKeyguard,但当我在我的应用程序中执行相同操作时,它却无法工作(disableKeyguard已过时,但我使用@supress warnings("deprecation"))。

1
除了KIOSK模式之外,坦率的答案是您无法禁用主页按钮。 - Shree Krishna
我不介意你说的话,但我已经在第一行提到了我阅读了所有与我的问题相关的问题,并且每篇帖子都说你无法在Android中禁用主页按钮,而且最后我还说了Play商店上有许多屏幕锁定应用程序可以禁用主页按钮,如果你有时间,可以尝试其中之一。 - Ruag
@Ruag,我恳请您查看PiLocker,这是一个开源的锁屏程序,特别是:https://github.com/Pi-Developers/Pi-Locker/blob/master/pilocker/src/main/java/com/pilockerstable/Lock.java#L600,它将引导您查看HomeKeyLocker,最终到达https://github.com/shaobin0604/Android-HomeKey-Locker/。 - JasonSec
谢谢@JasonSec。我已经检查过一次了(我已经浏览了在Github上处理禁用Home键的每个开源项目),但它并没有百分之百地工作,但是我认为我应该像你建议的那样再次研究一下。Shaobin0604的Home键锁不是一个完美的解决方案,因为它不能处理虚拟Home键。 - Ruag
你应该意识到,任何你可能找到的“变通方法”很可能会在未来的谷歌更新中被修复,因为这是一个安全问题,对吧? - tyczj
显示剩余5条评论
6个回答

7

来源 - https://github.com/shaobin0604/Android-HomeKey-Locker

这个项目是一个安卓应用程序锁屏的实现方案,可以锁定设备的主页键和返回键。它使用系统级别的权限来确保不会被其他应用程序绕过。你可以通过下载源代码并自己构建来使用此解决方案。
//Copy this class
public class HomeKeyLocker {
    private OverlayDialog mOverlayDialog;
    public void lock(Activity activity) {
        if (mOverlayDialog == null) {
            mOverlayDialog = new OverlayDialog(activity);
            mOverlayDialog.show();
        }
    }
    public void unlock() {
        if (mOverlayDialog != null) {
            mOverlayDialog.dismiss();
            mOverlayDialog = null;
        }
    }
    private static class OverlayDialog extends AlertDialog {

        public OverlayDialog(Activity activity) {
            super(activity, R.style.OverlayDialog);
            WindowManager.LayoutParams params = getWindow().getAttributes();
            params.type =  WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
            params.dimAmount = 0.0F; // transparent
            params.width = 0;
            params.height = 0;
            params.gravity = Gravity.BOTTOM;
            getWindow().setAttributes(params);
            getWindow().setFlags( WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |  WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, 0xffffff);
            setOwnerActivity(activity);
            setCancelable(false);
        }

        public final boolean dispatchTouchEvent(MotionEvent motionevent) {
            return true;
        }

        protected final void onCreate(Bundle bundle) {
            super.onCreate(bundle);
            FrameLayout framelayout = new FrameLayout(getContext());
            framelayout.setBackgroundColor(0);
            setContentView(framelayout);
        }
    }
}

//Paste this in your activity
mHomeKeyLocker = new HomeKeyLocker();
mHomeKeyLocker.lock(this);

源代码:这个Github项目。请始终引用您的来源。问题:在锁屏情况下,是否可以使用硬件按钮(或打开通知)绕过此方法?这仅适用于此黑客攻击适用的选择Android手机/版本(2.3-某些4.x版本)。 - Aaron Gillion
1
@Nitin 我已经尝试过了,但是当我在我的Activity中使用这两行代码(mHomeKeyLocker = new HomeKeyLocker(); mHomeKeyLocker.lock(this);)时,应用程序会崩溃。 - Ruag
@Raug:你能否请粘贴崩溃日志? - NitinM
@freakAR 是的,这个库不仅可以阻止键盘输入,还可以阻止返回按钮。 - mahoriR
@Nitin:在初始化WindowManager.LayoutParams OverlayDialog方法后添加// getWindow().setType(TYPE_APPLICATION_OVERLAY); //。 - Mohit Singh
显示剩余2条评论

3
你可以使用shaobin0604库来实现此功能。它还会禁用返回按钮。你的活动将如下所示:


public class MainActivity extends Activity {

HomeKeyLocker homeKeyLocker;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    homeKeyLocker = new HomeKeyLocker();
    homeKeyLocker.lock(this);
}

}


好的参考,我不知道这种解决方法的存在。除了shaobin列出的那些手机,能否提供一个完整的适用(和不适用)列表将会很好。同时想知道是否可以绕过此功能(对于锁屏来说非常关键)。 - Aaron Gillion
@Jordan 在来这里提问之前,我已经在这个网站上探索了所有的问题,也查看了所有与我的问题相关的开源项目,包括在Github上。我也尝试过shaobin0604的库,但是使用那段代码应用程序会崩溃。 - Ruag
UT880设备完全无法工作。 - Rahul Sonone

3

确保提供完美的非root锁屏功能的方法是将您的“locker”应用想法与启动器应用程序相结合。

在清单中进行简单更改,使您的应用程序可以注册为主屏幕/启动器,这将使您的.apk文件完全控制主页按钮:

<application
    android:launchMode="singleTask"
    android:clearTaskOnLaunch="true"
    android:stateNotNeeded="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <activity
        android:name="ch.arnab.simplelauncher.HomeScreen"
        android:label="@string/app_name"
        android:launchMode="singleTask"
        android:excludeFromRecents="true"
        android:screenOrientation="nosensor">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <!-- These 2 intent-filters identify a launcher: -->
            <category android:name="android.intent.category.HOME" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </activity>
</application>

源自本教程

然后您将拥有两个活动,一个用于主屏幕,一个用于锁定屏幕。

您需要检测屏幕何时关闭/开启,以显示您的锁定屏幕活动:

public class MainActivity extends Activity {

    //Create a receiver for screen-on/screen-off
    BroadcastReceiver mybroadcast = new BroadcastReceiver() {   
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
                //Show lock-screen
            }
            else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
                //Also show lock-screen, to remove flicker/delay when screen on?
            }

        }
    };
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        registerReceiver(mybroadcast, new IntentFilter(Intent.ACTION_SCREEN_ON));
        registerReceiver(mybroadcast, new IntentFilter(Intent.ACTION_SCREEN_OFF));
    }
}

摘自这个答案

顺便提一下,由于此时您的“锁屏”仍然被认为是您的启动器的一部分,应用程序将能够在您的锁屏上启动,如果:用户可以访问通知抽屉以点击消息/推文等,但也可以有好处:能够在不解锁手机的情况下接听来电。

无论哪种方式,您的锁屏活动都应该覆盖onPause,检查用户是否已“经过身份验证”,如果没有,则假定用户打开了某些东西,应返回到锁屏界面:

@Override
public void onPause() {
    super.onPause();
    if(password!=storedPassword) {
      //Lockscreen activity shouldn't ever be escaped without the right password!
      //Return to launcher without root!
      Intent homeIntent = new Intent(Intent.ACTION_MAIN);
      homeIntent.addCategory(Intent.CATEGORY_HOME);
      homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      startActivity(homeIntent);
    }
}

为了更好的用户体验,您应该给用户提供选择跳过锁屏的一些应用程序(例如Spotify),您可以将其包含在上面的if语句中(例如:if(password!=storedPassword || isForegroundAppSpotify))。
至于在主屏幕上加载应用程序,您可以参考Google教程创建自己的启动器活动。
结合启动器+锁屏是避免root访问的最简单方法。您可能会发现使用root更容易,但这将限制您的客户群。
希望这可以帮到您!

3
谢谢@Aaron Gillion。我已经尝试了这个方法,它也有效,但问题是它会在按下Home键时询问启动器,而我不想要这样。有没有办法通过编程为我的应用程序设置主屏幕启动器? - Ruag
问题与主页按钮而非主屏幕锁定有关。 - Duna

2

我进行了大量的研究来设计锁屏,最终找到了一个解决方案。Android禁用了除返回按钮外覆盖系统栏的功能。但是有一个小技巧可以让它起作用:

耐心地理解和实现屏幕固定,你就会成功。

你可以创建一个应用程序来控制你想要在其中实现屏幕固定的所有应用程序,或者你可以直接在你想要固定的同一应用程序中实现屏幕固定。

本文将展示后一种实现方法:

1. 首先,你的应用程序应该是设备所有者。

你可以通过多种方式来实现它,最简单的方法是执行以下命令:

adb shell dpm set-device-owner [yourPackageName]/.[MyDeviceAdminReceiver]

创建一个扩展DeviceAdminReceiver的接收器(MyDeviceAdminReceiver)。你不需要在这里编写任何代码。有关设备所有者实现的更多信息,请参阅此链接:
http://florent-dupont.blogspot.com/2015/02/10-things-to-know-about-device-owner.html

在AndroidManifest.xml文件中这样注册接收器:

<receiver
       android:name=".MyDeviceAdminReceiver"
       android:label="@string/app_name"
       android:permission="android.permission.BIND_DEVICE_ADMIN">
     <meta-data
       android:name="android.app.device_admin"
       android:resource="@xml/device_admin" />

       <intent-filter>
         <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
       </intent-filter>
  </receiver>

2. 您的onCreate方法应该像这样:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_lock_screen);

    ComponentName deviceAdmin = new ComponentName(this, MyDeviceAdminReceiver.class);
    DevicePolicyManager mDpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);


    if (mDpm.isDeviceOwnerApp(getPackageName())) {
        mDpm.setLockTaskPackages(deviceAdmin, new String[]{getPackageName()});
    }

    if (mDpm.isLockTaskPermitted(this.getPackageName()))
        startLockTask();

3.取消固定屏幕并使导航栏可用:

在代码的某个位置调用函数 stopLockTask() 以取消固定屏幕。例如,在我的应用程序中,当我确认用户输入了正确的密码时,就会调用此函数:

 if (userInput.length() == 4) {

                    if (userInput.equals(passcode)) {
                        userInput = "";
                        etxtPasscodeDisplay.setText("");
                        stopLockTask(); // this is what you need
                        unlockHomeButton(); // A method to show home screen when 
                         passcode is correct
                        finishAffinity(); //kill other activities
                    }

额外信息,通常用于锁屏:

1. 如果您的应用程序是开机后出现的第一件事情

您需要一个服务(StartAtBootService)和一个接收器(BootCompletedReceiver)来实现此功能。

2. 如果您想让您的应用程序在屏幕锁定和解锁后显示(按下电源按钮锁定和解锁):

创建一个扩展了service的AEScreenOnOffService和一个扩展了BroadcastReceiver的AEScreenOnOffReceiver,在屏幕打开时启动您的活动。

有关我在这里提到的所有详细信息,请参见http://www.sureshjoshi.com/mobile/android-kiosk-mode-without-root/。这是一篇非常好的文章,对我帮助很大。特别感谢作者。

我需要至少10个声望才能发布两个以上的链接。由于我是stackoverflow的新手,所以我没有足够的声望,因此很抱歉无法分享我参考的所有链接。一旦我获得访问权限,我肯定会更新帖子。


在Android Studio中,这应该是一个广播接收器,对吧?另外,在尝试在AndroidManifest.xml中注册接收器时,我遇到了这个错误:“无法解析符号'@xml/device_admin'”。你知道为什么吗? - wanderer0810
是的,我的DeviceAdminReceiver扩展了广播接收器。关于XML错误,我认为您可能没有xml文件夹或xml中的device_admin。在xml文件夹中创建device_admin文件,它就可以正常工作了。 - Nithesh

1

简单回答你的问题是你无法做到这一点。

你提到的解决方案是我四年前提出的 [链接]

onUserLeavesHint、onKeyDown和onKeyDispatch永远不会“禁用”硬件键。

如果你真的想要“处理”Home按钮,你将不得不将你的应用程序设置为主屏幕。请查看thisthis

如果你真的想要禁用硬件键而不制作主屏幕应用程序,你应该对设备进行root并从内核模块中删除相应的设备文件。(请自行承担风险!)


谢谢 @PC。但是你提供的所有观点都没有帮助到我。我认为你没有尝试过来自Google Play的任何锁屏应用程序,否则你不会那样回答。请浏览其中任何一个,它将在应用程序开发的某个时候真正帮助你。顺便说一下,我探索了一些开源的锁屏应用程序项目,他们使用警报对话框和锁定活动,使其尺寸为0,颜色透明,并将其重力设置为底部。但我不理解实现方式,也不知道它如何工作?你能再帮我一点吗? - Ruag

-1
你可以像这样重写主页键功能:
@Override public boolean onKeyDown(int keyCode, KeyEvent event)
{
    if(keyCode == KeyEvent.KEYCODE_HOME)
    {
        //The Code Want to Perform.
    }
});

那么你应该能够使用这个按钮防止用户返回主屏幕。 希望这对你有用。

编辑: 覆盖主页按钮的问题是谷歌认为它是一个安全漏洞,所以每当有人找到一种覆盖它的方法时,谷歌都会修补这个“漏洞”。


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