国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Android 10關(guān)機(jī)界面定制

這篇具有很好參考價(jià)值的文章主要介紹了Android 10關(guān)機(jī)界面定制。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

1、背景說明

在項(xiàng)目開發(fā)過程中,需要對(duì)開機(jī)界面進(jìn)行定制,使得產(chǎn)品界面風(fēng)格統(tǒng)一。

  • 軟件版本:Android 10
  • 方案供應(yīng)商:高通
  • 目的:定制關(guān)機(jī)UI
    系統(tǒng)原始的關(guān)機(jī)UI:
    Android 10關(guān)機(jī)界面定制
    定制后的關(guān)機(jī)UI:
    Android 10關(guān)機(jī)界面定制

2、關(guān)機(jī)流程

本文定制的關(guān)機(jī)界面為長(zhǎng)按power鍵觸發(fā)的關(guān)機(jī)界面,首先我們先了解Android 10整理的關(guān)機(jī)流程,熟悉整理流程后再進(jìn)行定制開發(fā)。
關(guān)機(jī)流程涉及的代碼路徑如下

frameworks\base\core\res\res\values\config.xml
frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java
frameworks\base\services\core\java\com\android\server\policy\GlobalActions.java
frameworks\base\packages\SystemUI\src\com\android\systemui\globalactions\GlobalActionsImpl.java
frameworks\base\services\core\java\com\android\server\policy\LegacyGlobalActions.java
frameworks\base\services\core\java\com\android\server\policy\PowerAction.java
frameworks\base\services\core\java\com\android\server\policy\WindowManagerPolicy.java
frameworks\base\services\core\java\com\android\server\wm\WindowManagerService.java
frameworks\base\services\core\java\com\android\server\power\ShutdownThread.java
frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java
frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java
frameworks\base\services\core\java\com\android\server\power\PowerManagerService.java
system\core\init\property_service.cpp
system\core\init\init.cpp
system\core\init\reboot.cpp
system\core\init\reboot_utils.cpp

長(zhǎng)按power鍵關(guān)機(jī)流程時(shí)序圖如下:
Android 10關(guān)機(jī)界面定制

2.1、Power鍵響應(yīng)

這里我們不關(guān)注驅(qū)動(dòng)及設(shè)備層如何識(shí)別及上報(bào)物理power事件,重點(diǎn)介紹android層如何攔截及響應(yīng)power key event。
frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java

//分發(fā)未經(jīng)處理的key,由InputManagerService調(diào)用
    @Override
    public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
        // Note: This method is only called if the initial down was unhandled.
        KeyEvent fallbackEvent = null;
        if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
            final KeyCharacterMap kcm = event.getKeyCharacterMap();
            final int keyCode = event.getKeyCode();
            final int metaState = event.getMetaState();
            final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN
                    && event.getRepeatCount() == 0;

            // Check for fallback actions specified by the key character map.
            final FallbackAction fallbackAction;
            if (initialDown) {
   				if (!interceptFallback(win, fallbackEvent, policyFlags)) {//對(duì)特殊按鍵(power、vol、home、mute等)進(jìn)行攔截處理,不分發(fā)至app
                    fallbackEvent.recycle();
                    fallbackEvent = null;
                }
            }
            ...
 	}
 ...
    private boolean interceptFallback(WindowState win, KeyEvent fallbackEvent, int policyFlags) {
        int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags);//key加入隊(duì)列之前進(jìn)行攔截
        if ((actions & ACTION_PASS_TO_USER) != 0) {
            long delayMillis = interceptKeyBeforeDispatching(//分發(fā)key之前進(jìn)行攔截
                    win, fallbackEvent, policyFlags);
            if (delayMillis == 0) {
                return true;
            }
        }
        return false;
    }

    @Override
    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
    // Handle special keys.
    ...
        switch (keyCode) {
        ...
        	case KeyEvent.KEYCODE_POWER: {
                EventLogTags.writeInterceptPower(
                        KeyEvent.actionToString(event.getAction()),
                        mPowerKeyHandled ? 1 : 0, mPowerKeyPressCounter);
                // Any activity on the power button stops the accessibility shortcut
                cancelPendingAccessibilityShortcutAction();
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false; // wake-up will be handled separately
                if (down) {
                    interceptPowerKeyDown(event, interactive);//攔截power鍵down 事件
                } else {
                    interceptPowerKeyUp(event, interactive, canceled);//攔截power鍵up 事件
                }
                break;
            }
        }
        ...
    }
    ...
     private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
     	...
     	// If the power key has still not yet been handled, then detect short
        // press, long press, or multi press and decide what to do.
        mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered
                || mA11yShortcutChordVolumeUpKeyTriggered || gesturedServiceIntercepted;
     	if (!mPowerKeyHandled) {
            if (interactive) {
                // When interactive, we're already awake.
                // Wait for a long press or for the button to be released to decide what to do.
                if (hasLongPressOnPowerBehavior()) {//長(zhǎng)按power鍵行為
                    if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
                        powerLongPress();//處理power鍵長(zhǎng)按
                    } else {
                        ...
                }
            }
    	}
    	...
     }

private void powerLongPress() {
        final int behavior = getResolvedLongPressOnPowerBehavior();//從配置文件中獲取power鍵長(zhǎng)按行為設(shè)置mLongPressOnPowerBehavior
        switch (behavior) {
            case LONG_PRESS_POWER_NOTHING:
                break;
            case LONG_PRESS_POWER_GLOBAL_ACTIONS:
                mPowerKeyHandled = true;
                performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                        "Power - Long Press - Global Actions");
                showGlobalActionsInternal();
                break;
            case LONG_PRESS_POWER_SHUT_OFF:
            case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
                mPowerKeyHandled = true;
                performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                        "Power - Long Press - Shut Off");
                sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
                mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
                break;
           ...
    }

    private int getResolvedLongPressOnPowerBehavior() {
        if (FactoryTest.isLongPressOnPowerOffEnabled()) {
            return LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;
        }
        return mLongPressOnPowerBehavior;
    }

mLongPressOnPowerBehavior = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_longPressOnPowerBehavior);

frameworks\base\core\res\res\values\config.xml

    <!-- Control the behavior when the user long presses the power button.
            0 - Nothing    											//不處理power鍵,即什么也不做
            1 - Global actions menu							//關(guān)機(jī)顯示全局行為菜單
            2 - Power off (with confirmation)				//關(guān)機(jī)前彈出對(duì)話框再次確認(rèn)
            3 - Power off (without confirmation)		//關(guān)機(jī)前不彈出對(duì)話框,直接關(guān)機(jī)
            4 - Go to voice assist								//轉(zhuǎn)到語言助手
            5 - Go to assistant (Settings.Secure.ASSISTANT)	轉(zhuǎn)到設(shè)置助手
    -->
    <integer name="config_longPressOnPowerBehavior">1</integer>//默認(rèn)為系統(tǒng)全局菜單

接著上面powerLongPress()往下走
frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java

private void powerLongPress() {
	    final int behavior = getResolvedLongPressOnPowerBehavior();
        switch (behavior) {
            case LONG_PRESS_POWER_NOTHING:
                break;
            case LONG_PRESS_POWER_GLOBAL_ACTIONS:
                mPowerKeyHandled = true;
                performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                        "Power - Long Press - Global Actions");
                showGlobalActionsInternal();//config_longPressOnPowerBehavior為1則調(diào)用mGlobalActions.showDialog();
                break;
            //config_longPressOnPowerBehavior為2則調(diào)用mWindowManagerFuncs.shutdown();
            case LONG_PRESS_POWER_SHUT_OFF:
            case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
                mPowerKeyHandled = true;
                performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                        "Power - Long Press - Shut Off");
                sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
                mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
                break;
           ...
    }

    void showGlobalActionsInternal() {
        if (mGlobalActions == null) {
            mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
        }
        final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
        mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
        // since it took two seconds of long press to bring this up,
        // poke the wake lock so they have some time to see the dialog.
        mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
    }

2.2、顯示關(guān)機(jī)確認(rèn)框界面

這里先按源碼流程case 1分析,case 2的后續(xù)定制關(guān)機(jī)界面中會(huì)介紹。
frameworks\base\services\core\java\com\android\server\policy\GlobalActions.java

    public void showDialog(boolean keyguardShowing, boolean deviceProvisioned) {
        if (DEBUG) Slog.d(TAG, "showDialog " + keyguardShowing + " " + deviceProvisioned);
        if (mGlobalActionsProvider != null && mGlobalActionsProvider.isGlobalActionsDisabled()) {
            return;
        }
        mKeyguardShowing = keyguardShowing;
        mDeviceProvisioned = deviceProvisioned;
        mShowing = true;
        if (mGlobalActionsAvailable) {//全局行為可使用
            mHandler.postDelayed(mShowTimeout, 5000);
            mGlobalActionsProvider.showGlobalActions();
        } else {
            // SysUI isn't alive, show legacy menu.SysUI 不可用則顯示傳統(tǒng)關(guān)機(jī)菜單
            ensureLegacyCreated();
            mLegacyGlobalActions.showDialog(mKeyguardShowing, mDeviceProvisioned);
        }
    }

沿著傳統(tǒng)關(guān)機(jī)流程繼續(xù)分析
frameworks\base\services\core\java\com\android\server\policy\LegacyGlobalActions.java

public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
        mKeyguardShowing = keyguardShowing;
        mDeviceProvisioned = isDeviceProvisioned;
        if (mDialog != null) {//關(guān)機(jī)對(duì)話框已存在
            mDialog.dismiss();
            mDialog = null;
            // Show delayed, so that the dismiss of the previous dialog completes
            mHandler.sendEmptyMessage(MESSAGE_SHOW);
        } else {
            handleShow();
        }
    }

    private void handleShow() {
        awakenIfNecessary();
        mDialog = createDialog();//創(chuàng)建新的對(duì)話框,加載關(guān)機(jī)選項(xiàng),設(shè)置點(diǎn)擊選項(xiàng)
        prepareDialog();//更新靜音、飛行等各種模式

        ...
            if (mDialog != null) {
                WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
                attrs.setTitle("LegacyGlobalActions");
                mDialog.getWindow().setAttributes(attrs);
                mDialog.show();
                mDialog.getWindow().getDecorView().setSystemUiVisibility(
                        View.STATUS_BAR_DISABLE_EXPAND);
            }
        }
    }

private ActionsDialog createDialog() {

        mItems = new ArrayList<Action>();
        String[] defaultActions = mContext.getResources().getStringArray(
                com.android.internal.R.array.config_globalActionsList);//設(shè)置默認(rèn)的全局行為選項(xiàng)

        ArraySet<String> addedKeys = new ArraySet<String>();
        for (int i = 0; i < defaultActions.length; i++) {
            String actionKey = defaultActions[i];
            if (addedKeys.contains(actionKey)) {
                // If we already have added this, don't add it again.
                continue;
            }
            if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {//關(guān)機(jī)模式
                mItems.add(new PowerAction(mContext, mWindowManagerFuncs));
            } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {//飛行模式
                mItems.add(mAirplaneModeOn);
            }
            ...
            addedKeys.add(actionKey);//將默認(rèn)關(guān)機(jī)選項(xiàng)加入global action menu
        }
		...
        ActionsDialog dialog = new ActionsDialog(mContext, params);
        dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
        dialog.getListView().setItemsCanFocus(true);
        dialog.getListView().setLongClickable(true);
        dialog.getListView().setOnItemLongClickListener(
                new AdapterView.OnItemLongClickListener() {
                    @Override
                    public boolean onItemLongClick(AdapterView<?> parent, View view, int position,
                            long id) {
                        final Action action = mAdapter.getItem(position);
                        if (action instanceof LongPressAction) {
                            return ((LongPressAction) action).onLongPress();//處理選項(xiàng)點(diǎn)擊事件
                        }
                        return false;
                    }
        });
       ...
        return dialog;
    }

全局行為模式定義如下:
frameworks\base\core\res\res\values\config.xml

<!-- Defines the default set of global actions. Actions may still be disabled or hidden based
         on the current state of the device.
         Each item must be one of the following strings:
         "power" = Power off
         "settings" = An action to launch settings
         "airplane" = Airplane mode toggle
         "bugreport" = Take bug report, if available
         "silent" = silent mode
         "users" = list of users
         "restart" = restart device
         "emergency" = Launch emergency dialer
         "lockdown" = Lock down device until the user authenticates
         "logout" =  Logout the current user
         -->
    <string-array translatable="false" name="config_globalActionsList">
        <item>power</item> 			//關(guān)機(jī)
        <item>restart</item>		//重啟
        <item>lockdown</item>		//鎖屏
        <item>logout</item>			//注銷賬戶
        <item>bugreport</item>		//上報(bào)錯(cuò)誤
        <item>screenshot</item>		//截屏
        <item>emergency</item>		//緊急
    </string-array>

接著PowerAction.java onLongPress()
frameworks\base\services\core\java\com\android\server\policy\PowerAction.java

    @Override
    public boolean onLongPress() {
        UserManager um = mContext.getSystemService(UserManager.class);
        if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
            mWindowManagerFuncs.rebootSafeMode(true);
            return true;
        }
        return false;
    }

frameworks\base\services\core\java\com\android\server\wm\WindowManagerService.java

@Override
    public void rebootSafeMode(boolean confirm) {
        // Pass in the UI context, since ShutdownThread requires it (to show UI).
        ShutdownThread.rebootSafeMode(ActivityThread.currentActivityThread().getSystemUiContext(),
                confirm);
    }

2.3、顯示關(guān)機(jī)進(jìn)度框

進(jìn)入關(guān)機(jī)線程ShutdownThread
frameworks\base\services\core\java\com\android\server\power\ShutdownThread.java

public static void rebootSafeMode(final Context context, boolean confirm) {
        ...
        shutdownInner(context, confirm);
    }

private static void shutdownInner(final Context context, boolean confirm) {
		...
        if (confirm) {
            final CloseDialogReceiver closer = new CloseDialogReceiver(context);
            if (sConfirmDialog != null) {
                sConfirmDialog.dismiss();
            }
            sConfirmDialog = new AlertDialog.Builder(context)//創(chuàng)建關(guān)機(jī)確認(rèn)對(duì)話框
                    .setTitle(mRebootSafeMode
                            ? com.android.internal.R.string.reboot_safemode_title
                            : com.android.internal.R.string.power_off)
                    .setMessage(resourceId)
                    .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            beginShutdownSequence(context);//選擇確認(rèn)關(guān)機(jī),開始執(zhí)行關(guān)機(jī)流程
                        }
                    })
                    .setNegativeButton(com.android.internal.R.string.no, null)
                    .create();
            closer.dialog = sConfirmDialog;
            sConfirmDialog.setOnDismissListener(closer);
            sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
            sConfirmDialog.show();
        } else {
            beginShutdownSequence(context);
        }
    }

    private static void beginShutdownSequence(Context context) {
		...
        /* If shutdown animation enabled, notify bootanimation module to play
           shutdown animation by set prop */
        final boolean shutdownAnimationEnabled = context.getResources()
                .getBoolean(com.android.internal.R.bool.config_shutdownAnimationEnabled);
        if (shutdownAnimationEnabled) {
            SystemProperties.set("sys.powerctl", "shutdownanim");
            SystemProperties.set("service.bootanim.exit", "0");
            SystemProperties.set("ctl.start", "bootanim");
        }
        sInstance.mProgressDialog = showShutdownDialog(context);//顯示關(guān)機(jī)進(jìn)度框
       	...
        if (SecurityLog.isLoggingEnabled()) {
            SecurityLog.writeEvent(SecurityLog.TAG_OS_SHUTDOWN);
        }
        // start the thread that initiates shutdown
        sInstance.mHandler = new Handler() {
        };
        sInstance.start();
    }

 private static ProgressDialog showShutdownDialog(Context context) {
        // Throw up a system dialog to indicate the device is rebooting / shutting down.
        ProgressDialog pd = new ProgressDialog(context);

        // Path 1: Reboot to recovery for update
        //   Condition: mReason startswith REBOOT_RECOVERY_UPDATE
        //
        //  Path 1a: uncrypt needed
        //   Condition: if /cache/recovery/uncrypt_file exists but
        //              /cache/recovery/block.map doesn't.
        //   UI: determinate progress bar (mRebootHasProgressBar == True)
        //
        // * Path 1a is expected to be removed once the GmsCore shipped on
        //   device always calls uncrypt prior to reboot.
        //
        //  Path 1b: uncrypt already done
        //   UI: spinning circle only (no progress bar)
        //
        // Path 2: Reboot to recovery for factory reset
        //   Condition: mReason == REBOOT_RECOVERY
        //   UI: spinning circle only (no progress bar)
        //
        // Path 3: Regular reboot / shutdown
        //   Condition: Otherwise
        //   UI: spinning circle only (no progress bar)

        // mReason could be "recovery-update" or "recovery-update,quiescent".
        if (mReason != null && mReason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) {
            // We need the progress bar if uncrypt will be invoked during the
            // reboot, which might be time-consuming.
            mRebootHasProgressBar = RecoverySystem.UNCRYPT_PACKAGE_FILE.exists()
                    && !(RecoverySystem.BLOCK_MAP_FILE.exists());
            pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));
            if (mRebootHasProgressBar) {
                pd.setMax(100);
                pd.setProgress(0);
                pd.setIndeterminate(false);
                pd.setProgressNumberFormat(null);
                pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
                pd.setMessage(context.getText(
                            com.android.internal.R.string.reboot_to_update_prepare));
            } else {
                if (showSysuiReboot()) {
                    return null;
                }
                pd.setIndeterminate(true);
                pd.setMessage(context.getText(
                            com.android.internal.R.string.reboot_to_update_reboot));
            }
        } else if (mReason != null && mReason.equals(PowerManager.REBOOT_RECOVERY)) {
            if (RescueParty.isAttemptingFactoryReset()) {
                // We're not actually doing a factory reset yet; we're rebooting
                // to ask the user if they'd like to reset, so give them a less
                // scary dialog message.
                pd.setTitle(context.getText(com.android.internal.R.string.power_off));
                pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
                pd.setIndeterminate(true);
            } else {
                // Factory reset path. Set the dialog message accordingly.
                pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
                pd.setMessage(context.getText(
                            com.android.internal.R.string.reboot_to_reset_message));
                pd.setIndeterminate(true);
            }
        } else {
            if (showSysuiReboot()) {
                return null;
            }
            pd.setTitle(context.getText(com.android.internal.R.string.power_off));
            pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
            pd.setIndeterminate(true);
        }
        pd.setCancelable(false);
        pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
		pd.show();
        return pd;
    }

pd.show()將各自模式的關(guān)機(jī)進(jìn)度對(duì)話框顯示,直至系統(tǒng)關(guān)機(jī)。至此,定制化關(guān)機(jī)界面所需處理的流程到這里就可以結(jié)束了,但為了進(jìn)一步了解關(guān)機(jī)流程我們繼續(xù)深入follow。
在beginShutdownSequence()方法最后開啟了一個(gè)線程,執(zhí)行了run()
frameworks\base\services\core\java\com\android\server\power\ShutdownThread.java

/**
     * Makes sure we handle the shutdown gracefully.
     * Shuts off power regardless of radio state if the allotted time has passed.
     */
    public void run() {
        ...
        final IActivityManager am =
                IActivityManager.Stub.asInterface(ServiceManager.checkService("activity"));
        if (am != null) {
            try {
                am.shutdown(MAX_BROADCAST_TIME);//關(guān)機(jī)前關(guān)閉AMS
            } catch (RemoteException e) {
            }
        }
        if (mRebootHasProgressBar) {
            sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
        }
        ...
        final PackageManagerService pm = (PackageManagerService)
            ServiceManager.getService("package");
        if (pm != null) {
            pm.shutdown();//關(guān)機(jī)前關(guān)閉PMS
        }
        // Shutdown radios.
        shutdownRadios(MAX_RADIO_WAIT_TIME);//關(guān)機(jī)前關(guān)閉radios
        if (mRebootHasProgressBar) {
            sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
        }
		...
        // Remaining work will be done by init, including vold shutdown
        rebootOrShutdown(mContext, mReboot, mReason);//進(jìn)入重啟或關(guān)機(jī)
    }

public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
        ...           
        final boolean shutdownAnimEnabled = context.getResources().getBoolean(
                com.android.internal.R.bool.config_shutdownAnimationEnabled);//是否啟用關(guān)機(jī)動(dòng)畫

        if (shutdownAnimEnabled) {
            final int shutdownAnimDuration = context.getResources().getInteger(
                    com.android.internal.R.integer.config_shutdownAnimationDurationMs);
            int sleepDuration = reboot ? shutdownAnimDuration
                    : shutdownAnimDuration - SHUTDOWN_VIBRATE_MS;
            try {
                if (sleepDuration > 0) {
                    Thread.sleep(sleepDuration);
                }
            } catch (InterruptedException unused) {
            }
        }

        if (reboot) {
            Log.i(TAG, "Rebooting, reason: " + reason);
            PowerManagerService.lowLevelReboot(reason);//reboot重啟流程
            Log.e(TAG, "Reboot failed, will attempt shutdown instead");
            reason = null;
        } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
            // vibrate before shutting down
            Vibrator vibrator = new SystemVibrator(context);
            try {
                vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
            } catch (Exception e) {
                // Failure to vibrate shouldn't interrupt shutdown.  Just log it.
                Log.w(TAG, "Failed to vibrate during shutdown.", e);
            }

        // Shutdown power
        Log.i(TAG, "Performing low-level shutdown...");
        PowerManagerService.lowLevelShutdown(reason);
    }

frameworks\base\services\core\java\com\android\server\power\PowerManagerService.java

public static void lowLevelShutdown(String reason) {
        if (reason == null) {
            reason = "";
        }
        SystemProperties.set("sys.powerctl", "shutdown," + reason);//設(shè)置系統(tǒng)控制屬性sys.powerctl=shutdown
    }

在設(shè)置系統(tǒng)屬性流程中對(duì)于特殊屬性值的改變需進(jìn)行監(jiān)聽,做特殊處理.
具體屬性設(shè)置流程可以參考我的另一篇文檔:Android 系統(tǒng)屬性(SystemProperties)介紹
system\core\init\property_service.cpp

static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
    ...
    property_changed(name, value);
    return PROP_SUCCESS;
}

property_changed()中獲取sys.powerctl值傳給shutdown_command,并設(shè)置關(guān)機(jī)標(biāo)志位do_shutdown為true
system\core\init\init.cpp

void property_changed(const std::string& name, const std::string& value) {
    // If the property is sys.powerctl, we bypass the event queue and immediately handle it.
    // This is to ensure that init will always and immediately shutdown/reboot, regardless of
    // if there are other pending events to process or if init is waiting on an exec service or
    // waiting on a property.
    // In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
    // commands to be executed.
    if (name == "sys.powerctl") {
        // Despite the above comment, we can't call HandlePowerctlMessage() in this function,
        // because it modifies the contents of the action queue, which can cause the action queue
        // to get into a bad state if this function is called from a command being executed by the
        // action queue.  Instead we set this flag and ensure that shutdown happens before the next
        // command is run in the main init loop.
        // TODO: once property service is removed from init, this will never happen from a builtin,
        // but rather from a callback from the property service socket, in which case this hack can
        // go away.
        shutdown_command = value;
        do_shutdown = true;//設(shè)置do shutdown關(guān)機(jī)標(biāo)志
    }
	...
}

int SecondStageMain(int argc, char** argv) {
	...
	//監(jiān)聽do_shutdown值變化,為true時(shí)調(diào)用HandlePowerctlMessage()
	while (true) {
        // By default, sleep until something happens.
        auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
        if (do_shutdown && !shutting_down) {
            do_shutdown = false;
            if (HandlePowerctlMessage(shutdown_command)) {//發(fā)送關(guān)機(jī)msg
                shutting_down = true;
            }
        }
        ...
    }
    ...
}

system\core\init\reboot.cpp

bool HandlePowerctlMessage(const std::string& command) {
    ...
    if (cmd_params.size() > 3) {
        command_invalid = true;
    } else if (cmd_params[0] == "shutdown") {//關(guān)機(jī)
        cmd = ANDROID_RB_POWEROFF;
        if (cmd_params.size() == 2) {
            if (cmd_params[1] == "userrequested") {
                // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
                // Run fsck once the file system is remounted in read-only mode.
                run_fsck = true;
            } else if (cmd_params[1] == "thermal") {
                // Turn off sources of heat immediately.
                TurnOffBacklight();//息屏,關(guān)背光
                // run_fsck is false to avoid delay
                cmd = ANDROID_RB_THERMOFF;
            }
        }
    } else if (cmd_params[0] == "reboot") {//重啟
        cmd = ANDROID_RB_RESTART2;
        if (cmd_params.size() >= 2) {
            reboot_target = cmd_params[1];
            // adb reboot fastboot should boot into bootloader for devices not
            // supporting logical partitions.
            if (reboot_target == "fastboot" &&
                !android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
                reboot_target = "bootloader";
            }
            // When rebooting to the bootloader notify the bootloader writing
            // also the BCB.
            ...
    auto shutdown_handler = [cmd, command, reboot_target, run_fsck](const BuiltinArguments&) {
        DoReboot(cmd, command, reboot_target, run_fsck);
        return Success();
    };
    ...
    return true;
}

system\core\init\reboot_utils.cpp

void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
    LOG(INFO) << "Reboot ending, jumping to kernel";

    if (!IsRebootCapable()) {
        // On systems where init does not have the capability of rebooting the
        // device, just exit cleanly.
        exit(0);
    }
    switch (cmd) {
        case ANDROID_RB_POWEROFF:
            reboot(RB_POWER_OFF);//進(jìn)行關(guān)機(jī)
            break;
			...
    }
    // In normal case, reboot should not return.
    PLOG(ERROR) << "reboot call returned";
    abort();
}

reboot后續(xù)調(diào)用kernel相關(guān)進(jìn)行硬件層面的關(guān)機(jī)流程,到這里關(guān)機(jī)流程跑完了,在流程中也發(fā)現(xiàn)了息屏動(dòng)作是如何設(shè)置的。

3、定制關(guān)機(jī)界面

從上述關(guān)機(jī)流程中不難看出有多種方式實(shí)現(xiàn)關(guān)機(jī)界面的定制化,這里給出兩種方案。需要注意的是定制的關(guān)機(jī)界面實(shí)際上是兩個(gè)界面:關(guān)機(jī)關(guān)機(jī)確認(rèn)界面和關(guān)機(jī)進(jìn)度界面,所以在定制時(shí)需要將兩個(gè)界面都替換,并保持風(fēng)格統(tǒng)一。

3.1、GobalActionsMenu關(guān)機(jī)界面定制:

frameworks\base\services\core\java\com\android\server\policy\GlobalActions.java

public GlobalActions(Context context, WindowManagerFuncs windowManagerFuncs) {
        mContext = context;
        mHandler = new Handler();
        mWindowManagerFuncs = windowManagerFuncs;

        mGlobalActionsProvider = LocalServices.getService(GlobalActionsProvider.class);
        if (mGlobalActionsProvider != null) {
            mGlobalActionsProvider.setGlobalActionsListener(this);//注冊(cè)監(jiān)聽
        } else {
            Slog.i(TAG, "No GlobalActionsProvider found, defaulting to LegacyGlobalActions");
        }
    }
	...
    public void showDialog(boolean keyguardShowing, boolean deviceProvisioned) {
        ...
        mKeyguardShowing = keyguardShowing;
        mDeviceProvisioned = deviceProvisioned;
        mShowing = true;
        if (mGlobalActionsAvailable) {//全局行為可使用
            mHandler.postDelayed(mShowTimeout, 5000);
            mGlobalActionsProvider.showGlobalActions();//顯示全局關(guān)機(jī)選項(xiàng)界面
        } else {
            // SysUI isn't alive, show legacy menu.SysUI 不可用則顯示傳統(tǒng)關(guān)機(jī)菜單
            ensureLegacyCreated();
            mLegacyGlobalActions.showDialog(mKeyguardShowing, mDeviceProvisioned);
        }
    }

frameworks\base\packages\SystemUI\src\com\android\systemui\globalactions\GlobalActionsImpl.java

@Override
    public void showGlobalActions(GlobalActionsManager manager) {
        if (mDisabled) return;
        if (mGlobalActions == null) {
            mGlobalActions = new GlobalActionsDialog(mContext, manager);
        }
        mGlobalActions.showDialog(mKeyguardMonitor.isShowing(),
                mDeviceProvisionedController.isDeviceProvisioned(),
                mPanelExtension.get());
        KeyguardUpdateMonitor.getInstance(mContext).requestFaceAuth();
    }

frameworks\base\packages\SystemUI\src\com\android\systemui\globalactions\GlobalActionsDialog.java

   public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned,
            GlobalActionsPanelPlugin panelPlugin) {
        mKeyguardShowing = keyguardShowing;
        mDeviceProvisioned = isDeviceProvisioned;
        mPanelPlugin = panelPlugin;
        if (mDialog != null) {
            mDialog.dismiss();
            mDialog = null;
            // Show delayed, so that the dismiss of the previous dialog completes
            mHandler.sendEmptyMessage(MESSAGE_SHOW);
        } else {
            handleShow();
        }
    }
	...
	//在這里才真正實(shí)例化關(guān)機(jī)dialog并show出來,因此在這里進(jìn)行定制化
    private void handleShow() {
        awakenIfNecessary();
        mDialog = createDialog();
        //Mart!nHu Patch Start
        Dialog mNewDialog = ceateNewDialog();//創(chuàng)建定制關(guān)機(jī)選擇框界面
        //Mart!nHu Patch End
        prepareDialog();

        // If we only have 1 item and it's a simple press action, just do this action.
        if (mAdapter.getCount() == 1
                && mAdapter.getItem(0) instanceof SinglePressAction
                && !(mAdapter.getItem(0) instanceof LongPressAction)) {
            ((SinglePressAction) mAdapter.getItem(0)).onPress();
        } else {
            WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
            attrs.setTitle("ActionsDialog");
            attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
            mDialog.getWindow().setAttributes(attrs);
            //Mart!nHu Patch Start
            //mDialog.show();//隱藏默認(rèn)關(guān)機(jī)界面
            mNewDialog.show();//顯示定制關(guān)機(jī)界面
            //Mart!nHu Patch End
            mWindowManagerFuncs.onGlobalActionsShown();
        }
    }
//Mart!nHu Patch Start
    private Dialog createShutDownConfirmDialog(){
        if(mShutDownConfirmDialog != null){
            return mShutDownConfirmDialog;
        }
        mShutDownConfirmDialog = new Dialog(mContext,com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
                    // Window initialization
        Window window = mShutDownConfirmDialog.getWindow();
        window.requestFeature(Window.FEATURE_NO_TITLE);
        // Inflate the decor view, so the attributes below are not overwritten by the theme.
        window.getDecorView();
        window.setGravity(Gravity.CENTER);
        window.setBackgroundDrawableResource(android.R.color.transparent);
        window.getAttributes().systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
        window.setLayout(WRAP_CONTENT, WRAP_CONTENT);
        window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
        window.addFlags(
                    WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                    | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
                    | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                    | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
        window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
        mShutDownConfirmDialog.setContentView(com.android.systemui.R.layout.shutdown_confirm_dialog);//使用定制布局
        TextView tv_confirm = mShutDownConfirmDialog.findViewById(com.android.systemui.R.id.btn_confirm);
        TextView tv_cancel = mShutDownConfirmDialog.findViewById(com.android.systemui.R.id.btn_cancel);
        //設(shè)置點(diǎn)擊事件
        tv_confirm.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mShutDownConfirmDialog.dismiss();
                mWindowManagerFuncs.shutdown();

            }
        });
        tv_cancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mShutDownConfirmDialog.dismiss();
            }
        });
        return mShutDownConfirmDialog;
     }
//Mart!nHu Patch End

3.2、ShutdownThread關(guān)機(jī)界面定制

frameworks\base\core\res\res\values\config.xml

    <!-- Control the behavior when the user long presses the power button.
            0 - Nothing    											//不處理power鍵,即什么也不做
            1 - Global actions menu							//關(guān)機(jī)顯示全局行為菜單
            2 - Power off (with confirmation)				//關(guān)機(jī)前彈出對(duì)話框再次確認(rèn)
            3 - Power off (without confirmation)		//關(guān)機(jī)前不彈出對(duì)話框,直接關(guān)機(jī)
            4 - Go to voice assist								//轉(zhuǎn)到語言助手
            5 - Go to assistant (Settings.Secure.ASSISTANT)	轉(zhuǎn)到設(shè)置助手
    -->
    //Mart!nHu Patch Start  
    <integer name="config_longPressOnPowerBehavior">2</integer>//選用帶確認(rèn)的關(guān)機(jī)界面
    //Mart!nHu Patch End

修改后系統(tǒng)關(guān)機(jī)界面如下:
Android 10關(guān)機(jī)界面定制

在powerLongPress()調(diào)用mWindowManagerFuncs.shutdown()
frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java

private void powerLongPress() {
        final int behavior = getResolvedLongPressOnPowerBehavior();//從配置文件中獲取power鍵長(zhǎng)按行為設(shè)置mLongPressOnPowerBehavior
        switch (behavior) {
            case LONG_PRESS_POWER_NOTHING:
                break;
            case LONG_PRESS_POWER_GLOBAL_ACTIONS:
                mPowerKeyHandled = true;
                performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                        "Power - Long Press - Global Actions");
                showGlobalActionsInternal();
                break;
            case LONG_PRESS_POWER_SHUT_OFF:
            case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
                mPowerKeyHandled = true;
                mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
                break;
           ...
    }

frameworks\base\services\core\java\com\android\server\wm\WindowManagerService.java

@Override
    public void shutdown(boolean confirm) {
        // Pass in the UI context, since ShutdownThread requires it (to show UI).
        ShutdownThread.shutdown(ActivityThread.currentActivityThread().getSystemUiContext(),
                PowerManager.SHUTDOWN_USER_REQUESTED, confirm);
    }

frameworks\base\services\core\java\com\android\server\power\ShutdownThread.java

//Mart!nHu Patch Start
private static Dialog sCustomizedConfirmDialog;
//Mart!nHu Patch End

public static void shutdown(final Context context, String reason, boolean confirm) {
        mReboot = false;
        mRebootSafeMode = false;
        mReason = reason;
        shutdownInner(context, confirm);
    }

    private static void shutdownInner(final Context context, boolean confirm) {
        ...

        final int longPressBehavior = context.getResources().getInteger(
                        com.android.internal.R.integer.config_longPressOnPowerBehavior);
        final int resourceId = mRebootSafeMode
                ? com.android.internal.R.string.reboot_safemode_confirm
                : (longPressBehavior == 2
                        ? com.android.internal.R.string.shutdown_confirm_question
                        : com.android.internal.R.string.shutdown_confirm);

        Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);

        if (confirm) {
            final CloseDialogReceiver closer = new CloseDialogReceiver(context);
            if (sConfirmDialog != null) {
                sConfirmDialog.dismiss();
            }
            sConfirmDialog = new AlertDialog.Builder(context)
                    .setTitle(mRebootSafeMode
                            ? com.android.internal.R.string.reboot_safemode_title
                            : com.android.internal.R.string.power_off)
                    .setMessage(resourceId)
                    .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            beginShutdownSequence(context);
                        }
                    })
                    .setNegativeButton(com.android.internal.R.string.no, null)
                    .create();
            closer.dialog = sConfirmDialog;
            sConfirmDialog.setOnDismissListener(closer);
            sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
            //Mart!nHu Patch Start
            //sConfirmDialog.show();//隱藏默認(rèn)關(guān)機(jī)確認(rèn)框
            if(sCustomizedConfirmDialog != null) {
                sCustomizedConfirmDialog.dismiss();
                sCustomizedConfirmDialog = null;
            }
            sCustomizedConfirmDialog = createCustomizedConfirmDialog(context);//創(chuàng)建定制關(guān)機(jī)確認(rèn)框
            closer.dialog = sCustomizedConfirmDialog;
            sCustomizedConfirmDialog.setOnDismissListener(closer);
            sCustomizedConfirmDialog.show();//顯示定制關(guān)機(jī)界面
        } else {
            beginShutdownSequence(context);
        }
    }
    
    private static Dialog CustomizedConfirmDialog(final Context context) {
        sCustomizedConfirmDialog = new Dialog(context);
        Window window = sCustomizedConfirmDialog.getWindow();
        setCustomizedShutdownWindow(window);//對(duì)定制dialog所在window進(jìn)行配置
        sCustomizedConfirmDialog.setContentView(com.android.internal.R.layout.shutdown_confirm_dialog);
        sCustomizedConfirmDialog.setCancelable(false);//設(shè)置點(diǎn)擊空白處不關(guān)閉dialog
        TextView shutdownConfirmMsg = sCustomizedConfirmDialog.findViewById(com.android.internal.R.id.tv_shutdown_confirm_msg);
        TextView tv_confirm = sCustomizedConfirmDialog.findViewById(com.android.internal.R.id.btn_confirm);
        TextView tv_cancel = sCustomizedConfirmDialog.findViewById(com.android.internal.R.id.btn_cancel);
        shutdownConfirmMsg.setText(com.android.internal.R.string.shutdown_confirm_question);
        tv_confirm.setText(com.android.internal.R.string.yes);
        tv_cancel.setText(com.android.internal.R.string.no);
        tv_confirm.setOnClickListener(new View.OnClickListener() { 
            @Override
            public void onClick(View view) {
                sCustomizedConfirmDialog.dismiss();
                beginShutdownSequence(context);//選擇確認(rèn)后執(zhí)行后續(xù)關(guān)機(jī)流程
            }
        });
        tv_cancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                sCustomizedConfirmDialog.dismiss();
            }
        });
        return sCustomizedConfirmDialog;
    }

    private static void setCustomizedShutdownWindow(Window window) {
        window.requestFeature(Window.FEATURE_NO_TITLE);//去掉window標(biāo)題欄
        window.getDecorView();
        window.setGravity(Gravity.CENTER);//居中顯示
        window.setBackgroundDrawableResource(android.R.color.transparent);//設(shè)置背景透明
        window.getAttributes().systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
        window.setLayout(WRAP_CONTENT, WRAP_CONTENT);
        window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
        window.addFlags(
                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
                | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
        window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
    }

    private static void beginShutdownSequence(Context context) {
		...
        /* If shutdown animation enabled, notify bootanimation module to play
           shutdown animation by set prop */
        final boolean shutdownAnimationEnabled = context.getResources()
                .getBoolean(com.android.internal.R.bool.config_shutdownAnimationEnabled);
        if (shutdownAnimationEnabled) {
            SystemProperties.set("sys.powerctl", "shutdownanim");
            SystemProperties.set("service.bootanim.exit", "0");
            SystemProperties.set("ctl.start", "bootanim");
        }
        sInstance.mProgressDialog = showShutdownDialog(context);//顯示關(guān)機(jī)進(jìn)度框,這里也需要進(jìn)行定制
       	...
        // start the thread that initiates shutdown
        sInstance.mHandler = new Handler() {
        };
        sInstance.start();
    }

private static ProgressDialog showShutdownDialog(Context context) {
        // Throw up a system dialog to indicate the device is rebooting / shutting down.
        ProgressDialog pd = new ProgressDialog(context);
		...
        } else {
            if (showSysuiReboot()) {
                return null;
            }
            //Mart!nHu Patch Start
            pd.setTitle(context.getText(com.android.internal.R.string.power_off));
            pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
            pd.setIndeterminate(true);
            sCustomizedShuttingDownDialog = createCustomizedShuttingDownDialog(context);//創(chuàng)建定制關(guān)機(jī)進(jìn)度框
        }
        pd.setCancelable(false);
        pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
        if(sCustomizedShuttingDownDialog != null) {
            sCustomizedShuttingDownDialog.show();
        } else {
            pd.show();
        }
        return pd;
    }

    private static Dialog createCustomizedShuttingDownDialog(final Context context) {
        if(sCustomizedShuttingDownDialog != null) {
            return sCustomizedShuttingDownDialog;
        }
        sCustomizedShuttingDownDialog = new Dialog(context);
        Window window = sCustomizedShuttingDownDialog.getWindow();
        setCustomizedShutdownWindow(window);
        sCustomizedShuttingDownDialog.setContentView(com.android.internal.R.layout.shuttingdown_dialog);
        sCustomizedShuttingDownDialog.setCancelable(false);
        ImageView shuttingDownImage = sCustomizedShuttingDownDialog.findViewById(com.android.internal.R.id.tv_shutting_down);
        //定制關(guān)機(jī)動(dòng)畫
        AnimatedImageDrawable shuttingDownGif = (AnimatedImageDrawable) context.getDrawable(com.android.internal.R.drawable.shutting_down);
        shuttingDownImage.setImageDrawable(shuttingDownGif);
        shuttingDownGif.start();//播放關(guān)機(jī)動(dòng)畫
        TextView message = sCustomizedShuttingDownDialog.findViewById(com.android.internal.R.id.tv_shutting_down_msg);
        message.setText(com.android.internal.R.string.shutdown_progress);
        return sCustomizedShuttingDownDialog;
    }
     //Mart!nHu Patch End

4、總結(jié)

本文從代碼流程角度,大致的梳理了系統(tǒng)power鍵關(guān)機(jī)從framework開始的后續(xù)流程,通過代碼流程了解到:文章來源地址http://www.zghlxwxcb.cn/news/detail-402061.html

  • 系統(tǒng)默認(rèn)關(guān)機(jī)界面有多種樣式及模式
  • 關(guān)機(jī)界面加載關(guān)機(jī)選項(xiàng)的實(shí)現(xiàn)過程
  • 通過設(shè)置sys.powerctl=shutdown,init進(jìn)程循環(huán)監(jiān)聽該屬性變化并觸發(fā)shutdown流程
  • 定制關(guān)機(jī)界面通常需要定制關(guān)機(jī)確認(rèn)界面以及關(guān)機(jī)進(jìn)度條界面

到了這里,關(guān)于Android 10關(guān)機(jī)界面定制的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Android 12.0 原生SystemUI下拉通知欄UI背景設(shè)置為圓角背景的定制(二)

    ?在12.0的系統(tǒng)rom定制化開發(fā)中,在原生系統(tǒng)SystemUI下拉狀態(tài)欄的下拉通知欄的背景默認(rèn)是白色四角的背景, 由于在產(chǎn)品設(shè)計(jì)中,在對(duì)下拉通知欄通知的背景需要把四角背景默認(rèn)改成圓角背景,所以就需要分析系統(tǒng)原生下拉通知欄的每條通知的默認(rèn)背景, 然后通過systemui的通知

    2024年02月08日
    瀏覽(34)
  • Android 9.0 原生SystemUI下拉通知欄UI背景設(shè)置為圓角背景的定制(二)

    Android 9.0 原生SystemUI下拉通知欄UI背景設(shè)置為圓角背景的定制(二)

    ?在9.0的系統(tǒng)rom定制化開發(fā)中,在原生系統(tǒng)SystemUI下拉狀態(tài)欄的通知欄的背景是默認(rèn)白色四角的背景,由于在產(chǎn)品設(shè)計(jì)中,需要把四角背景默認(rèn)改成圓角背景,所以就需要分析系統(tǒng)原生下拉通知欄的每條通知的默認(rèn)背景,然后通過熟悉systemui的通知欄流程,設(shè)置默認(rèn)下拉狀態(tài)欄

    2024年02月05日
    瀏覽(26)
  • Android 9.0 原生SystemUI下拉通知欄UI背景設(shè)置為圓角背景的定制(一)

    Android 9.0 原生SystemUI下拉通知欄UI背景設(shè)置為圓角背景的定制(一)

    ?在9.0的系統(tǒng)rom產(chǎn)品定制化開發(fā)中,在原生系統(tǒng)SystemUI下拉狀態(tài)欄的通知欄的通知背景默認(rèn)是白色四角的背景, 由于在產(chǎn)品設(shè)計(jì)中,需要把四角背景默認(rèn)改成圓角背景,所以就需要分析系統(tǒng)原生下拉通知欄的每條通知的默認(rèn)背景, 然后通知顯示流程,設(shè)置默認(rèn)下拉狀態(tài)欄UI中

    2024年02月01日
    瀏覽(33)
  • 項(xiàng)目開發(fā)日志(登錄界面):2. LoginTitle組件

    項(xiàng)目開發(fā)日志(登錄界面):2. LoginTitle組件

    ????????????????屬性 屬性名 含義 類型 是否必填 默認(rèn)值 welcomeTitle 歡迎標(biāo)語 String 是 無 mainTitle 標(biāo)題 String 是 無 ????????????????樣式 ????????????????????????@mainColor - 主題顏色 ? ? ? ? 頁面參考:yostar通行證

    2024年02月19日
    瀏覽(15)
  • Android 11.0 framework關(guān)于systemUI定制之導(dǎo)航欄透明背景的功能實(shí)現(xiàn)

    Android 11.0 framework關(guān)于systemUI定制之導(dǎo)航欄透明背景的功能實(shí)現(xiàn)

    在11.0的系統(tǒng)rom產(chǎn)品定制化開發(fā)中,在對(duì)于系統(tǒng)原生SystemUI的導(dǎo)航欄背景在沉浸式導(dǎo)航欄的 情況下默認(rèn)是會(huì)隨著背景顏色的變化而改變的,在一些特定背景下導(dǎo)航欄的背景也是會(huì)改變的,所以由于產(chǎn)品開發(fā)需要 要求需要設(shè)置導(dǎo)航欄背景為透明的,所以就需要在Activity創(chuàng)建的時(shí)候

    2024年02月04日
    瀏覽(95)
  • 將本機(jī)開發(fā)項(xiàng)目上傳到Gitee庫(kù)的操作演示及說明

    將本機(jī)開發(fā)項(xiàng)目上傳到Gitee庫(kù)的操作演示及說明

    下載Git這個(gè)工具并在你的電腦中安裝,Git工具下載鏈接如下: Git工具下載 訪問Gitee的官網(wǎng)并完成注冊(cè)添加好相關(guān)公鑰,官網(wǎng)地址如下: Gitee官網(wǎng) Git命令窗口中復(fù)制命令需要使用的快捷鍵為:Shift + Insert 在本機(jī) 英文目錄 下創(chuàng)建一個(gè)空文件夾并右鍵打開Git命令工具,如圖: 打開

    2024年03月18日
    瀏覽(26)
  • 【安卓開發(fā)】開源Notepad記事本APP項(xiàng)目(完整代碼+說明文檔)

    【安卓開發(fā)】開源Notepad記事本APP項(xiàng)目(完整代碼+說明文檔)

    作業(yè):記事本APP。要求: (1) 給出實(shí)現(xiàn)源程序,對(duì)源程序加以說明; (2) 給出屏幕運(yùn)行結(jié)果的截圖,運(yùn)行結(jié)果含有個(gè)人特征; (3) 用word文檔提交,文件名:班號(hào)-姓名-學(xué)號(hào)后3位-作業(yè)。 1.頁面設(shè)計(jì)美觀,可以正確顯示數(shù)據(jù)(20分)。 2.實(shí)現(xiàn)數(shù)據(jù)保存、查詢、修改和讀取功能(20分

    2023年04月12日
    瀏覽(25)
  • 【AUTOSAR】BMS開發(fā)實(shí)際項(xiàng)目講解(七)----BMS硬件架構(gòu)設(shè)計(jì)概要說明

    概要說明 / General Information 文檔目的 / Document Purpose 本文檔定義 BMS 平臺(tái)的硬件架構(gòu)設(shè)計(jì),包含 SBC 電源模塊,模擬信號(hào)采集模塊,數(shù)字信號(hào)采集模塊,高壓互鎖模塊, MCU 最小系統(tǒng), CAN 收發(fā)器模塊, HSD , LSD ,高壓及絕緣采集模塊,高壓側(cè) MCU 系統(tǒng),前端采樣芯片及通信轉(zhuǎn)換

    2024年02月12日
    瀏覽(23)
  • 軟件開發(fā)項(xiàng)目文檔系列之八數(shù)據(jù)庫(kù)設(shè)計(jì)說明書

    軟件開發(fā)項(xiàng)目文檔系列之八數(shù)據(jù)庫(kù)設(shè)計(jì)說明書

    數(shù)據(jù)庫(kù)設(shè)計(jì)說明書是一個(gè)關(guān)鍵文檔,它提供了有關(guān)數(shù)據(jù)庫(kù)的詳細(xì)信息,包括設(shè)計(jì)、結(jié)構(gòu)、運(yùn)行環(huán)境、數(shù)據(jù)安全、管理和維護(hù)等方面的內(nèi)容。 引言部分,簡(jiǎn)要介紹數(shù)據(jù)庫(kù)設(shè)計(jì)說明書的目的和內(nèi)容。這部分通常包括以下內(nèi)容: 引言的目的:解釋為什么需要數(shù)據(jù)庫(kù)設(shè)計(jì)說明書,它

    2024年02月06日
    瀏覽(20)
  • Android10 ROM定制導(dǎo)讀

    本專欄出現(xiàn)的原因: 由于從事系統(tǒng)開發(fā)也有了不少的時(shí)間,但是自己研究的模塊內(nèi)容又比較多,僅僅自己的筆記對(duì)自己的成長(zhǎng)和對(duì)模塊的掌握無法達(dá)到熟練和精通,所以有了自己寫博客整理的想法。通過寫博客沉淀自己,和各位大佬互相幫助一起進(jìn)步。 本專欄主要內(nèi)容:

    2024年02月10日
    瀏覽(23)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包