有没有办法防止用户滑动状态栏(展开)或向后折叠?
我正在尝试更换锁屏,似乎这是一项必备功能。有没有不需要root权限的方法?
你实际上可以防止状态栏扩展,而不需要生根手机。请参阅此链接。这会绘制一个状态栏高度的窗口并消耗所有触摸事件。
在 onCreate() 之后调用以下代码。
public static void preventStatusBarExpansion(Context context) {
WindowManager manager = ((WindowManager) context.getApplicationContext()
.getSystemService(Context.WINDOW_SERVICE));
Activity activity = (Activity)context;
WindowManager.LayoutParams localLayoutParams = new WindowManager.LayoutParams();
localLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
localLayoutParams.gravity = Gravity.TOP;
localLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
// this is to enable the notification to recieve touch events
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
// Draws over status bar
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
localLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
//https://stackoverflow.com/questions/1016896/get-screen-dimensions-in-pixels
int resId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");
int result = 0;
if (resId > 0) {
result = activity.getResources().getDimensionPixelSize(resId);
}
localLayoutParams.height = result;
localLayoutParams.format = PixelFormat.TRANSPARENT;
customViewGroup view = new customViewGroup(context);
manager.addView(view, localLayoutParams);
}
public static class customViewGroup extends ViewGroup {
public customViewGroup(Context context) {
super(context);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.v("customViewGroup", "**********Intercepted");
return true;
}
}
简答:这是不可能的!
现在你应该有兴趣知道为什么这是不可能的:
操作系统状态栏有两个权限,EXPAND_STATUS_BAR和STATUS_BAR。前者可以被任何应用程序请求,而后者则保留给使用平台签名的应用程序(系统应用程序,非第三方应用程序)。 可以展开/折叠系统状态栏(参见“如何通过意图打开或展开状态栏?”)但请注意需要反射,因为StatusBarManager类不是SDK的一部分。 Android 拨号器用于防止状态栏展开的禁用方法,如果没有上述 STATUS_BAR 权限,则应用程序无法访问该方法。
资料来源:个人经验:-)
首先,如果您的应用程序未使用手机的 rom 认证签名,则无法修改状态栏,如果您尝试修改它,您将获得安全异常。
更新:在新的 API 中,方法是“collapsePanels”而不是“collapse”。
经过几个小时的工作,我发现的唯一方法是覆盖活动的“onWindowFocusChanged”方法,当它失去焦点时(可能用户已经触摸了通知栏),强制折叠 StatusBar,这里是代码(在 Defy+ 2.3.5 上工作)。
您需要在清单上声明以下权限:
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR"/>
并覆盖以下方法:
public void onWindowFocusChanged(boolean hasFocus)
{
try
{
if(!hasFocus)
{
Object service = getSystemService("statusbar");
Class<?> statusbarManager = Class.forName("android.app.StatusBarManager");
Method collapse = statusbarManager.getMethod(Build.VERSION.SDK_INT > 16 ? "collapsePanels" : "collapse");
collapse.setAccessible(true);
collapse.invoke(service);
}
}
catch(Exception ex)
{
}
}
Update:您也必须通过重写它们的 onWindowFocusChanged 方法来使用您自己的自定义警报对话框,因为警报对话框有自己的焦点。
这实际上可以通过我无意中发现的一个小技巧来完成,但需要许可
android.permission.SYSTEM_ALERT_WINDOW
:
您所做的是将状态栏的确切大小的视图直接添加到 WindowManager,并使用覆盖状态栏并防止其接收触摸事件的某些参数:
View disableStatusBarView = new View(context);
WindowManager.LayoutParams handleParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.FILL_PARENT,
<height of the status bar>,
// This allows the view to be displayed over the status bar
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
// this is to keep button presses going to the background window
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
// this is to enable the notification to recieve touch events
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
// Draws over status bar
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT);
handleParams.gravity = Gravity.TOP;
context.getWindow().addView(disableStatusBarView, handleParams);
这基本上会在状态栏上创建一个透明视图,它将接收所有触摸事件并阻止事件到达状态栏,从而防止它被扩展。
注意:此代码未经测试,但这个概念有效。
我尝试了 GrantLand 和 PoOk 提到的解决方案,但两者都不适用于我的情况。不过,另一个解决方案清除/隐藏最近的任务列表对我有用。我正在编写一个启动器应用程序,我必须为此禁用最近的应用程序菜单,以便用户无法从中打开锁定的应用程序。此外,我必须防止通知栏扩展,这个技巧让我实现了这两个目标。在您的活动中覆盖OnWindowFocusChanged方法并检查这是否是您想要的。
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
Log.d("Focus debug", "Focus changed !");
if(!hasFocus) {
Log.d("Focus debug", "Lost focus !");
Intent closeDialog = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
sendBroadcast(closeDialog);
}
}
锁屏你为什么不在你的主要活动中使用以下内容:
@Override
public void onAttachedToWindow() {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
}
如果用户没有安全锁屏设置,应用程序会让用户下拉状态栏并打开活动,但这并不重要,因为用户显然不想要安全屏幕。
如果用户确实设置了安全锁屏,则应用程序将显示状态栏,但不允许与其交互。这是因为手机实际上仍处于锁定状态,并且只有您的 Activity 才能运行,直到用户解锁手机。无论如何关闭您的应用程序也会打开标准的安全锁屏。所有这些都是非常可取的,因为您不必花费所有时间来编写您无法保证与库存功能一样安全的安全功能。
如果您真的不希望用户能够与状态栏进行交互,也许您可以省略标志
FLAG_DISMISS_KEYGUARD
调用。然后就在你要解锁手机之前设置标志,就像我在第一个块中展示的那样。我不知道这最后一部分是否有效,但值得一试。
希望有帮助
抱歉,它不起作用。使用
FLAG_LAYOUT_IN_SCREEN
可防止您捕获任何触摸事件。
顺便说一句: 使用窗口管理器向窗口添加视图:
WindowManager winMgr = (WindowManager)getSystemService(WINDOW_SERVICE);
winMgr.addView(disableStatusBar, handleParams);
你可以做的是循环折叠状态栏,所以当用户试图展开它时,它会自动折叠,这是代码,只需将它添加到你的 MainActivity.java 文件中:
private Handler mHandler = new Handler();
private Runnable mRunnable = new Runnable() {
@Override
public void run() {
try {
@SuppressLint("WrongConstant") Object service = getSystemService("statusbar");
Class<?> statusBarManager = Class.forName("android.app.StatusBarManager");
Method collapse = statusBarManager.getMethod(Build.VERSION.SDK_INT > 16 ? "collapsePanels" : "collapse");
collapse.setAccessible(true);
collapse.invoke(service);
} catch (Exception ex) {
// handle exception
}
mHandler.postDelayed(mRunnable, 100);
}
};
@Override
protected void onResume() {
super.onResume();
mHandler.postDelayed(mRunnable, 100);
}
@Override
protected void onPause() {
super.onPause();
mHandler.removeCallbacks(mRunnable);
}
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);