Android灯光系统(3)——背光灯控制实现

Android中所有系统的定义:lights.h
--------------------------
#define LIGHT_ID_BACKLIGHT          "backlight"


JNI: com_android_server_lights_LightsService.cpp
---------------------------
setLight_native:调用HAL去控制背光


Service:LightsService.java
-------------------------------
1.它是各种灯光和背光的Service,提供了对背光灯操作的所有方法
2.setLightLocked():是实际调用JNI操作背光灯的函数,所有向应用程序公开的LCD操作接口都使用synchronized (this){... setLightLocked() ...}
确保互斥访问硬件。
3.onStart()中:publishLocalService(LightsManager.class, mService);

DisplayPowerController.java
----------------------------------
1.获取LightsService中注册的LightsManager:mLights = LocalServices.getService(LightsManager.class);
2.然后在构造函数中只获取了背光灯:new DisplayPowerState(mBlanker, mLights.getLight(LightsManager.LIGHT_ID_BACKLIGHT), new ColorFade(Display.DEFAULT_DISPLAY)); 整个系统中也只有这里获取了背光灯。

DisplayPowerState.java
-----------------------------------
DisplayPowerState(DisplayBlanker blanker, Light backlight, ColorFade electronBeam)
    --> mPhotonicModulator = new PhotonicModulator();
    --> mPhotonicModulator.start();
        --> run() //无限循环执行这个线程
            --> mLock.wait(); //睡眠
            --> setBrightness(backlight);

在setState()中notifyAll(); //唤醒

在mScreenUpdateRunnable中调用了setState()
private final Runnable mScreenUpdateRunnable = new Runnable() {
    mPhotonicModulator.setState(mScreenState, brightness);
}

mScreenUpdateRunnable作为一个处理消息的响应线程
postScreenUpdateThreadSafe中mHandler.post(mScreenUpdateRunnable);

scheduleScreenUpdate()
    --> postScreenUpdateThreadSafe();

由上,也就是调用scheduleScreenUpdate()来触发向消息队列中存放一个消息,消息处理函数中notifyAll()然后在上面无限循环的
run()中设置背光亮度。

DisplayPowerState
    --> scheduleScreenUpdate
setScreenState
    --> scheduleScreenUpdate
setScreenBrightness
    --> scheduleScreenUpdate
setColorFadeLevel
    --> scheduleScreenUpdate


DisplayPowerController.java
-------------------------
    --> new DisplayPowerState()
    --> animateScreenStateChange //在updatePowerState中被调用

处理消息:
Handler的handleMessage中调用updatePowerState
updatePowerState()
    animateScreenStateChange //这里面设置背光亮度

发送消息:
requestPowerState
    sendUpdatePowerStateLocked //基本上所有操作都是通过它执行的,也就是这个函数对上层做基础支持。
        mHandler.sendMessage(msg); //它通过发送消息在Handler的handleMessage中处理

updateDisplayPowerStateLocked
    requestPowerState

什么时候发消息:
PowerManagerService.java
----------------------------
updatePowerStateLocked
    updateDisplayPowerStateLocked

systemReady方法中:
(1)注册了4个Receiver
eg:
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
mContext.registerReceiver(new BatteryReceiver(), filter, null, mHandler);
当数据库中的Intent.ACTION_BATTERY_CHANGED变化的时候,BatteryReceiver的onReceive()就会被调用。

(2)对于背光灯还注册了1个ContentObserver
resolver.registerContentObserver(Settings.Secure.getUriFor(
                    Settings.Secure.SCREENSAVER_ENABLED),
                    false, mSettingsObserver, UserHandle.USER_ALL);
当数据库中Settings.Secure.SCREENSAVER_ENABLED变化的时候,mSettingsObserver中的onChange()方法就会被调用。

Receiver                监听的事件
BatteryReceiver            ACTION_BATTERY_CHANGED
DreamReceiver            ACTION_DREAMING_STARTED
UserSwitchedReceiver    ACTION_USER_SWITCHED
DockReceiver            ACTION_DOCK_EVENT


mSettingsObserver
SCREENSAVER_ENABLED                    屏保功能开启
SCREENSAVER_ACTIVATE_ON_SLEEP        在睡眠时屏保启动
SCREENSAVER_ACTIVATE_ON_DOCK        连接底座并且屏保启动
SCREEN_OFF_TIMEOUT                    进入dream状态前未活动时间
SLEEP_TIMEOUT                        进入sleep状态前未活动时间
STAY_ON_WHILE_PLUGGED_IN            有插入并且屏幕开启
SCREEN_BRIGHTNESS                    屏幕亮度
SCREEN_BRIGHTNESS_MODE                屏幕亮度模式
SCREEN_AUTO_BRIGHTNESS_ADJ
LOW_POWER_MODE
LOW_POWER_MODE_TRIGGER_LEVEL

2.使用到内容观察者模式

使用方法:
1.创建一个 ContentObserver 的子类,实现 onChange() 方法。
private final class SettingsObserver extends ContentObserver {
    public SettingsObserver(Handler handler) {
        super(handler);
    }

    /*目的是复写这个onChange方法*/
    @Override
    public void onChange(boolean selfChange, Uri uri) {
        synchronized (mLock) {
            /*它里面也是去调用updatePowerStateLocked*/
            handleSettingsChangedLocked();
        }
    }
}

2.注册 ContentObserver
/*
arg1:需要监听的 uri。
arg2:为 false 表示精确匹配,即只匹配该 Uri。为 true 表示可以同时匹配其派生的 Uri,如:
    content://com.qin.cb/student(精确匹配)
    content://com.qin.cb/student/# (派生,false 才能匹配到)
    content://com.qin.cb/student/schoolchild(派生,false 才能匹配到)
arg3:ContentObserver 的实例。
*/
registerContentObserver(Settings.Secure.getUriFor(Settings.Secure.SCREENSAVER_ENABLED),
    false, mSettingsObserver, UserHandle.USER_ALL);

3.用完后记得取消注册 ContentObserver
unregisterContentObserver(myContentObserver)

参考:
ContentObserver:https://www.jianshu.com/p/3bc164010b5f

3.App测试Demo MainActivity.java

package com.example.mm.lcd_brightness;

import android.os.Bundle;
import android.provider.Settings;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.SeekBar;

public class MainActivity extends AppCompatActivity {
    final private int LED_NOTIFICATION_ID = 123;
    private SeekBar mBacklightSeekBar = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        mBacklightSeekBar = (SeekBar)findViewById(R.id.seekBar);

        try {
            /*将背光调节模式改为手动,无为自动的话手动调节无效*/
            Settings.System.putInt(getContentResolver(),
                    Settings.System.SCREEN_BRIGHTNESS_MODE,
                    Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);

            /*让滚动条的初始状态显示在正确的位置*/
            int brightness = android.provider.Settings.System.getInt(getContentResolver(),
                    android.provider.Settings.System.SCREEN_BRIGHTNESS);
            mBacklightSeekBar.setProgress(brightness*100/255);
        } catch (Settings.SettingNotFoundException e) {
            e.printStackTrace();
        }

        /*设置滚动条的监听函数*/
        mBacklightSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener (){
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                int brightness = mBacklightSeekBar.getProgress();
                /*应用程序传入的是0--100,而底层是0--255*/
                brightness = brightness * 255 / 100;

                /*设置到数据库中,会导致内容观察者对应的设置屏幕亮度的方法被调用*/
                android.provider.Settings.System.putInt(getContentResolver(),
                        android.provider.Settings.System.SCREEN_BRIGHTNESS, brightness);

            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                Log.d("App_Brightness: ","==============onStartTrackingTouch===============");
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                Log.d("App_Brightness: ","==============onStartTrackingTouch===============");
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
View Code

4.遇到问题

(1) 应用程序执行后闪退,报没有权限“android.permission.WRITE_SETTINGS”,然后在AndroidManifest.xml中添加这个权限:

<manifest
    ...
    <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
</manifest>

5.Android自带的背光调节程序BrightnessDialog.java分析

Settings --> Display --> Sleep --> Brightness Level 设置背光,对应的系统自带的App是BrightnessDialog.java

/*是第一次创建这个对话框的时候被调用*/
protected void onCreate(Bundle savedInstanceState) {
    ...
    /*根据ID获得一个控件,只不过这里是ToggleSlider,我们的App里面是Slide bar*/
    final ToggleSlider slider = (ToggleSlider) findViewById(R.id.brightness_slider);
    /*
     * 与我们的App不同,它这个slide没有处理函数,而是作为一个参数传入了BrightnessController
    */
    mBrightnessController = new BrightnessController(this, icon, slider);
}

@Override
/*是每一次使用对话框时调用*/
protected void onStart() {
    super.onStart();
    /*这里面设置了监听*/
    mBrightnessController.registerCallbacks();
}

public void registerCallbacks() {
    if (mListening) {
        return;
    }

    /*注意: startObserving setOnChangedListener,它两个构成设置滑动块的处理方法*/
    /*和之前的非常像,也是去注册ContentObserver,内容观察者,
    若是滑动滑动块了最终就会导致这里面的onChanged()方法被调用
    */
    mBrightnessObserver.startObserving();
    mUserTracker.startTracking();

    // Update the slider and mode before attaching the listener so we don't
    // receive the onChanged notifications for the initial values.
    updateMode();
    updateSlider();

    /*OnChanged 的监听,this里面肯定有一个onChanged方法*/
    mControl.setOnChangedListener(this);
    mListening = true;
}

/*BrightnessController.java*/
@Override
public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value) {
    updateIcon(mAutomatic);
    if (mExternalChange) return;

    if (!mAutomatic) {
        final int val = value + mMinimumBacklight;
        /*这里有设置背光*/
        setBrightness(val);
        /*如果当前不是一直按着滑动块而是松手了的话就把那些数据写到数据库里面去。
        注意在滑动过程中是直接调用setBrightness(),而不是通过数据库来实现的。但是松手后
        才写入数据库的。
        */
        if (!tracking) {
            AsyncTask.execute(new Runnable() {
                    public void run() {
                        /*这里会写入数据库,最终会导致内容观察者的函数被调用
                        本文件中的BrightnessObserver中的onChange被调用
                        */
                        Settings.System.putIntForUser(mContext.getContentResolver(),
                                Settings.System.SCREEN_BRIGHTNESS, val,
                                UserHandle.USER_CURRENT);
                    }
                });
        }
    } else {
        final float adj = value / (BRIGHTNESS_ADJ_RESOLUTION / 2f) - 1;
        setBrightnessAdj(adj);
        if (!tracking) {
            AsyncTask.execute(new Runnable() {
                public void run() {
                    Settings.System.putFloatForUser(mContext.getContentResolver(),
                            Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, adj,
                            UserHandle.USER_CURRENT);
                }
            });
        }
    }

    for (BrightnessStateChangeCallback cb : mChangeCallbacks) {
        cb.onBrightnessLevelChanged();
    }
}
原文地址:https://www.cnblogs.com/hellokitty2/p/10812244.html