Предотвращение расширения строки состояния
Есть ли в любом случае, чтобы запретить пользователям сдвинуть строку состояния (развернуть) или свернуть назад?
Я пытаюсь заменить экран блокировки и кажется, что это обязательная функция. Есть ли какой-нибудь возможный способ сделать это, не требуя привилегий root?
9 ответов
Вы можете запретить расширение строки состояния без рутирования телефона. Смотрите эту ссылку. Это рисует окно высоту строки состояния и потребляет все сенсорные события.
Вызовите следующий код сразу после 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://stackru.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.
Источники: личный опыт:-)
Прежде всего, невозможно изменить строку состояния, если ваше приложение не подписано с использованием сертификата телефона, если вы попытаетесь изменить его, вы получите исключение безопасности.
Обновление: в новых 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("collapse");
collapse .setAccessible(true);
collapse .invoke(service);
}
}
catch(Exception ex)
{
}
}
Обновление: вам придется использовать свой собственный диалог оповещений, переопределив его также и методом 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);
}
Если у пользователя нет защищенного экрана блокировки, приложение позволит пользователю опустить строку состояния и открыть действия, но это не имеет значения, поскольку пользователь, очевидно, не хочет защищенного экрана в любом случае.
Если у пользователя установлен безопасный локскрин, приложение покажет строку состояния, но не разрешит взаимодействие с ней. Это связано с тем, что телефон все еще заблокирован, и разрешено работать только вашей активности, пока пользователь не разблокирует телефон. Кроме того, закрытие вашего приложения в любом случае откроет стандартный безопасный экран блокировки. Все это очень желательно, потому что вам не нужно тратить все это время на кодирование безопасных функций, которые вы не можете гарантировать, будут такими же безопасными, как и стандартные.
Если вы действительно не хотите, чтобы пользователь мог взаимодействовать со строкой состояния, возможно, вы можете оставить флажок FLAG_DISMISS_KEYGUARD
вызов. Затем, перед тем, как вы собираетесь разблокировать телефон, установите флаг, как я показал в первом блоке. Я не знаю, работает ли эта последняя часть, но стоит попробовать.
надеюсь, это поможет
Что вы можете сделать, так это свернуть строку состояния в цикле, поэтому, когда пользователь попытается ее развернуть, она автоматически свернется. Вот код, просто добавьте его в свой файл 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);
}
Извините, но это не работает. с помощью FLAG_LAYOUT_IN_SCREEN
не позволяет вам ловить любые сенсорные события.
И кстати: для добавления вида в окно используйте менеджер окон:
WindowManager winMgr = (WindowManager)getSystemService(WINDOW_SERVICE);
winMgr.addView(disableStatusBar, handleParams);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);