Android 2.2 滑蓋(lid)的影響

來源:互聯網
上載者:User

前段時間項目之中遇到了按鍵燈不亮的問題,稍微看了一下framework的代碼,發現是因為滑蓋的開關狀態對其有影響。

在PhoneWindowManager中有定義:

    boolean mLidOpen;

這裡沒有初始化,所以為false,而且硬體確實沒有裝滑蓋,所以PhoneWindowManager對就不會對mLidOpen進行更新。

    void readLidState() {        try {            int sw = mWindowManager.getSwitchState(RawInputEvent.SW_LID);  //return -1            if (sw >= 0) {                mLidOpen = sw == 0;            }        } catch (RemoteException e) {            // Ignore        }    }

這裡,mLidOpen也不會更新,輸入事件不會主動上報。

    /** {@inheritDoc} */    public boolean preprocessInputEventTq(RawInputEvent event) {        switch (event.type) {            case RawInputEvent.EV_SW:                if (event.keycode == RawInputEvent.SW_LID) {                    // lid changed state                    mLidOpen = event.value == 0;                    boolean awakeNow = mKeyguardMediator.doLidChangeTq(mLidOpen);                    updateRotation(Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE);                    // ...省略部分代碼                }        }        return false;    }

因此,PhoneWindowManager會調用PowerManagerService的setKeyboardVisibility

    /** {@inheritDoc} */    public void adjustConfigurationLw(Configuration config) {        readLidState();        final boolean lidOpen = !KEYBOARD_ALWAYS_HIDDEN && mLidOpen;        mPowerManager.setKeyboardVisibility(lidOpen);        // ...省略部分代碼    }

在PowerManagerService中,會根據滑蓋狀態,決定鍵盤的可見狀態。

    public void setKeyboardVisibility(boolean visible) {        synchronized (mLocks) {            if (mKeyboardVisible != visible) {                mKeyboardVisible = visible;                // ...省略部分代碼            }        }    }

也就是說,如果滑蓋沒有開啟,鍵盤是不可見的(用過滑蓋手機的都知道)。

這裡要命的就是,PowerManagerService會根據鍵盤的可見狀態,來決定亮不亮鍵盤燈,因此,我們的鍵盤燈一直是不亮的。

    private int applyKeyboardState(int state) {        int brightness = -1;        if (!mKeyboardVisible) {            brightness = 0;        }        // ...省略部分代碼    }

現在反思一下,我們要亮的是按鍵燈,不是鍵盤燈,現在兩個燈等同為一個燈,鍵盤跟按鍵還是有區別的吧,android沒有考慮到按鍵燈。所以我們只能把鍵盤燈當成按鍵燈使用了。

解決方案看起來很簡單,就是回到問題的源頭,在PhoneWindowManager中,就把mLidOpen的值初始化為true,這樣它就一直會保持在true的狀態,相當於滑蓋一直是開的,因此鍵盤就一隻是可見的,鍵盤燈就會亮了。

但是事情遠遠沒有這麼簡單,自從改了這個以後,問題接踵而至,各種感應器失效,由於感測起也是剛開始調,因此不會不知道是就是因為改了這個值引起的。

1、重力感應器檢測到方向有改變時,系統會調用PhoneWindowManager的rotationForOrientationLw方法,繼而根據角度更新介面。

    public int rotationForOrientationLw(int orientation, int lastRotation,            boolean displayEnabled) {        synchronized (mLock) {            switch (orientation) {                case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:                    //always return landscape if orientation set to landscape                    return mLandscapeRotation;                case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:                    //always return portrait if orientation set to portrait                    return mPortraitRotation;            }            // case for nosensor meaning ignore sensor and consider only lid            // or orientation sensor disabled            //or case.unspecified            if (mLidOpen) {                return mLidOpenRotation;            }             // ...省略部分代碼        }     } 

這裡,因為 mLidOpen一直保持在true狀態,因此旋轉螢幕角度一直保持在mLidOpenRotation。任憑你怎麼轉,螢幕就是不旋轉。

mLidOpenRotation在frameworks/base/core/res/res/values/config.xml中讀取。

因此可以感歎android的邏輯是多麼嚴謹,如果你滑蓋一直開啟,說明就是有按鍵動作,這時候在轉螢幕,是不合理的。

2、打電話時,如果臉貼近螢幕,接近感應器會將螢幕背光燈熄滅(省電,且防止誤操作),這本身也是個很人性化的操作,沒想到也會影響。在InCallScreen中

    /* package */ void updateProximitySensorMode(Phone.State state) {        if (proximitySensorModeEnabled()) {            synchronized (mProximityWakeLock) {                // turn proximity sensor off and turn screen on immediately if                // we are using a headset, the keyboard is open, or the device                // is being held in a horizontal position.                boolean screenOnImmediately = (isHeadsetPlugged()                            || PhoneUtils.isSpeakerOn(this)                            || ((mBtHandsfree != null) && mBtHandsfree.isAudioOn())                            || mIsHardKeyboardOpen                            || mOrientation != AccelerometerListener.ORIENTATION_VERTICAL);                if (((state == Phone.State.OFFHOOK) || mBeginningCall) && !screenOnImmediately) {                    // Phone is in use!  Arrange for the screen to turn off                    // automatically when the sensor detects a close object.                    if (!mProximityWakeLock.isHeld()) {                        if (DBG) Log.d(LOG_TAG, "updateProximitySensorMode: acquiring...");                        mProximityWakeLock.acquire();                    } else {                        if (VDBG) Log.d(LOG_TAG, "updateProximitySensorMode: lock already held.");                    }                }                        }        }    }

這裡的mIsHardKeyboardOpen會一直為true,因此就不會去acquire這個wake lock,因此靠近螢幕就不會熄背光燈。值得注意的是還依賴mOrientation,它是豎直方向才aquire,因為只有舉起電話之時,才代表接起了電話。

mIsHardKeyboardOpen在onConfigurationChanged得到賦值

    @Override    public void onConfigurationChanged(Configuration newConfig) {        if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {            mIsHardKeyboardOpen = true;        } else {            mIsHardKeyboardOpen = false;        }        // Update the Proximity sensor based on keyboard state        updateProximitySensorMode();        super.onConfigurationChanged(newConfig);    }

因此,應用程式層根據Configuration中的hardKeyboardHidden來判斷鍵盤是否可見。

這個值是在PhoneWindowManager中決定的,

    /** {@inheritDoc} */    public void adjustConfigurationLw(Configuration config) {        readLidState();        final boolean lidOpen = !KEYBOARD_ALWAYS_HIDDEN && mLidOpen;        mPowerManager.setKeyboardVisibility(lidOpen);        config.hardKeyboardHidden = determineHiddenState(lidOpen,                mLidKeyboardAccessibility, Configuration.HARDKEYBOARDHIDDEN_YES,                Configuration.HARDKEYBOARDHIDDEN_NO);        config.navigationHidden = determineHiddenState(lidOpen,                mLidNavigationAccessibility, Configuration.NAVIGATIONHIDDEN_YES,                Configuration.NAVIGATIONHIDDEN_NO);        config.keyboardHidden = (config.hardKeyboardHidden                        == Configuration.HARDKEYBOARDHIDDEN_NO || mHasSoftInput)                ? Configuration.KEYBOARDHIDDEN_NO                : Configuration.KEYBOARDHIDDEN_YES;    }

因此,PowerManagerService可能跟Configuration針對鍵盤狀態可能不同,取決於determineHiddenState的傳回值,determineHiddenState綜合了根據滑蓋狀態,和mLidKeyboardAccessibility,來決定hardKeyboardHidden的狀態,mLidKeyboardAccessibility從config.xml中讀取

    <!-- Indicate whether the lid state impacts the accessibility of         the physical keyboard.  0 means it doesn't, 1 means it is accessible         when the lid is open, 2 means it is accessible when the lid is         closed.  The default is 1. -->    <integer name="config_lidKeyboardAccessibility">1</integer>

再看determineHiddenState,意思滑蓋關閉的時候,如果mLidKeyboardAccessibility為1,代表鍵盤同時不可見。

    private int determineHiddenState(boolean lidOpen,            int mode, int hiddenValue, int visibleValue) {        switch (mode) {            case 1:                return lidOpen ? visibleValue : hiddenValue;            case 2:                return lidOpen ? hiddenValue : visibleValue;        }        return visibleValue;    }

這些寫出來很容易,其實定位問題花了我好長時間,因此吸取一個教訓,framework裡面的千萬不能隨意改,不然會出大亂子。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.