概述
本文我们在QS下拉快捷开关添加一个"高铁模式"的按钮(实际功能只是模拟开启振动模式),点击后控制状态栏右上角ICON图标去显示一个"高铁icon"(类似飞行模型的飞机icon)。
首先添加状态栏"高铁模式"的icon
SystemUI的config.xml文件里面添加item项,见
注释为添加代码
<string-array name="config_statusBarIcons">
<item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_train</xliff:g></item>
<!--jiaxian-->
<item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item>
<string translatable="false" name="status_bar_train">train</string>
<!--jiaxian-->
然后回到PhoneStatusBarPolicy.java中,控制其显示
public class PhoneStatusBarPolicy
implements BluetoothController.Callback,
CommandQueue.Callbacks,
RotationLockControllerCallback,
Listener,
ZenModeController.Callback,
DeviceProvisionedListener,
KeyguardStateController.Callback,
LocationController.LocationChangeCallback,
RecordingController.RecordingStateChangeCallback,
TrainController.Callback{
...
private final TrainController mTrainController;
...
private final String mSlotTrain;//jiaxian
...
@Inject
public PhoneStatusBarPolicy(StatusBarIconController iconController,
...
CommandQueue commandQueue, TrainController trainController){
...
mTrainController = trainController;
...
mSlotTrain = resources.getString(com.android.internal.R.string.status_bar_train);//jiaxian
}
...
/** Initialize the object after construction. */
public void init() {
// listen for broadcasts
mIconController.setIcon(mSlotTrain, R.drawable.stat_sys_train, null);
//先设置为false,不显示,后期是否为true的显示由QS设置后,通过回调函数来改变
mIconController.setIconVisibility(mSlotTrain, false);
//把PhoneStatusBarPolicy加入回调函数中
mTrainController.addCallback(this);
}
//实现TrainController.Callback接口后,重写回调方法onTrainStateChange ,
//当在trainControllerImpl执行notifyAllChanged的时候,该方法就会被执行回调调用。
//回调传过来的enabled参数即是控制是否显示的参数。
@Override
public void onTrainStateChange(boolean enabled) {
Log.d("PhoneStatusBarPolicy","onTrainStateChange:"+enabled);
updateTrain();
}
//jiaxian train
private final void updateTrain() {
boolean trainVisible = false;
//这里通过调用isTrainEnabled()来获取状态,其实也可以将onTrainStateChange的形参enabled
//传过来,然后再传入setIconVisibility中
trainVisible = mTrainController.isTrainEnabled();
Log.d("PhoneStatusBarPolicy","updateTrain:"+trainVisible);
mIconController.setIcon(mSlotTrain, R.drawable.stat_sys_train, null);
mIconController.setIconVisibility(mSlotTrain, trainVisible);
}
}
添加QS快捷开关的流程参考之前写的文章:https://blog.csdn.net/z1804362542/article/details/126126003?spm=1001.2014.3001.5502
这里我们集中讲解需要我们自己创建的TrainTile.java类、TrainController类、TrainControllerImpl类所做的功能
重要的代码在于
1、trainController.observe(this, mCallback); 这个做的就是把当前的TrainTile类注册进回调里面,因为observe走下去其实就后会执行到addCallback(this),这个就是和上面PhoneStatusBarPolicy.java出现的一样了,PhoneStatusBarPolicy也是把自己addCallback(this)进去。
2、final boolean train = trainController.isTrainEnabled();通过trainController的方法来控制目前是显示还是隐藏的状态,这个方法TrainTile用到,PhoneStatusBarPolicy也会用到,这样大家才能一起同步目前的状态。而如何同步的关键在于,回调函数发送作用了。
3、
private final TrainController.Callback mCallback = new TrainController.Callback() {
@Override
public void onTrainStateChange(boolean enabled) {
refreshState(enabled);
}
};
这个是写在TrainTile方的一个回调方法,当在TrainControllerImpl的notifyAllChanged()通知触发的时候,这个回到就会被执行,然后回调修改状态,在PhoneStatusBarPolicy中就是我们的重写的onTrainStateChange方法,区别是PhoneStatusBarPolicy是通过实现implments TrainController.addCallback来实现的,而我这里是直接new TrainController.Callback然后匿名内部类实现的
/*
* Copyright (c) 2016, The Android Open Source Project
* Contributed by the Paranoid Android Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ape.systemui.qs.tiles;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import android.provider.Settings;
import android.content.Context;
import android.util.Log;
import android.os.UserManager;
import android.os.SystemClock;
import android.content.Intent;
import android.content.res.Resources;
import android.service.quicksettings.Tile;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import android.widget.Toast;
import android.content.Intent;
import android.media.AudioManager;
import com.android.systemui.statusbar.policy.TrainController.Callback;
import static android.media.AudioManager.RINGER_MODE_NORMAL;
import static android.media.AudioManager.RINGER_MODE_SILENT;
import static android.media.AudioManager.RINGER_MODE_VIBRATE;
import com.android.systemui.statusbar.policy.TrainController;
import com.android.systemui.R;
import javax.inject.Inject;
/** Quick settings tile: jiaxian.zhang add mute **/
public class TrainTile extends QSTileImpl<BooleanState> {
//这个可以Intent到Settings中的音量设置页面,是Settings专门提供给我们的
private static final Intent SOUND_SETTINGS = new Intent(Settings.ACTION_SOUND_SETTINGS);
private final TrainController trainController;
public static final int MSG_RINGER_MODE_SILENT = 0;
//静音模式
public static final int MSG_RINGER_MODE_NORMAL = 2;
//一般模式
//注解不要少加
@Inject
public TrainTile(QSHost host,TrainController trainController) {
super(host);
this.trainController = trainController;
//在这里把TrainTile注册进去,自己成为一名观察者
trainController.observe(this, mCallback);
}
@Override
public BooleanState newTileState() {
return new BooleanState();
}
@Override
protected void handleLongClick() {
}
@Override
public Intent getLongClickIntent() {
Log.d("Mute","getLongClickIntent");
return new Intent(Settings.ACTION_SOUND_SETTINGS);
}
@Override
public boolean isAvailable() {
return mContext.getString(R.string.quick_settings_tiles_stock).contains("train");
}
@Override
protected void handleClick() {
//当是打开时,mState.value为true。当是关闭是,mState.value为false
//假如mState.value是false,获取后再取反,则最后setTrainEnabled();传进去的就是false
final boolean newState = !mState.value;
Log.d("Train","handleClick"+" mState.value:"+mState.value+" newState:"+newState);
trainController.setTrainEnabled(!newState);
refreshState(newState);//其实在这里refreshState也可以不写,参数newState也不重要,
//因为我们的回调onTrainStateChange最终也会去执行refreshState()
//然后最终还是会触发到我们的handleUpdateState进行QS图标和文件的刷新
}
@Override
public CharSequence getTileLabel() {
return mContext.getString(R.string.quick_settings_mute_label);
}
/*这个方法的执行有2情况
1、下划QS面板的时候触发所有可选的Tile类的handleUpdateState()执行
2、执行refreshState()的时候,触发所在类的handleUpdateState()执行
*/
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
final boolean train = trainController.isTrainEnabled();
//trainController.isTrainEnabled();获得的就是传给setTrainEnabled的true或者false
//所以我们这里要对它取反,以修改QS图标状态显示
Log.d("handleUpdateStatestate","isTrainEnabled()"+train);
state.value = !train;
Log.d("handleUpdateStatestate","state.value"+state.value);
state.icon = ResourceIcon.get(R.drawable.ic_train_normal);
state.label = mContext.getString(R.string.quick_settings_mute_label);
state.contentDescription = getTrainString();
state.state = state.value ? Tile.STATE_INACTIVE : Tile.STATE_ACTIVE;
}
private String getTrainString() {
return mContext.getString(R.string.quick_settings_mute_label);
}
@Override
public int getMetricsCategory() {
return MetricsEvent.QS_TRAIN;
}
@Override
public void handleSetListening(boolean listening) {
}
private final TrainController.Callback mCallback = new TrainController.Callback() {
@Override
public void onTrainStateChange(boolean enabled) {
//监听到其他类修改了后,作为观察者的"我"回调观察到了,回调执行的更新状态,
//是谁执行了然后回调到我这里呢?执行者是在TrainControllerImpl类的notifyChanged()方法中。
//我只要被促发就执行refreshState(),refreshState会触发TrainTile类的handleUpdateState()
//方法来改变QS的图标和文本,所以我们的 refreshState(enableld) 和 refreshState()是否传参数进去区别不大,
//因为在handleUpdateState()是靠trainController来获取true、false控制区别状态的
refreshState(enableld);
}
};
}
---------------------------------------------------------------------
补充,我们也可以让TrainTile和PhoneStatusBarPolicy的做法一样,实现类,重写方法,不用匿名内部类的实现方式
public class TrainTile extends QSTileImpl<BooleanState>
implements TrainController.Callback {
@Inject
public TrainTile(QSHost host,TrainController trainController) {
super(host);
this.trainController = trainController;
//
trainController.observe(this, mCallback);改为:
trainController.addCallback(this);
}
//
private final TrainController.Callback mCallback = new TrainController.Callback() {
//
@Override
//
public void onTrainStateChange(boolean enabled) {
//
refreshState(enabled);//监听到其他类修改了后,作为观察者的"我"回调观察到了,回调执行的更新状态,
//
//是谁执行了然后回调到我这里呢?执行者是在RotationLockControllerImpl类的notifyChanged()方法中。
//
}
//
}; 改为:
@Override
public void onTrainStateChange(boolean enabled) {
//监听到其他类修改了后,作为观察者的"我"回调观察到了,回调执行的更新状态,
//是谁执行了然后回调到我这里呢?执行者是在RotationLockControllerImpl类的notifyChanged()方法中。
refreshState(enabled);
}
}
现在看来TrainControllerImpl很重要,需要由它来发送通知同步状态、修改状态、获取状态。但在之前,我们需要先定义它的接口规范。TrainController继承父类CallbackController,所以TrainController是间接拥有addCallback等方法的。
package com.android.systemui.statusbar.policy;
import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.policy.TrainController.Callback;
import java.util.Collection;
import java.util.List;
public interface TrainController extends CallbackController<Callback>{
boolean isTrainEnabled();
void setTrainEnabled(boolean enabled);
public interface Callback {
void onTrainStateChange(boolean enabled);
}
}
下面这个类系统提供,不用我们创建,我们只是让TrainController 实现它即可:
public interface CallbackController<T> {
void addCallback(T listener);
void removeCallback(T listener);
/**
* Wrapper to {@link #addCallback(Object)} when a lifecycle is in the resumed state
* and {@link #removeCallback(Object)} when not resumed automatically.
*/
default T observe(LifecycleOwner owner, T listener) {
return observe(owner.getLifecycle(), listener);
}
/**
* Wrapper to {@link #addCallback(Object)} when a lifecycle is in the resumed state
* and {@link #removeCallback(Object)} when not resumed automatically.
*/
default T observe(Lifecycle lifecycle, T listener) {
lifecycle.addObserver((LifecycleEventObserver) (lifecycleOwner, event) -> {
if (event == Event.ON_RESUME) {
addCallback(listener);
} else if (event == Event.ON_PAUSE) {
removeCallback(listener);
}
});
return listener;
}
}
创建实现类
package com.android.systemui.statusbar.policy;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.WeakHashMap;
import android.media.AudioManager;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
*/
@Singleton
public class TrainControllerImpl implements TrainController{
private static final String TAG = "TrainController";
//这个Callback即是TrainController.Callback
//private ArrayList<Callback> mTrainChangeCallbacks = new ArrayList<>();
private Context mContext;
private boolean mEnabled;
private boolean mIsActive;
private AudioManager mAudioManager;
private final H mHandler;
private int mState;
/**
*/
@Inject
public TrainControllerImpl(Context context,@Main Looper mainLooper) {
mContext = context;
mHandler = new H(mainLooper);
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
}
@Override
public boolean isTrainEnabled() {
Log.d("TrainController","mEnabled:"+mEnabled);
return mEnabled;
}
@Override
public void setTrainEnabled(boolean enabled) {
Log.d("TrainController","enabled:"+enabled);
//如果开关被点击是true,表激活,就开启振动模式
//开启高铁模式我们做的功能是开启震动模式,关闭就是恢复正常
if(enabling){
mAudioManager.setRingerMode(MSG_RINGER_MODE_VIBRATE);
}else{
mAudioManager.setRingerMode(MSG_RINGER_MODE_NORMAL);
}
mEnabled = enabled;
/*通知所有观察者改状态 直接调用notifyAllChanged()这种实现形式会有一个报错:
android.view.ViewRootImpl$CalledFromWrongThreadException:
Only the original thread that created a view hierarchy can touch its views.
也就是我们现在不是在创建线程的原始线程,所以不准,解决方式是模仿一下
LocationControllerImpl的构造方法中的参数@Main Looper mainLooper
*/
//notifyAllChanged();
//新的可实现方式,用mHandler发送信息给原始线程H来解决。 实现成功
mHandler.obtainMessage(H.CHANGE_ICON,isTrainEnabled()).sendToTarget();
}
//
public void notifyAllChanged(){
//
for (Callback callback : mTrainChangeCallbacks) {
//
callback.onTrainStateChange(isTrainEnabled());
//
}
//
}
@Override
public void addCallback(Callback cb) {
mHandler.obtainMessage(H.MSG_ADD_CALLBACK, cb).sendToTarget();
}
@Override
public void removeCallback(Callback cb) {
mHandler.obtainMessage(H.MSG_REMOVE_CALLBACK, cb).sendToTarget();
}
//构造创建视图的原始线程来执行notifyAllChanged操作
private final class H extends Handler {
private static final int MSG_ADD_CALLBACK = 3;
private static final int MSG_REMOVE_CALLBACK = 4;
private static final int CHANGE_ICON = 5;
private ArrayList<TrainController.Callback> mTrainChangeCallbacks = new ArrayList<>();
H(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_ADD_CALLBACK:
mTrainChangeCallbacks.add((TrainController.Callback) msg.obj);
break;
case MSG_REMOVE_CALLBACK:
mTrainChangeCallbacks.remove((TrainController.Callback) msg.obj);
break;
case CHANGE_ICON:
//取出 开关状态参数 传入notifyAllChanged
boolean isTrainEnabled = (boolean)msg.obj;
notifyAllChanged(isTrainEnabled);
break;
}
}
private void notifyAllChanged(boolean isTrainEnabled) {
for (TrainController.Callback callback : mTrainChangeCallbacks) {
callback.onTrainStateChange(isTrainEnabled);
}
}
}
}
最后
以上就是有魅力香菇为你收集整理的QS快捷开关控制状态栏ICON显示的全部内容,希望文章能够帮你解决QS快捷开关控制状态栏ICON显示所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复