Android-x86-6.0定製之路 - 動態顯示、隱藏狀態欄和導航欄(廣播方式)

需求確立

描述

當 App 進入 kiosk 模式,要求隱藏底部虛擬鍵、禁止下拉出現狀態欄,這樣保證我們的 App 一直處於系統前臺運行。

1. 通過getWindow().getDecorView().setSystemUiVisibility(Options)控制 SystemUi 是否可見就行了,但是並沒有做到真正的隱藏,當觸措屏幕的時候狀態欄和導航欄還是顯示出來了,顯然這種實現方式並不能滿足需求。

View decorView = getWindow().getDecorView();
            int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
//                    | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
                    | View.SYSTEM_UI_FLAG_IMMERSIVE;
            decorView.setSystemUiVisibility(uiOptions);
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);

2. 模擬系統設置的應用鎖功能,嘗試在自己的 App 裏實現應用鎖的達到鎖定應用,隱藏狀態欄和導航欄。很不容易,的確用代碼實現了應用鎖功能,但是坑的是 App 進入鎖定模式時,系統會給出解鎖方法的提示,而且屏蔽不了。對於使用應用鎖進入 Kiosk 模式,請查看react-native-kiosk-mode

確認

簡單整理下需求:

  • Settings.apk 設置 -> 顯示下, 添加系統欄顯示功能,控制顯示與隱藏
  • 通過廣播的方法動態顯示、隱藏系統欄(狀態欄和導航欄)

功能實現

定義廣播

找到frameworks/base/core/java/android/content/Intent.java文件,在文件末尾插入如下代碼:

public static final String ACTION_SYSTEM_BAR_SHOW = "android.intent.action.SYSTEM_BAR_SHOW"; // 顯示系統欄
public static final String ACTION_SYSTEM_BAR_HIDE = "android.intent.action.SYSTEM_BAR_HIDE"; // 隱藏系統欄

過慮廣播

找到frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java文件,定位至 926 行,修改代碼接收定義的廣播:

    // receive broadcasts
    IntentFilter filter = new IntentFilter();
    filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
    filter.addAction(Intent.ACTION_SCREEN_OFF);
    filter.addAction(Intent.ACTION_SCREEN_ON);
    filter.addAction(Intent.ACTION_SYSTEM_BAR_SHOW);
    filter.addAction(Intent.ACTION_SYSTEM_BAR_HIDE);
    context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);

根據廣播顯示、隱藏

下面的代碼主要是判斷接收的廣播,調用對應的方法

private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            if (DEBUG) Log.v(TAG, "onReceive: " + intent);
            String action = intent.getAction();
            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
                if (isCurrentProfile(getSendingUserId())) {
                    int flags = CommandQueue.FLAG_EXCLUDE_NONE;
                    String reason = intent.getStringExtra("reason");
                    if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
                        flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
                    }
                    animateCollapsePanels(flags);
                }
            }
            else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
                notifyNavigationBarScreenOn(false);
                notifyHeadsUpScreenOff();
                finishBarAnimations();
                resetUserExpandedStates();
            }
            else if (Intent.ACTION_SCREEN_ON.equals(action)) {
                notifyNavigationBarScreenOn(true);
            }
            else if (Intent.ACTION_SYSTEM_BAR_SHOW.equals(action)) {
                setSystemBarVisibility(View.VISIBLE);
            }
            else if (Intent.ACTION_SYSTEM_BAR_HIDE.equals(action)) {
                setSystemBarVisibility(View.GONE);
            }
        }
    };

下面的代碼纔是核心功能的實現,顯示與隱藏 SystemBar

private void setSystemBarVisibility(int visibility) {
    if (DEBUG) Log.v(TAG, "setSystemBarVisibility: " + visibility);

    if (visibility == View.GONE && mNavigationBarView != null && mStatusBarWindow != null) {
        try {
            mWindowManager.removeViewImmediate(mNavigationBarView);
            mStatusBarWindow.setVisibility(View.GONE);
            Settings.System.putInt(mContext.getContentResolver(), Settings.System.SYSTEM_BAR_DISPLAY, 0);
        } catch (IllegalArgumentException e) {
            Log.w(TAG, "IllegalArgumentException: " + e);
        }
        mNavigationBarView = null;

    } else if (visibility == View.VISIBLE && mNavigationBarView == null && mStatusBarWindow != null) {
        try {
            makeNavigationBarView();
            addNavigationBar();
            mStatusBarWindow.setVisibility(View.VISIBLE);
            Settings.System.putInt(mContext.getContentResolver(), Settings.System.SYSTEM_BAR_DISPLAY, 1);
        } catch (WindowManager.BadTokenException e) {
            // ignore
            Log.w(TAG, "BadTokenException: " + e.getMessage());
        } catch (RuntimeException e) {
            // don't crash if something else bad happens, for example a
            // failure loading resources because we are loading from an app
            // on external storage that has been unmounted.
            Log.w(TAG, "RuntimeException: " + e);
        }
    }
}

private void makeNavigationBarView() {
    mNavigationBarView = (NavigationBarView) View.inflate(mContext, R.layout.navigation_bar, null);
    mNavigationBarView.setDisabledFlags(mDisabled1);
    mNavigationBarView.setBar(this);
    mNavigationBarView.setOnVerticalChangedListener(
            new NavigationBarView.OnVerticalChangedListener() {
                @Override
                public void onVerticalChanged(boolean isVertical) {
                    if (mAssistManager != null) {
                        mAssistManager.onConfigurationChanged();
                    }
                    mNotificationPanel.setQsScrimEnabled(!isVertical);
                }
            });
    mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            checkUserAutohide(v, event);
            return false;
        }
    });
}

系統設置添加系統欄顯示功能

1.語言支持中文與英文

找到packages/apps/Settings/res/values-zh-rCN/strings.xml文件,插入以下代碼:

 <!-- System Bar settings title-->
    <string name="system_bar_settings_title">系統欄顯示</string>
    <!-- System Bar settings summary-->
    <string name="system_bar_settings_summary">控制系統欄顯示或隱藏</string>

找到apps/Settings/res/values/strings.xml文件,插入以下代碼:

 <!-- System Bar settings title-->
    <string name="system_bar_settings_title">System Bar Display</string>
    <!-- System Bar settings summary-->
    <string name="system_bar_settings_summary">Control System Bar show or hide</string>

2.構建系統欄顯示 UI

找到packages/apps/Settings/res/xml/display_settings.xml文件,插入以下代碼:

<SwitchPreference
    android:key="system_bar"
    android:title="@string/system_bar_settings_title"
    android:summary="@string/system_bar_settings_summary"
    android:persistent="false" />

3.調用廣播實現切換

導入包

import static android.provider.Settings.System.SYSTEM_BAR_DISPLAY;
import android.content.Intent;

聲明變量

private static final String KEY_SYSTEM_BAR = "system_bar";
private SwitchPreference mSystemBarPreference;

onCreate()方法中,初始化控件

mSystemBarPreference = (SwitchPreference) findPreference(KEY_SYSTEM_BAR);
if (mSystemBarPreference != null) {
    mSystemBarPreference.setOnPreferenceChangeListener(this);
}

updateState()方法,更新控件狀態

 // update system bar
if (mSystemBarPreference != null) {
    int value = Settings.System.getInt(getContentResolver(), SYSTEM_BAR_DISPLAY, 1);
    mSystemBarPreference.setChecked(value == 1);
}

onPreferenceChange()回調方法,發送控制系統欄的廣播

 if (preference == mSystemBarPreference) {
    boolean value = (Boolean) objValue;
    getContext().sendBroadcast(new Intent(value ? Intent.ACTION_SYSTEM_BAR_SHOW : Intent.ACTION_SYSTEM_BAR_HIDE));
    Settings.System.putInt(getContentResolver(), SYSTEM_BAR_DISPLAY, value ? 1 : 0);
}

用法

簡單講解下通過廣播的方式控制系統欄的顯示與隱藏

adb

adb shell am broadcast -a android.intent.action.SYSTEM_BAR_SHOW // 顯示
adb shell am broadcast -a android.intent.action.SYSTEM_BAR_HIDE // 隱藏

intent

// 顯示
Intent intent = new Intent();
intent.setAction("android.intent.action.SYSTEM_BAR_SHOW");
sendBroadcast(intent);

// 隱藏
Intent intent = new Intent();
intent.setAction("android.intent.action.SYSTEM_BAR_HIDE");
sendBroadcast(intent);

小結

在這裏插入圖片描述

如何從源碼的角度實現動態顯示、隱藏系統欄(狀態欄和導航欄),具體實現也就這樣了,這裏僅是簡單記錄下。

Android-6.0 動態顯示、隱藏系統欄(狀態欄和導航欄),提供下載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章