Android L SystemUI 流程简要分析
来源:互联网 发布:伦敦高级应召女郎 知乎 编辑:程序博客网 时间:2024/05/23 19:18
Android L SystemUI 流程简要分析
1.SystemUI 启动流程
1.1概述
SystemUI通常包含状态栏,下拉栏。
状态栏主要包含一些蓝牙,wifi,信号强度,Sim卡等模块的信息状态.
状态栏是用来实时显示系统状态信息,是需要时刻运行在系统中,在android系统中只有service 才能长期运行在系统中.其实状态栏本身就是一个长期运行的服务.在android系统启动之后,完成ActivityManagerService这些服务的启动之后,系统会提供SystemServer来启动SystemUIService,从而来启动状态栏服务,这样状态栏就开启长期运行在系统中了。
想要知道SystemUI 相关的源码的路径执行以下命令:
find android_top_dir -name "*.mk" |xargs -i grep -rwnH "SystemUI" {} // 查找systemui所在目录,android迭代后,很多功能位置发生变化.
状态栏主要的效果图如下:
and
1.2启动流程分析
SystemUI 包含的功能很多,有状态栏,导航栏,近期任务,PowerUI,音量调节进度条,截图,壁纸等等都在SystemUI里面.其中有些功能是按需启动,比如:近期任务和截图.但是想状态栏和导致栏就是一直运行的,他们是通过SystemUIService来启动运行的.
在负责启动系统各个服务的SystemServer.java中,它的大致流程是SystemServer.java -> main() - > run()-> startOtherServices().至于为什么是从main方法开始,因为java 语法是这样的..
在startOtherServices() 中通过调用ActivityManagerService.systemReady()方法通知AMS系统已经就绪。这个systemReady()拥有一个名为goingCallback的Runnable实例作为参数。顾名思义,当AMS完成对systemReady()的处理后将会回调这一Runnable的run()方法.
mActivityManagerService.systemReady(new Runnable() {
@Override
public void run() {
Slog.i(TAG, "Making services ready");
......
try {
startSystemUi(context);
} catch (Throwable e) {
reportWtf("starting System UI", e);
}
....
}
}
具体时序图如下:
static final void startSystemUi(Context context) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.systemui",
"com.android.systemui.SystemUIService"));
//Slog.d(TAG, "Starting service: " + intent);
context.startServiceAsUser(intent, UserHandle.OWNER);
}
startServiceAsUser直接转调startServiceCommon
private ComponentName startServiceCommon(Intent service, UserHandle user) {
try {
validateServiceIntent(service);
service.prepareToLeaveProcess();
ComponentName cn = ActivityManagerNative.getDefault().startService(
mMainThread.getApplicationThread(), service,
service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier());
if (cn != null) {
if (cn.getPackageName().equals("!")) {
throw new SecurityException( "Not allowed to start service " + service+ " without permission " + cn.getClassName());
} else if (cn.getPackageName().equals("!!")) {
throw new SecurityException( "Unable to start service " + service + ": " + cn.getClassName());
}
}
return cn;
} catch (RemoteException e) {
return null;
}
}
启动SystemUIService服务之后,SystemUIService.onCreate会被调用
public void onCreate() {
super.onCreate();
((SystemUIApplication) getApplication()).startServicesIfNeeded();
}
public void startServicesIfNeeded() {
if (mServicesStarted) {
return;
}
if (!mBootCompleted) {
// check to see if maybe it was already completed long before we began
// see ActivityManagerService.finishBooting()
if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
mBootCompleted = true;
if (DEBUG) Log.v(TAG, "BOOT_COMPLETED was already sent");
}
}
Log.v(TAG, "Starting SystemUI services.");
final int N = SERVICES.length;
for (int i=0; i<N; i++) {
Class<?> cl = SERVICES[i];
if (DEBUG) Log.d(TAG, "loading: " + cl);
try {
mServices[i] = (SystemUI)cl.newInstance();
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InstantiationException ex) {
throw new RuntimeException(ex);
}
mServices[i].mContext = this;
//这里设置上下文对象,最后会在PhoneStatusBar里面makeStatusBarView
//方法用来用来加载super_status_bar.xml
mServices[i].mComponents = mComponents;
if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
mServices[i].start();
if (mBootCompleted) {
mServices[i].onBootCompleted();
}
}
mServicesStarted = true;
}
1.3 SystemUI启动的主要的服务
下面看一下所有的子服务,他们并不是Android 里面经常提到真正的服务,只是继承了SystemUI这个抽象类的服务
private final Class<?>[] SERVICES = new Class[] {
com.android.systemui.keyguard.KeyguardViewMediator.class,
com.android.systemui.recent.Recents.class,
com.android.systemui.volume.VolumeUI.class,
com.android.systemui.statusbar.SystemBars.class,
com.android.systemui.usb.StorageNotification.class,
com.android.systemui.power.PowerUI.class,
com.android.systemui.media.RingtonePlayer.class
};
KeyguardViewMediator为锁屏模块, 包含锁屏机制;
Recents 为近期任务列表;
VolumeUI为全局音量控制UI;
SystemBars为系统栏;
StorageNotification 为存储信息通知栏;
PowerUI 为电源界面;
RingtonePlayer 为铃声播放;
其中的SystemBars 就是启动状态栏的一个核心服务.具体流程如下:
SystemUIApplication.startServicesIfNeeded()会将调用SystemBars.start()
@Override
public void start() {
if (DEBUG) Log.d(TAG, "start");
//ServiceMonitor是个监听器,这里就是监听这个BAR_SERVICE_COMPONENT是否改变
mServiceMonitor = new ServiceMonitor(TAG, DEBUG,
mContext, Settings.Secure.BAR_SERVICE_COMPONENT, this);
//这里看到注释,如果Service不存在 ,就调用onNoService->createStatusBarFromConfig();
mServiceMonitor.start(); // will call onNoService if no remote service is found
}
mServiceMonitor.start(); 将会通过handler 调用startService() 方法. 由于SystemBars实现了ServiceMonitor.Callbacks 接口, startService()就可以回调onNoService()方法,接着转掉createStatusBarFromConfig(),其中config_statusBarComponent 字符串就是com.android.systemui.statusbar.phone.PhoneStatusBar,所以最终会调用PhoneStatusBar.start(). PhoneStatusBar是SystemUI的核心类.
public ServiceMonitor(String ownerTag, boolean debug,
Context context, String settingKey, Callbacks callbacks) {
mTag = ownerTag + ".ServiceMonitor";
mDebug = debug;
mContext = context;
mSettingKey = settingKey;
mCallbacks = callbacks;
}
public void start() {
// listen for setting changes
ContentResolver cr = mContext.getContentResolver();
cr.registerContentObserver(Settings.Secure.getUriFor(mSettingKey),
false /*notifyForDescendents*/, mSettingObserver, UserHandle.USER_ALL);
// listen for package/component changes
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addDataScheme("package");
mContext.registerReceiver(mBroadcastReceiver, filter);
mHandler.sendEmptyMessage(MSG_START_SERVICE);
}
private final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch(msg.what) {
case MSG_START_SERVICE:
startService();
break;
case MSG_CONTINUE_START_SERVICE:
continueStartService();
break;
case MSG_STOP_SERVICE:
stopService();
break;
case MSG_PACKAGE_INTENT:
packageIntent((Intent)msg.obj);
break;
case MSG_CHECK_BOUND:
checkBound();
break;
case MSG_SERVICE_DISCONNECTED:
serviceDisconnected((ComponentName)msg.obj);
break;
}
}
};
private void startService() {
//获取Service的ComponentName
mServiceName = getComponentNameFromSetting();
if (mDebug) Log.d(mTag, "startService mServiceName=" + mServiceName);
if (mServiceName == null) {
mBound = false;
//如果没有Service,就回调,从之前的看,就是start PhoneStatusBar
mCallbacks.onNoService();
} else {
//销毁已经存在的status bar
long delay = mCallbacks.onServiceStartAttempt();
//喜闻乐见的bindService,说破天就是在启动PhoneStatusBar
mHandler.sendEmptyMessageDelayed(MSG_CONTINUE_START_SERVICE, delay);
}
}
private ComponentName getComponentNameFromSetting() {
String cn = Settings.Secure.getStringForUser(mContext.getContentResolver(),
mSettingKey, UserHandle.USER_CURRENT);
return cn == null ? null : ComponentName.unflattenFromString(cn);
}
SystemBars.java
private void createStatusBarFromConfig() {
if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
final String clsName = mContext.getString(R.string.config_statusBarComponent);
if (clsName == null || clsName.length() == 0) {
throw andLog("No status bar component configured", null);
}
Class<?> cls = null;
try {
cls = mContext.getClassLoader().loadClass(clsName);
} catch (Throwable t) {
throw andLog("Error loading status bar component: " + clsName, t);
}
try {
mStatusBar = (BaseStatusBar) cls.newInstance();
} catch (Throwable t) {
throw andLog("Error creating status bar component: " + clsName, t);
}
mStatusBar.mContext = mContext;
mStatusBar.mComponents = mComponents;
mStatusBar.start();
if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
}
其中startKeyguard() 是解锁页面相关的,
addNavigationBar()是添加虚拟导航菜单,
super.start()是调用父类BaseStatusBar的start() 方法.
BaseStatusBar的start()会调用createAndAddWindows(),但是该方法是抽象方法,具体的实现是在PhoneStatusBar里面.
public void createAndAddWindows() {
addStatusBarWindow();
}
private void addStatusBarWindow() {
makeStatusBarView();
mStatusBarWindowManager = new StatusBarWindowManager(mContext);
if(mStatusBarWindowManager!=null){
mStatusBarWindowManager.setPhoneStatusBar(this);
}
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
}
createAndAddWindows继续转调addStatusBarWindow (),addStatusBarWindow转调用makeStatusBarView()来进行构造系统状态栏的View和一些系统状态栏的监听的初始化.makeStatusBarView() 函数返回的是PhoneStatusBarView, PhoneStatusBarView主要是负责状态栏上面的一些Icon(wifi,sim,电池)和时间等,也就是说系统是在PhoneStatusBar.java里面来加载SystemUI的视图的.
1.4 SystemUI和StatusBarManagerService的交互
if (!disableSystemUI) {
try {
Slog.i(TAG, "Status Bar");
statusBar = new StatusBarManagerService(context, wm);
ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
} catch (Throwable e) {
reportWtf("starting StatusBarManagerService", e);
}
}
public class StatusBarManagerService extends IStatusBarService.Stub {
private StatusBarIconList mIcons = new StatusBarIconList();
.............
/**
* Construct the service, add the status bar view to the window manager
*/
public StatusBarManagerService(Context context, WindowManagerService windowManager) {
mContext = context;
mWindowManager = windowManager;
final Resources res = context.getResources();
mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.config_statusBarIcons));//这个
config_statusBarIcons数组里面就是状态栏上面的图标LocalServices.addService(StatusBarManagerInternal.class, mInternalService);
/// M: DM Lock Feature.
registerDMLock();//mtk 添加的功能,具体不知道
}
<!-- Do not translate. Defines the slots for the right-hand side icons. That is to say, the
icons in the status bar that are not notifications. -->
<string-array name="config_statusBarIcons">
<item><xliff:g id="id">ime</xliff:g></item>
<item><xliff:g id="id">sync_failing</xliff:g></item>
<item><xliff:g id="id">sync_active</xliff:g></item>
<item><xliff:g id="id">cast</xliff:g></item>
<item><xliff:g id="id">hotspot</xliff:g></item>
<item><xliff:g id="id">location</xliff:g></item>
<item><xliff:g id="id">bluetooth</xliff:g></item>
<item><xliff:g id="id">nfc</xliff:g></item>
<!-- M: Support "SystemUI Headset icon" feature. -->
<item><xliff:g id="id">headset</xliff:g></item>
<item><xliff:g id="id">tty</xliff:g></item>
<item><xliff:g id="id">speakerphone</xliff:g></item>
<item><xliff:g id="id">zen</xliff:g></item>
<item><xliff:g id="id">mute</xliff:g></item>
<item><xliff:g id="id">volume</xliff:g></item>
<item><xliff:g id="id">wifi</xliff:g></item>
<item><xliff:g id="id">cdma_eri</xliff:g></item>
<item><xliff:g id="id">data_connection</xliff:g></item>
<item><xliff:g id="id">phone_evdo_signal</xliff:g></item>
<item><xliff:g id="id">phone_signal</xliff:g></item>
<item><xliff:g id="id">battery</xliff:g></item>
<item><xliff:g id="id">alarm_clock</xliff:g></item>
<item><xliff:g id="id">secure</xliff:g></item>
<item><xliff:g id="id">clock</xliff:g></item>
</string-array>
@Override
public void start() {
//...
super.start(); // calls createAndAddWindows()
//...
addNavigationBar();
// Lastly, call to the icon policy to install/update all the icons.
//注释上看,最终调用这个PhoneStatusBarPolicy.java来安装或者更新Icon
mIconPolicy = new PhoneStatusBarPolicy(mContext, mCastController, mHotspotController);
}
public void start() {
//...
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
//..
// Connect in to the status bar manager service
StatusBarIconList iconList = new StatusBarIconList();
//传入了this,也就是说这个类或者子类实现了接口
mCommandQueue = new CommandQueue(this, iconList);
int[] switches = new int[8];
ArrayList<IBinder> binders = new ArrayList<IBinder>();
try {
mBarService.registerStatusBar(mCommandQueue, iconList, switches, binders);
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}
//...
// Set up the initial icon state
int N = iconList.size();
int viewIndex = 0;
for (int i=0; i<N; i++) {
StatusBarIcon icon = iconList.getIcon(i);//初始化图标的名字
if (icon != null) {
addIcon(iconList.getSlot(i), i, viewIndex, icon);
viewIndex++;
}
}
}
public interface Callbacks {
public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon);
public void updateIcon(String slot, int index, int viewIndex,
StatusBarIcon old, StatusBarIcon icon);
public void removeIcon(String slot, int index, int viewIndex);
public void disable(int state, boolean animate);
- .........
public void notificationLightPulse(int argb, int onMillis, int offMillis);
public void showScreenPinningRequest();
/// M: [SystemUI] Support "SIM indicator". @{
public void showSimIndicator(String businessType);
public void hideSimIndicator();
- .........
public void showRestoreButton(boolean flag);
}
@Override
public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList,
int switches[], List<IBinder> binders) {
enforceStatusBarService();
Slog.i(TAG, "registerStatusBar bar=" + bar);
mBar = bar;
synchronized (mIcons) {
//把mIcons的index和icon拷贝到iconList中,mIcons就是之前说到的那个数组转换来的
iconList.copyFrom(mIcons);
}
synchronized (mLock) {
switches[0] = gatherDisableActionsLocked(mCurrentUserId);
switches[1] = mSystemUiVisibility;
switches[2] = mMenuVisible ? 1 : 0;
switches[3] = mImeWindowVis;
switches[4] = mImeBackDisposition;
switches[5] = mShowImeSwitcher ? 1 : 0;
binders.add(mImeToken);
}
}
// Lastly, call to the icon policy to install/update all the icons.
mIconPolicy = new PhoneStatusBarPolicy(mContext, mCastController, mHotspotController);
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Xlog.d(TAG, "onReceive:" + action);
if (action.equals(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) {
updateAlarm();
}
else if (action.equals(Intent.ACTION_SYNC_STATE_CHANGED)) {
updateSyncState(intent);
}
else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED) ||
action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
updateBluetooth();
}
else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION) ||
action.equals(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)) {
updateVolumeZen();
}
else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
updateSimState(intent);
}
else if (action.equals(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED)) {
int currentTtyMode = intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE,
TelecomManager.TTY_MODE_OFF);
updateTTY(currentTtyMode);
}
else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
updateAlarm();
/// M: [Multi-User] register Alarm intent by user @{
int newUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
registerAlarmClockChanged(newUserId, true);
/// M: [Multi-User] register Alarm intent by user @}
}
/// M: [SystemUI] Support "Headset icon". @{
else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
updateHeadSet(intent);
}
/// @}
}
};
public PhoneStatusBarPolicy(Context context, CastController cast, HotspotController hotspot) {
mContext = context;
mCast = cast;
mHotspot = hotspot;
mService = (StatusBarManager)context.getSystemService(Context.STATUS_BAR_SERVICE);
// listen for broadcasts
IntentFilter filter = new IntentFilter();
//filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);
filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
filter.addAction(Intent.ACTION_USER_SWITCHED);
/// M: [SystemUI] Support "Headset icon". @{
filter.addAction(Intent.ACTION_HEADSET_PLUG);
/// @}
mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
/// M: [Multi-User] register Alarm intent by user
registerAlarmClockChanged(UserHandle.USER_OWNER, false);
// TTY status
mService.setIcon(SLOT_TTY, R.drawable.stat_sys_tty_mode, 0, null);
mService.setIconVisibility(SLOT_TTY, false);
/// M: [ALPS01870707] Get the TTY status when power on @{
int settingsTtyMode = Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.TTY_MODE_ENABLED,
TelecomManager.TTY_MODE_OFF);
updateTTY(settingsTtyMode);
/// M: [ALPS01870707] Get the TTY status when power on @}
// Cdma Roaming Indicator, ERI
mService.setIcon(SLOT_CDMA_ERI, R.drawable.stat_sys_roaming_cdma_0, 0, null);
mService.setIconVisibility(SLOT_CDMA_ERI, false);
// bluetooth status
updateBluetooth();
// Alarm clock
mService.setIcon(SLOT_ALARM_CLOCK, R.drawable.stat_sys_alarm, 0, null);
mService.setIconVisibility(SLOT_ALARM_CLOCK, false);
// Sync state
mService.setIcon(SLOT_SYNC_ACTIVE, R.drawable.stat_sys_sync, 0, null);
mService.setIconVisibility(SLOT_SYNC_ACTIVE, false);
// "sync_failing" is obsolete: b/1297963
// zen
mService.setIcon(SLOT_ZEN, R.drawable.stat_sys_zen_important, 0, null);
mService.setIconVisibility(SLOT_ZEN, false);
// volume
mService.setIcon(SLOT_VOLUME, R.drawable.stat_sys_ringer_vibrate, 0, null);
mService.setIconVisibility(SLOT_VOLUME, false);
updateVolumeZen();
// cast
// M: Remove CastTile when WFD is not support in quicksetting
if (mCast != null) {
mService.setIcon(SLOT_CAST, R.drawable.stat_sys_cast, 0, null);
mService.setIconVisibility(SLOT_CAST, false);
mCast.addCallback(mCastCallback);
}
// hotspot
mService.setIcon(SLOT_HOTSPOT, R.drawable.stat_sys_hotspot, 0, null);
mService.setIconVisibility(SLOT_HOTSPOT, mHotspot.isHotspotEnabled());
mHotspot.addCallback(mHotspotCallback);
/// M: [SystemUI] Support "Headset icon". @{
mService.setIcon(SLOT_HEADSET, R.drawable.stat_sys_headset_with_mic, 0, null);
mService.setIconVisibility(SLOT_HEADSET, false);
/// @}
}
public void setIcon(String slot, int iconId, int iconLevel, String contentDescription) {
try {
final IStatusBarService svc = getService();
if (svc != null) {
svc.setIcon(slot, mContext.getPackageName(), iconId, iconLevel,
contentDescription);
}
} catch (RemoteException ex) {
// system process is dead anyway.
throw new RuntimeException(ex);
}
}
private synchronized IStatusBarService getService() {
if (mService == null) {
mService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
if (mService == null) {
Slog.w("StatusBarManager", "warning: no STATUS_BAR_SERVICE");
}
}
return mService;
}
@Override
public void setIcon(String slot, String iconPackage, int iconId, int iconLevel,
String contentDescription) {
enforceStatusBar();
synchronized (mIcons) {
int index = mIcons.getSlotIndex(slot);
if (index < 0) {
throw new SecurityException("invalid status bar icon slot: " + slot);
}
StatusBarIcon icon = new StatusBarIcon(iconPackage, UserHandle.OWNER, iconId,
iconLevel, 0,
contentDescription);
//Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon);
mIcons.setIcon(index, icon);
if (mBar != null) {
try {
mBar.setIcon(index, icon);
} catch (RemoteException ex) {
}
}
}
}
public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
if (SPEW) Log.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
+ " icon=" + icon);
StatusBarIconView view = new StatusBarIconView(mContext, slot, null);
view.set(icon);
//add in mStatusIcons
mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, mIconSize));
view = new StatusBarIconView(mContext, slot, null);
view.set(icon);
mStatusIconsKeyguard.addView(view, viewIndex, new LinearLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, mIconSize));
}
2. 下滑状态栏的响应流程
首先我们要知道的是SystemUI 的状态栏再收起的时候,并不是说只有上面的状态栏那一部分才会调用相关的onTouchEvent方法的,实际上SystemUI 谁监听整个屏幕的,我们在桌面上面随便一个地方点击一下,com.android.systemui.statusbar.phone.StatusBarWindowView 都是会调用它的onTouchEvent方法的.这里先分析的是我们触碰一下状态栏哪里,状态栏是怎么样一步步的响应,最后拉下通知栏部分的.
在前面SystemUI 启动章节就提到系统是在PhoneStatusBar.java里面来加载SystemUI的视图的.那就makeStatusBarView() 从开始. super_status_bar其实就是SystemUI 状态栏的第一个layout配置文件. StatusBarWindowView设置了touch事件处理器,其实最后还是调用的自己的onTouchEvent方法.如果onTouch()返回的false,那么就会执行StatusBarWindowView 的onTouchEvent().
PhoneStatusBarView就是屏幕上面的状态栏那一部分.我们下滑之后快捷开关设置部分和通知部分就是NotificationPanelView.
protected PhoneStatusBarView makeStatusBarView() {
final Context context = mContext;
- .......
mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
R.layout.super_status_bar, null);
mStatusBarWindow.setBackground(null);
mStatusBarWindow.mService = this;
mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
checkUserAutohide(v, event);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (mExpandedVisible) {
animateCollapsePanels();
}
}
return mStatusBarWindow.onTouchEvent(event);
}});
mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
mStatusBarView.setBar(this);
holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
mStatusBarView.setPanelHolder(holder);//
PhoneStatusBarView就是通过PanelHolder来获取NotificationPanelView对象的mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
R.id.notification_panel);
mNotificationPanel.setStatusBar(this);
.....
}
public void addPanel(PanelView pv) {
mPanels.add(pv);
pv.setBar(this);
}
public void setPanelHolder(PanelHolder ph) {
if (ph == null) {
Log.e(TAG, "setPanelHolder: null PanelHolder", new Throwable());
return;
}
ph.setBar(this);
mPanelHolder = ph;
final int N = ph.getChildCount();
for (int i=0; i<N; i++) {
final View v = ph.getChildAt(i);
if (v != null && v instanceof PanelView) {
addPanel((PanelView) v);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
- .........
// figure out which panel needs to be talked to here
if (event.getAction() == MotionEvent.ACTION_DOWN) {
final PanelView panel = selectPanelForTouch(event);
if (panel == null) {
// panel is not there, so we'll eat the gesture
Log.v(TAG, String.format("onTouch: no panel for touch at (%d,%d)",
(int) event.getX(), (int) event.getY()));
mTouchingPanel = null;
return true;
}
boolean enabled = panel.isEnabled();
if (DEBUG) LOG("PanelBar.onTouch: state=%d ACTION_DOWN: panel %s %s", mState, panel,
(enabled ? "" : " (disabled)"));
if (!enabled) {
// panel is disabled, so we'll eat the gesture
Log.v(TAG, String.format(
"onTouch: panel (%s) is disabled, ignoring touch at (%d,%d)",
panel, (int) event.getX(), (int) event.getY()));
mTouchingPanel = null;
return true;
}
startOpeningPanel(panel);
}
final boolean result = mTouchingPanel != null
? mTouchingPanel.onTouchEvent(event)
: true;
return result;
}
2.1 通知部分NotificationStackScrollLayout的展开流程
public void setExpandedHeightInternal(float h) {
Log.d(TAG,"setExpandedHeightInternal finish() h=" + h + " mHeightAnimator=" + (mHeightAnimator==null));
float fhWithoutOverExpansion = getMaxPanelHeight() - getOverExpansionAmount();
if (mHeightAnimator == null) {
float overExpansionPixels = Math.max(0, h - fhWithoutOverExpansion);
if (getOverExpansionPixels() != overExpansionPixels && mTracking) {
setOverExpansion(overExpansionPixels, true /* isPixels */);
}
mExpandedHeight = Math.min(h, fhWithoutOverExpansion) + getOverExpansionAmount();
} else {
mExpandedHeight = h;
if (mOverExpandedBeforeFling) {
setOverExpansion(Math.max(0, h - fhWithoutOverExpansion), false /* isPixels */);
}
}
mExpandedHeight = Math.max(0, mExpandedHeight);
mExpandedFraction = Math.min(1f, fhWithoutOverExpansion == 0
? 0
: mExpandedHeight / fhWithoutOverExpansion);
onHeightUpdated(mExpandedHeight);
notifyBarPanelExpansionChanged();
}
@Overrideprotected void onHeightUpdated(float expandedHeight) {Log.d(TAG,"onHeightUpdated expandedHeight=" + expandedHeight );if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) {positionClockAndNotifications();} //部分代码去掉了.......mNotificationStackScroller.setStackHeight(expandedHeight);updateHeader();updateUnlockIcon();updateNotificationTranslucency();}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
.....
mNotificationStackScroller.setOnHeightChangedListener(this);
mNotificationStackScroller.setOverscrollTopChangedListener(this);
mNotificationStackScroller.setOnEmptySpaceClickListener(this);
mNotificationStackScroller.setScrollView(mScrollView);
....
// recompute internal state when qspanel height changes
mQsContainer.addOnLayoutChangeListener(new OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right,
int bottom, int oldLeft, int oldTop, int oldRight,
int oldBottom) {
final int height = bottom - top;
final int oldHeight = oldBottom - oldTop;
if (height != oldHeight) {
onScrollChanged();
}
}
});
}
@Override
public void onScrollChanged() {
if (mQsExpanded) {
requestScrollerTopPaddingUpdate(false /* animate */);
requestPanelHeightUpdate();
}
}
private void requestScrollerTopPaddingUpdate(boolean animate) {
mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(),
mScrollView.getScrollY(),
mAnimateNextTopPaddingChange || animate,
mKeyguardShowing
&& (mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted));
mAnimateNextTopPaddingChange = false;
}
2.2 StatusBarHeaderView部分展开
/**
* Hides the header when notifications are colliding with it.
*/
private void updateHeader() {
if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
|| mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
Log.d(TAG,"updateHeader ------if ");
updateHeaderKeyguard();
} else {
Log.d(TAG,"updateHeader ------else ");
updateHeaderShade();
}
}
private void updateHeaderShade() {
Log.d(TAG,"updateHeaderShade mHeaderAnimatingIn=" + mHeaderAnimatingIn + " mQsExpansionHeight=" + mQsExpansionHeight +
" getHeaderTranslation=" + getHeaderTranslation());
if (!mHeaderAnimatingIn) {
mHeader.setTranslationY(getHeaderTranslation());
}
setQsTranslation(mQsExpansionHeight);
}
2.3 快捷设置QSPanel部分展开
2.3.1解锁之后的下滑状态栏栏流程.
public boolean onTouchEvent(MotionEvent event) {
if(DEBUG)Log.d(TAG , "onTouchEvent_npv action=" +event.getActionMasked());
if(getIsSuperSaveMode() || isSecurityMode()){//zhangle add
Log.d(TAG,"onTouchEvent IsSuperSaveMode" );
return false;
}
if (mBlockTouches) {
return false;
}
resetDownStates(event);
if ((!mIsExpanding || mHintAnimationRunning)
&& !mQsExpanded
&& mStatusBar.getBarState() != StatusBarState.SHADE) {
if(!doovDefaultLockView){
mAfforanceHelper.onTouchEvent(event);
}
}
if (mOnlyAffordanceInThisMotion) {
return true;
}
if (event.getActionMasked() == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
&& mStatusBar.getBarState() != StatusBarState.KEYGUARD && !mQsExpanded
&& mQsExpansionEnabled) {
// Down in the empty area while fully expanded - go to QS.
mQsTracking = true;
mConflictingQsExpansionGesture = true;
onQsExpansionStarted();
mInitialHeightOnTouch = mQsExpansionHeight;
mInitialTouchY = event.getX();
mInitialTouchX = event.getY();
}
mIsDown = mExpandedHeight > mLastExpandedHeight;
mLastExpandedHeight = mExpandedHeight;
//zhangle add end
if (mExpandedHeight != 0) {
handleQsDown(event);
}
if (!mQsExpandImmediate && mQsTracking) {//
mQsTracking会在NotificationPanelView的onInterceptTouchEvent()里面处理Action_Move的时间变成true.onQsTouch(event);
if (!mConflictingQsExpansionGesture) {
return true;
}
}
if (event.getActionMasked() == MotionEvent.ACTION_CANCEL
|| event.getActionMasked() == MotionEvent.ACTION_UP) {
mConflictingQsExpansionGesture = false;
}
if (event.getActionMasked() == MotionEvent.ACTION_DOWN && mExpandedHeight == 0
&& mQsExpansionEnabled) {
mTwoFingerQsExpandPossible = true;
}
//这一段代码是为了实现默认滑动下拉可以完全展开通知栏自己添加的,后面再分析
if (mTwoFingerQsExpandPossible && event.getActionMasked() == MotionEvent.ACTION_MOVE
&& event.getY(event.getActionIndex()) >= mStatusBarMinHeight && mIsExpanding) {
mQsExpandImmediate = true;
requestPanelHeightUpdate();
setListening(true);
}
if (mTwoFingerQsExpandPossible && event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN
&& event.getPointerCount() == 2
&& event.getY(event.getActionIndex()) < mStatusBarMinHeight) {
mQsExpandImmediate = true;
requestPanelHeightUpdate();
// Normally, we start listening when the panel is expanded, but here we need to start
// earlier so the state is already up to date when dragging down.
setListening(true);
}
super.onTouchEvent(event);//调用PanelView
return true;
}
public boolean onTouchEvent(MotionEvent event) {
if(DEBUG)Log.d(TAG , "onTouchEvent_pv action= " + event.getActionMasked() +(mInstantExpanding || mTouchDisabled));
if (mInstantExpanding || mTouchDisabled||(mStatusBar.getBarState() == StatusBarState.KEYGUARD)) {//解锁页面不处理相关事件
return false;
}
/*
* We capture touch events here and update the expand height here in case according to
* the users fingers. This also handles multi-touch.
*
* If the user just clicks shortly, we give him a quick peek of the shade.
*
* Flinging is also enabled in order to open or close the shade.
*/
int pointerIndex = event.findPointerIndex(mTrackingPointer);
if (pointerIndex < 0) {
pointerIndex = 0;
mTrackingPointer = event.getPointerId(pointerIndex);
}
final float y = event.getY(pointerIndex);
final float x = event.getX(pointerIndex);
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mGestureWaitForTouchSlop = mExpandedHeight == 0f;
}
boolean waitForTouchSlop = hasConflictingGestures() || mGestureWaitForTouchSlop;
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mInitialTouchY = y;
mInitialTouchX = x;
mInitialOffsetOnTouch = mExpandedHeight;
mTouchSlopExceeded = false;
mJustPeeked = false;
mPanelClosedOnDown = mExpandedHeight == 0.0f;
mHasLayoutedSinceDown = false;
mUpdateFlingOnLayout = false;
mPeekTouching = mPanelClosedOnDown;
mTouchAboveFalsingThreshold = false;
mDozingOnDown = isDozing();
if (mVelocityTracker == null) {
initVelocityTracker();
}
trackMovement(event);
if (!waitForTouchSlop || (mHeightAnimator != null && !mHintAnimationRunning) ||
mPeekPending || mPeekAnimator != null) {
cancelHeightAnimator();
cancelPeek();
mTouchSlopExceeded = (mHeightAnimator != null && !mHintAnimationRunning)
|| mPeekPending || mPeekAnimator != null;
onTrackingStarted();
}
if (mExpandedHeight == 0) {
schedulePeek();
}
break;
case MotionEvent.ACTION_POINTER_UP:
final int upPointer = event.getPointerId(event.getActionIndex());
if (mTrackingPointer == upPointer) {
// gesture is ongoing, find a new pointer to track
final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
final float newY = event.getY(newIndex);
final float newX = event.getX(newIndex);
mTrackingPointer = event.getPointerId(newIndex);
mInitialOffsetOnTouch = mExpandedHeight;
mInitialTouchY = newY;
mInitialTouchX = newX;
}
break;
case MotionEvent.ACTION_MOVE:
float h = y - mInitialTouchY;
// If the panel was collapsed when touching, we only need to check for the
// y-component of the gesture, as we have no conflicting horizontal gesture.
if (Math.abs(h) > mTouchSlop
&& (Math.abs(h) > Math.abs(x - mInitialTouchX)
|| mInitialOffsetOnTouch == 0f)) {
mTouchSlopExceeded = true;
if (waitForTouchSlop && !mTracking) {
if (!mJustPeeked && mInitialOffsetOnTouch != 0f) {
mInitialOffsetOnTouch = mExpandedHeight;
mInitialTouchX = x;
mInitialTouchY = y;
h = 0;
}
cancelHeightAnimator();
removeCallbacks(mPeekRunnable);
mPeekPending = false;
onTrackingStarted();
}
}
final float newHeight = Math.max(0, h + mInitialOffsetOnTouch);
if (newHeight > mPeekHeight) {
if (mPeekAnimator != null) {
mPeekAnimator.cancel();
}
mJustPeeked = false;
}
if (-h >= getFalsingThreshold()) {
mTouchAboveFalsingThreshold = true;
}
if (!mJustPeeked && (!waitForTouchSlop || mTracking) && !isTrackingBlocked()) {
Log.d(TAG , "onTouchEvent_move_newHeight="+newHeight);
setExpandedHeightInternal(newHeight);//手指在屏幕下移的时候调用的就是这里
}
trackMovement(event);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mTrackingPointer = -1;
trackMovement(event);
if ((mTracking && mTouchSlopExceeded)
|| Math.abs(x - mInitialTouchX) > mTouchSlop
|| Math.abs(y - mInitialTouchY) > mTouchSlop
|| event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
float vel = 0f;
float vectorVel = 0f;
if (mVelocityTracker != null) {
mVelocityTracker.computeCurrentVelocity(1000);
vel = mVelocityTracker.getYVelocity();
vectorVel = (float) Math.hypot(
mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
}
boolean expand = flingExpands(vel, vectorVel)
|| event.getActionMasked() == MotionEvent.ACTION_CANCEL;
onTrackingStopped(expand);
DozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
mStatusBar.isFalsingThresholdNeeded(),
mStatusBar.isScreenOnComingFromTouch());
// Log collapse gesture if on lock screen.
if (!expand && mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
float displayDensity = mStatusBar.getDisplayDensity();
int heightDp = (int) Math.abs((y - mInitialTouchY) / displayDensity);
int velocityDp = (int) Math.abs(vel / displayDensity);
EventLogTags.writeSysuiLockscreenGesture(
EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_UP_UNLOCK,
heightDp, velocityDp);
}
fling(vel, expand);//滑动抬起手指时,自动下滑就是这里在处理
mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown;
if (mUpdateFlingOnLayout) {
mUpdateFlingVelocity = vel;
}
} else {
boolean expands = onEmptySpaceClick(mInitialTouchX);
onTrackingStopped(expands);
}
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
mPeekTouching = false;
break;
}
return !waitForTouchSlop || mTracking;
}
2.3.2锁屏页面的下滑状态栏栏流程.
public boolean onInterceptTouchEvent(MotionEvent event) {
if(DEBUG){
Log.d(TAG , "onInterceptTouchEvent_npv mBlockTouches=" + mBlockTouches + " event" + event.getAction());
}
if(getIsSuperSaveMode() || isSecurityMode()){//zhangle add
Log.d(TAG,"onInterceptTouchEvent IsSuperSaveMode" );
return false;
}
if (mBlockTouches) {
return false;
}
resetDownStates(event);
int pointerIndex = event.findPointerIndex(mTrackingPointer);
if (pointerIndex < 0) {
pointerIndex = 0;
mTrackingPointer = event.getPointerId(pointerIndex);
}
final float x = event.getX(pointerIndex);
final float y = event.getY(pointerIndex);
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mIntercepting = true;
mInitialTouchY = y;
mInitialTouchX = x;
initVelocityTracker();
trackMovement(event);
if (shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
getParent().requestDisallowInterceptTouchEvent(true);
}
if (mQsExpansionAnimator != null) {
onQsExpansionStarted();
mInitialHeightOnTouch = mQsExpansionHeight;
mQsTracking = true;
mIntercepting = false;
mNotificationStackScroller.removeLongPressCallback();
}
break;
case MotionEvent.ACTION_POINTER_UP:
final int upPointer = event.getPointerId(event.getActionIndex());
if (mTrackingPointer == upPointer) {
// gesture is ongoing, find a new pointer to track
final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
mTrackingPointer = event.getPointerId(newIndex);
mInitialTouchX = event.getX(newIndex);
mInitialTouchY = event.getY(newIndex);
}
break;
case MotionEvent.ACTION_MOVE:
final float h = y - mInitialTouchY;
trackMovement(event);
if (mQsTracking) {
// Already tracking because onOverscrolled was called. We need to update here
// so we don't stop for a frame until the next touch event gets handled in
// onTouchEvent.
setQsExpansion(h + mInitialHeightOnTouch);
trackMovement(event);
mIntercepting = false;
return true;
}
if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)
&& shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
//锁屏页面下滑时
shouldQuickSettingsIntercept会返回truemQsTracking = true;
onQsExpansionStarted();
mInitialHeightOnTouch = mQsExpansionHeight;
mInitialTouchY = y;
mInitialTouchX = x;
mIntercepting = false;
mNotificationStackScroller.removeLongPressCallback();
return true;
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
trackMovement(event);
if (mQsTracking) {
flingQsWithCurrentVelocity(
event.getActionMasked() == MotionEvent.ACTION_CANCEL);
mQsTracking = false;
}
mIntercepting = false;
break;
}
return super.onInterceptTouchEvent(event);
}
public boolean onTouchEvent(MotionEvent event) {
........
if (!mQsExpandImmediate && mQsTracking) {//mQsTracking会在NotificationPanelView的onInterceptTouchEvent()里面处理Action_Move的时间变成true.
onQsTouch(event);
if (!mConflictingQsExpansionGesture) {
return true;
}
}
if (event.getActionMasked() == MotionEvent.ACTION_CANCEL
|| event.getActionMasked() == MotionEvent.ACTION_UP) {
mConflictingQsExpansionGesture = false;
}
if (event.getActionMasked() == MotionEvent.ACTION_DOWN && mExpandedHeight == 0
&& mQsExpansionEnabled) {
mTwoFingerQsExpandPossible = true;
}
//这一段代码是为了实现默认滑动下拉可以完全展开通知栏自己添加的,后面再分析
if (mTwoFingerQsExpandPossible && event.getActionMasked() == MotionEvent.ACTION_MOVE
&& event.getY(event.getActionIndex()) >= mStatusBarMinHeight && mIsExpanding) {
mQsExpandImmediate = true;
requestPanelHeightUpdate();
setListening(true);
}
........
super.onTouchEvent(event);//调用PanelView
return true;
}
private void onQsTouch(MotionEvent event) {
if(DEBUG)Log.d(TAG,"onQsTouch action=" + event.getActionMasked());
int pointerIndex = event.findPointerIndex(mTrackingPointer);
if (pointerIndex < 0) {
pointerIndex = 0;
mTrackingPointer = event.getPointerId(pointerIndex);
}
final float y = event.getY(pointerIndex);
final float x = event.getX(pointerIndex);
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
.....
case MotionEvent.ACTION_POINTER_UP:
.....
case MotionEvent.ACTION_MOVE:
final float h = y - mInitialTouchY;
setQsExpansion(h + mInitialHeightOnTouch);
if (h >= getFalsingThreshold()) {
mQsTouchAboveFalsingThreshold = true;
}
trackMovement(event);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mQsTracking = false;
mTrackingPointer = -1;
trackMovement(event);
float fraction = getQsExpansionFraction();
if ((fraction != 0f || y >= mInitialTouchY)
&& (fraction != 1f || y <= mInitialTouchY)) {
flingQsWithCurrentVelocity(
event.getActionMasked() == MotionEvent.ACTION_CANCEL);
} else {
mScrollYOverride = -1;
}
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
break;
}
}
private void slideDownloadQS() {
if(mIsExpanding && mQsExpansionEnabled && !mQsExpanded && mExpandedHeight!=0 && mIsDown){
Log.d(TAG , "slideDownloadQS ---");
setListening(true);
onQsExpansionStarted();
flingSettings(0 /* vel */, true /* expand */);
}
}
if (mTwoFingerQsExpandPossible && event.getActionMasked() == MotionEvent.ACTION_MOVE
&& event.getY(event.getActionIndex()) >= mStatusBarMinHeight && mIsExpanding) {
mQsExpandImmediate = true;
requestPanelHeightUpdate();
setListening(true);
}
private void setQsTranslation(float height) {
.........
if(DEBUG) Log.d(TAG,"setQsTranslation mHeaderAnimatingIn=" + mHeaderAnimatingIn );
if (!mHeaderAnimatingIn) {
//zhangle delete at 20150521
//mQsContainer.setY(height - mQsContainer.getDesiredHeight() + getHeaderTranslation());
//zhangle add at 20150521 start
if(DEBUG)Log.d(TAG,"setQsTranslation height=" + height + " " + getHeaderTranslation() );
int heightOverride = (int) (mQsMaxExpansionHeight - height) ;
int y = mHeader.getCollapsedHeight()-heightOverride;
mQsContainer.setScrollY(-heightOverride);
mQsContainer.setY(y);
mQsContainer.invalidate();
if(DEBUG) Log.d(TAG,"setQsTranslation heightOverride=" + heightOverride + "y=" +y);
- .......
- }
- .......
}
2.4按back键通知栏收起
public boolean dispatchKeyEvent(KeyEvent event) {
boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_BACK:
if (!down) {
mService.onBackPressed();//
mService就是PhoneStatusBar}
return true;
case KeyEvent.KEYCODE_MENU:
if (!down) {
return mService.onMenuPressed();
}
case KeyEvent.KEYCODE_SPACE:
if (!down) {
return mService.onSpacePressed();
}
break;
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
if (mService.isDozing()) {
MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(event, true);
return true;
}
break;
}
if (mService.interceptMediaKey(event)) {
return true;
}
return super.dispatchKeyEvent(event);
}
2.5 按home键时状态栏的收起流程.
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
if (DEBUG) Log.v(TAG, "onReceive: " + intent);
if(DEBUG) Log.d(TAG, "onReceive:-------------- intent.getAction=" + intent.getAction());
String action = intent.getAction();
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {//监听home键
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)) {
mScreenOn = false;
notifyNavigationBarScreenOn(false);
notifyHeadsUpScreenOn(false);
finishBarAnimations();
resetUserExpandedStates();
}
else if (Intent.ACTION_SCREEN_ON.equals(action)) {
mScreenOn = true;
notifyNavigationBarScreenOn(true);
}
else if (ACTION_DEMO.equals(action)) {
Bundle bundle = intent.getExtras();
if (bundle != null) {
String command = bundle.getString("command", "").trim().toLowerCase();
if (command.length() > 0) {
try {
dispatchDemoCommand(command, bundle);
} catch (Throwable t) {
Log.w(TAG, "Error running demo command, intent=" + intent, t);
}
}
}
} else if ("fake_artwork".equals(action)) {
if (DEBUG_MEDIA_FAKE_ARTWORK) {
updateMediaMetaData(true);
}
}else if(LEATHER_PATTERN.equals(action)){
mHandler.removeMessages(MSG_LEATHER_PATTERN);
mHandler.sendEmptyMessage(MSG_LEATHER_PATTERN);
}
}
};
public void animateCollapsePanels(int flags) {
animateCollapsePanels(flags, false /* force */);
}
public void animateCollapsePanels(int flags, boolean force) {
if (!force &&
(mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
runPostCollapseRunnables();
return;
}
if (SPEW) {
Log.d(TAG, "animateCollapse():"
+ " mExpandedVisible=" + mExpandedVisible
+ " flags=" + flags);
}
if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
if (!mHandler.hasMessages(MSG_HIDE_RECENT_APPS)) {
mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
}
}
if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) {
mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL);
mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL);
}
if (mStatusBarWindow != null) {
// release focus immediately to kick off focus change transition
mStatusBarWindowManager.setStatusBarFocusable(false);
mStatusBarWindow.cancelExpandHelper();
mStatusBarView.collapseAllPanels(true);//TODO:1
}
}
public void collapseAllPanels(boolean animate) {//case1:press home key -> animate=true
boolean waiting = false;
for (PanelView pv : mPanels) {
if (animate && !pv.isFullyCollapsed()) {//TODO
pv.collapse(true /* delayed */);//第一次是执行的这里
waiting = true;
} else {//后面会再次调用这个方法的时时候会传入参数为false,就执行的这里
pv.resetViews();
pv.setExpandedFraction(0); // just in case
pv.setVisibility(View.GONE);
pv.cancelPeek();
}
}
if (DEBUG) LOG("collapseAllPanels: animate=%s waiting=%s", animate, waiting);
Log.d(TAG,"collapseAllPanels animate=" +animate + " waiting=" + waiting + " mState=" + mState);
if (/*!waiting &&*/ mState != STATE_CLOSED) {//zhangle update
// it's possible that nothing animated, so we replicate the termination
// conditions of panelExpansionChanged here
go(STATE_CLOSED);
onAllPanelsCollapsed();
}
}
public void collapse(boolean delayed) {
if (DEBUG) logf("collapse: " + this);
if (mPeekPending || mPeekAnimator != null) {
mCollapseAfterPeek = true;
if (mPeekPending) {
// We know that the whole gesture is just a peek triggered by a simple click, so
// better start it now.
removeCallbacks(mPeekRunnable);
mPeekRunnable.run();
}
} else if (!isFullyCollapsed() && !mTracking && !mClosing) {
cancelHeightAnimator();
mClosing = true;
notifyExpandingStarted();
if (delayed) {
postDelayed(mFlingCollapseRunnable, 120);
} else {
fling(0, false /* expand */);
}
}
}
@Override
public void onAllPanelsCollapsed() {
super.onAllPanelsCollapsed();
// Close the status bar in the next frame so we can show the end of the animation.
postOnAnimation(new Runnable() {
@Override
public void run() {
mBar.makeExpandedInvisible();
}
});
mLastFullyOpenedPanel = null;
}
protected void fling(float vel, boolean expand) {
cancelPeek();
float target = expand ? getMaxPanelHeight() : 0.0f;
// Hack to make the expand transition look nice when clear all button is visible - we make
// the animation only to the last notification, and then jump to the maximum panel height so
// clear all just fades in and the decelerating motion is towards the last notification.
final boolean clearAllExpandHack = expand && fullyExpandedClearAllVisible()
&& mExpandedHeight < getMaxPanelHeight() - getClearAllHeight()
&& !isClearAllVisible();
final boolean isExpand = expand;//zhangle add
if (clearAllExpandHack) {
target = getMaxPanelHeight() - getClearAllHeight();
}
if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) {//TODO
notifyExpandingFinished();
return;
}
//......
}
3. 状态图标添加到状态栏的过程
先从配置文件来分析,从super_status_bar.xml开始,这个是整个SystemUI应用的第一个布局配置文件.其中包括状态栏,下拉菜单,通知栏,幻灯片页面,锁屏页面.其中锁屏页面是在SystemUI 里面的,不是在Keyguard里面的,只有涉及到安全解锁比如密码相关等才在Keyguard里面的
super_status_bar.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- This is the combined status bar / notification panel window. -->
<com.android.systemui.statusbar.phone.StatusBarWindowView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
- <!--幻灯片-->
<com.android.systemui.statusbar.BackDropView
android:id="@+id/backdrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
>
<ImageView android:id="@+id/backdrop_back"
android:layout_width="match_parent"
android:scaleType="centerCrop"
android:layout_height="match_parent" />
<ImageView android:id="@+id/backdrop_front"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:visibility="invisible" />
</com.android.systemui.statusbar.BackDropView>
- <!--这玩意没有见过-->
<com.android.systemui.statusbar.ScrimView android:id="@+id/scrim_behind"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no" />
- <!--状态栏部分-->
<include layout="@layout/status_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/status_bar_height" />
- <!--调节亮度的部分-->
<FrameLayout android:id="@+id/brightness_mirror"
android:layout_width="@dimen/notification_panel_width"
android:layout_height="wrap_content"
android:layout_gravity="@integer/notification_panel_layout_gravity"
android:paddingLeft="@dimen/notification_side_padding"
android:paddingRight="@dimen/notification_side_padding"
android:visibility="gone">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="2dp"
android:background="@drawable/brightness_mirror_background">
<include layout="@layout/quick_settings_brightness_dialog"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</FrameLayout>
</FrameLayout>
- <!--状态栏下拉之后部分也是锁屏首页部分-->
<com.android.systemui.statusbar.phone.PanelHolder
android:id="@+id/panel_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/transparent" >
<include layout="@layout/status_bar_expanded"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
</com.android.systemui.statusbar.phone.PanelHolder>
<com.android.systemui.statusbar.ScrimView android:id="@+id/scrim_in_front"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no" />
</com.android.systemui.statusbar.phone.StatusBarWindowView>
<com.android.systemui.statusbar.phone.PhoneStatusBarView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
android:id="@+id/status_bar"
android:background="@drawable/system_bar_background"
android:orientation="vertical"
android:focusable="true"
android:descendantFocusability="afterDescendants"
>
<ImageView
android:id="@+id/notification_lights_out"
android:layout_width="@dimen/status_bar_icon_size"
android:layout_height="match_parent"
android:paddingStart="6dip"
android:paddingBottom="2dip"
android:src="@drawable/ic_sysbar_lights_out_dot_small"
android:scaleType="center"
android:visibility="gone"
/>
<LinearLayout android:id="@+id/status_bar_contents"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="6dp"
android:paddingEnd="8dp"
android:orientation="horizontal"
>
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/notification_icon_area"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
>
<!-- The alpha of this area is both controlled from PhoneStatusBarTransitions and
PhoneStatusBar (DISABLE_NOTIFICATION_ICONS), so we need two views here. -->
<com.android.keyguard.AlphaOptimizedLinearLayout
android:id="@+id/notification_icon_area_inner"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<com.android.systemui.statusbar.StatusBarIconView android:id="@+id/moreIcon"
android:layout_width="@dimen/status_bar_icon_size"
android:layout_height="match_parent"
android:src="@drawable/stat_notify_more"
android:visibility="gone"
/>
<com.android.systemui.statusbar.phone.IconMerger android:id="@+id/notificationIcons"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:gravity="center_vertical"
android:orientation="horizontal"/>
</com.android.keyguard.AlphaOptimizedLinearLayout>
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
<com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
>
- <!--sim卡,wifi,蓝牙,电池等系统图标部分,我们想要添加其他图标可以在这个里面添加-->
<include layout="@layout/system_icons" />
- <!--时间部分-->
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:singleLine="true"
android:paddingStart="3dp"
android:gravity="center_vertical|start"
/>
<!-- zhangle update
android:paddingStart="7dp" to 3dp
-->
</com.android.keyguard.AlphaOptimizedLinearLayout>
</LinearLayout>
- <!--应该是显示个人图像的部分-->
<ViewStub
android:id="@+id/ticker_stub"
android:inflatedId="@+id/ticker"
android:layout="@layout/status_bar_ticker"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</com.android.systemui.statusbar.phone.PhoneStatusBarView>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/system_icons"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical">
<com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/statusIcons"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal"/>
- <!--sim/wifi/vpn/飞行模式-->
<include layout="@layout/signal_cluster_view"
android:id="@+id/signal_cluster"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"/>
- <!--电池百分比数字-->
<TextView
android:id="@+id/battery_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="5dp"
android:visibility="invisible"
android:textColor="@color/status_bar_clock_color"
android:textSize="@dimen/battery_level_text_size"
android:theme="@style/shadow_doov"
android:gravity="center"/>
<!--电池进度比例图标-->
<!-- battery must be padded below to match assets -->
<com.android.systemui.BatteryMeterView android:id="@+id/battery"
android:layout_height="20dp"
android:layout_width="20dp"
android:rotation="-90"
android:gravity="center"
android:background="@color/shadow_color"
android:layout_gravity="center"/>
</LinearLayout>
<com.android.systemui.statusbar.SignalClusterView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingEnd="@dimen/signal_cluster_battery_padding"
>
<ImageView
android:id="@+id/vpn"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:paddingEnd="6dp"
android:src="@drawable/stat_sys_vpn_ic"
/>
<FrameLayout
android:id="@+id/wifi_combo"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
>
<ImageView
android:id="@+id/wifi_signal"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
/>
</FrameLayout>
<View
android:id="@+id/wifi_signal_spacer"
android:layout_width="4dp"
android:layout_height="4dp"
android:visibility="gone"
/>
<!-- M: Support "SystemUI - VoLTE icon". @{ -->
<ImageView
android:id="@+id/volte_icon"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:visibility="gone"
/>
<!-- M: Support "SystemUI - VoLTE icon". }@ -->
<!-- M: Support "Default SIM Indicator". @{ -->
<ImageView
android:id="@+id/sim_indicator_internet_or_alwaysask"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_marginEnd="3dip"
/>
<!-- M: Support "Default SIM Indicator". }@ -->
<LinearLayout
android:id="@+id/mobile_signal_group"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
>
</LinearLayout>
<ImageView
android:id="@+id/no_sims"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="@drawable/stat_sys_no_sims"
/>
<View
android:id="@+id/wifi_airplane_spacer"
android:layout_width="4dp"
android:layout_height="4dp"
android:visibility="gone"
/>
<ImageView
android:id="@+id/airplane"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
/>
</com.android.systemui.statusbar.SignalClusterView>
protected PhoneStatusBarView makeStatusBarView() {
mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
R.layout.super_status_bar, null);//加载第一个布局文件
mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);//加载状态栏布局文件
mStatusBarView.setBar(this);
holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);//状态栏下拉部分
mStatusBarView.setPanelHolder(holder);
mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
R.id.notification_panel);//状态栏下拉部分
mNotificationPanel.setStatusBar(this);
mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area);
mSystemIcons = (LinearLayout) mStatusBarView.findViewById(R.id.system_icons);//系统图标
mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);
mNotificationIconArea = mStatusBarView.findViewById(R.id.notification_icon_area_inner);
mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon);
mNotificationIcons.setOverflowIndicator(mMoreIcon);
mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents);
//zhangle add for show battery level start
battery_percent = (TextView)mStatusBarView.findViewById(R.id.battery_percent);//电池百分比
battery_percent.setVisibility(getShowBatteryLevelStatus()?View.VISIBLE:View.GONE);
mBatteryLevelContentObserver = new BatteryLevelContentObserver(new Handler());
mBatteryLevelContentObserver.startObserver();
//zhangle add for show battery level end
mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById(
R.id.notification_stack_scroller);//下拉菜单通知部分
mStackScroller.setLongPressListener(getNotificationLongClicker());
mStackScroller.setPhoneStatusBar(this);
mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_no_notifications, mStackScroller, false);
mStackScroller.setEmptyShadeView(mEmptyShadeView);//下拉菜单无通知
mDismissView = (DismissView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_notification_dismiss_all, mStackScroller, false);//一键清除通知
mDismissView.setOnButtonClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
clearAllNotifications();
}
});
mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);//下拉菜单Header 部分,就是有设置图标和时间那个部分
mHeader.setActivityStarter(this);
mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header);//锁屏页面的状态栏
mStatusIconsKeyguard = (LinearLayout) mKeyguardStatusBar.findViewById(R.id.statusIcons);
mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
mKeyguardBottomArea =
(KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);
mBatteryController = new BatteryController(mContext);//电池控制器
mBatteryController.addStateChangedCallback(new BatteryStateChangeCallback() {
@Override
public void onPowerSaveChanged() {
}
@Override
public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
battery_percent.setText("" + level + "%");//zhangle add
}
});
mNetworkController = new NetworkControllerImpl(mContext);//网络控制器
mHotspotController = new HotspotControllerImpl(mContext);//热点控制器
mBluetoothController = new BluetoothControllerImpl(mContext, mHandlerThread.getLooper());//蓝牙控制器
mSecurityController = new SecurityControllerImpl(mContext);//安全控制器
if (SIMHelper.isMtkHotKnotSupport()) {
mHotKnotController = new HotKnotControllerImpl(mContext);//HotKnont控制器.mtk 自己研发的功能
} else {
mHotKnotController = null;
}
if (SIMHelper.isMtkAudioProfilesSupport()) {
mAudioProfileController = new AudioProfileControllerImpl(mContext);//情景模式控制器
} else {
mAudioProfileController = null;
}
if(!SIMHelper.isWifiOnlyDevice()) {
mDataConnectionController = new DataConnectionControllerImpl(mContext);//数据连接控制器
} else {
mDataConnectionController = null;
}
if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) {
mRotationLockController = new RotationLockControllerImpl(mContext);//屏幕旋转
}
mUserInfoController = new UserInfoController(mContext);//个人用户账号信息
mVolumeComponent = getComponent(VolumeComponent.class);
//音量控制if (mVolumeComponent != null) {
mZenModeController = mVolumeComponent.getZenController();
//勿扰模式}
return mStatusBarView;
}
3.1 如何实现状态栏图标和文字变色
<!-- 状态栏背景 -->
item name="android:colorPrimaryDark">@android:color/holo_blue_bright</item>
<!-- 标题栏背景 -->
<item name="android:colorPrimary">@android:color/holo_blue_bright</item>
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
getWindow().setBackgroundDrawableResource(R.color.red_color);//页面背景色
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
getWindow().setStatusBarColor(Color.BLUE);//状态栏背景色
}
private int updateSystemUiVisibilityLw() {
// If there is no window focused, there will be nobody to handle the events
// anyway, so just hang on in whatever state we're in until things settle down.
final WindowState win = mFocusedWindow != null ? mFocusedWindow
: mTopFullscreenOpaqueWindowState;
if (win == null) {
return 0;
}
if ((win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 && mHideLockScreen == true) {
// We are updating at a point where the keyguard has gotten
// focus, but we were last in a state where the top window is
// hiding it. This is probably because the keyguard as been
// shown while the top window was displayed, so we want to ignore
// it here because this is just a very transient change and it
// will quickly lose focus once it correctly gets hidden.
return 0;
}
int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null)
& ~mResettingSystemUiFlags
& ~mForceClearedSystemUiFlags;
if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) {
tmpVisibility &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS);
}
/// M: BMW. If top window is a floating window, we should clear the low profile and only content flags@{
if (MultiWindowProxy.isFeatureSupport()
&& win.isFloatingWindow()) {
tmpVisibility = tmpVisibility
& ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
& ~View.SYSTEM_UI_FLAG_LOW_PROFILE
& ~View.SYSTEM_UI_FLAG_FULLSCREEN;
}
/// @}
final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
final int diff = visibility ^ mLastSystemUiFlags;
final boolean needsMenu = win.getNeedsMenuLw(mTopFullscreenOpaqueWindowState);
if (diff == 0 && mLastFocusNeedsMenu == needsMenu
&& mFocusedApp == win.getAppToken()) {
return 0;
}
mLastSystemUiFlags = visibility;
mLastFocusNeedsMenu = needsMenu;
mFocusedApp = win.getAppToken();
mHandler.post(new Runnable() {
@Override
public void run() {
try {
IStatusBarService statusbar = getStatusBarService();
if (statusbar != null) {
statusbar.setSystemUiVisibility(visibility, 0xffffffff, win.toString());
/*if (DEBUG)*/ Slog.d(TAG, "setSystemUiVisibility = " + visibility);
statusbar.topAppWindowChanged(needsMenu);
}
} catch (RemoteException e) {
// re-acquire status bar service next time it is needed.
mStatusBarService = null;
}
}
});
return diff;
}
- if(VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
Window window = getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
| WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
window.getDecorView().setSystemUiVisibility(0x00000010);
//View.SYSTEM_UI_FLAG_TEXT_BLACK:0x00000010
}
private int mStatusBarBlackColor;
private boolean mBlack = false;
@Override // CommandQueue
public void setSystemUiVisibility(int vis, int mask) {
final int oldVal = mSystemUiVisibility;
final int newVal = (oldVal&~mask) | (vis&mask);
final int diff = newVal ^ oldVal;
if (DEBUG) Log.d(TAG, String.format(
"setSystemUiVisibility vis=%s mask=%s oldVal=%s newVal=%s diff=%s",
Integer.toHexString(vis), Integer.toHexString(mask),
Integer.toHexString(oldVal), Integer.toHexString(newVal),
Integer.toHexString(diff)));
if (diff != 0) {
// we never set the recents bit via this method, so save the prior state to prevent
// clobbering the bit below
final boolean wasRecentsVisible = (mSystemUiVisibility & View.RECENT_APPS_VISIBLE) > 0;
mSystemUiVisibility = newVal;
// update low profile
if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
final boolean lightsOut = (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0;
if (lightsOut) {
animateCollapsePanels();
if (mTicking) {
haltTicker();
}
}
setAreThereNotifications();
}
//zhangle add start
boolean black = mBlack;
mBlack = (vis & 0x00000010) != 0;
if(mBlack != black){
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
updateResColor(mBlack);
mBatteryController.setBlack(mBlack);
}
}, 200);
}
//zhangle add end
.............
}
}
/**
* @author zhangle
* @param black
*/
public void updateResColor(boolean black){
LogD.d(TAG,DEBUG,"updateResColor black=" +black);
int color = black ? mStatusBarBlackColor:Color.WHITE;
battery_percent.setTextColor(color);
mClock.setTextColor(color);
updateViewGroupColor(mSignalCluster, color);//wifi,sim
updateViewGroupColor(mNotificationIcons, color);//notifications icon
updateViewGroupColor(mStatusIcons, color);//zenMode,BT and other system status icon
}
public void updateViewGroupColor(ViewGroup vg,int color){
if(vg == null) return;
int counts = vg.getChildCount();
for (int i = 0; i < counts; i++) {
View child = vg.getChildAt(i);
if(child instanceof ImageView){
LogD.d(TAG,DEBUG,"updateViewGroupColor ImageView" );
((ImageView)child).setColorFilter(color);
}else if(child instanceof ViewGroup){
LogD.d(TAG,DEBUG,"updateViewGroupColor ViewGroup");
updateViewGroupColor((ViewGroup)child, color);
}else if(child instanceof TextView){
LogD.d(TAG,DEBUG,"updateViewGroupColor TextView");
((TextView)child).setTextColor(color);
}
}
}
private class MyTicker extends Ticker {
boolean black = false;//zhangle add
MyTicker(Context context, View sb) {
super(context, sb);
if (!mTickerEnabled) {
Log.w(TAG, "MyTicker instantiated with mTickerEnabled=false", new Throwable());
}
}
@Override
public void tickerStarting() {
if (!mTickerEnabled) return;
mTicking = true;
mStatusBarContents.setVisibility(View.GONE);
mTickerView.setVisibility(View.VISIBLE);
//zhangle add ,fix bug:notification ticker(left hand) icon not show clearly
if(black != mBlack){
updateViewGroupColor((ViewGroup)mTickerView, mBlack ? mStatusBarBlackColor:Color.WHITE);
black = mBlack;
}
mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
}
.......
}
updateViewGroupColor(mNotificationIcons, mBlack ? mStatusBarBlackColor:Color.WHITE);//notifications icon
public WindowInsets onApplyWindowInsets(WindowInsets insets) { //zhangle add start Log.d("zhangle","onApplyWindowInsets mStatusBarColor=" + mStatusBarColor ); /** * 进入非桌面应用时使用IPC:AIDL 来通知状态栏变色.桌面应用比较特殊,需要根据壁纸上方的颜色来适配 */ if(mLastStatusBarColor != mStatusBarColor){ //sysUiVisibility &= 0x00000010; //getDecorView().setSystemUiVisibility(getSystemUiVisibility() & 0x00000010); mHandler.post(new Runnable() { @Override public void run() { try { IStatusBarService statusbar = getStatusBarService(); if (statusbar != null) { statusbar.updateStatusBarColor(mStatusBarColor == -1); } } catch (RemoteException e) { // re-acquire status bar service next time it is needed. mStatusBarService = null; } } }); } mLastStatusBarColor = mStatusBarColor; //zhangle add end mFrameOffsets.set(insets.getSystemWindowInsets()); insets = updateColorViews(insets, true /* animate */); insets = updateStatusGuard(insets); updateNavigationGuard(insets); if (getForeground() != null) { drawableChanged(); } return insets; }
@Override
//zhangle add start
final Object mServiceAquireLock = new Object();
private IStatusBarService mStatusBarService;
private Handler mHandler = new Handler();
private static int mLastStatusBarColor ;
IStatusBarService getStatusBarService() {
synchronized (mServiceAquireLock) {
if (mStatusBarService == null) {
mStatusBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService("statusbar"));
}
return mStatusBarService;
}
}
//zhangle add end
void updateStatusBarColor(boolean black);//zhangle add
//zhangle add
public void updateStatusBarColor(final boolean black){
mHandler.post(new Runnable() {
public void run() {
if (mBar != null) {
try {
mBar.updateStatusBarColor(black);
} catch (RemoteException ex) {
}
}
}
});
}
void updateStatusBarColor(boolean black);//zhangle add
/**
* zhangle add
* @param black
*/
public void updateStatusBarColor(boolean black) {
mCallbacks.updateStatusBarColor(black);
}
public interface Callbacks{
....
public void updateStatusBarColor(boolean black) ;//zhangle add
....
}
public void updateStatusBarColor(boolean black) {
if(mBlack != black){
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
updateResColor(mBlack);
mBatteryController.setBlack(mBlack);
}
}, 200);
}
mBlack = black;
}
public void start(){
....
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
....
mCommandQueue = new CommandQueue(this, iconList);
int[] switches = new int[8];
ArrayList<IBinder> binders = new ArrayList<IBinder>();
try {
mBarService.registerStatusBar(mCommandQueue, iconList, switches, binders);
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}
..
}
public void setVisibility(int visibility) {
super.setVisibility(visibility);
Xlog.v("PhoneWindow", "DecorView setVisiblity: visibility = " + visibility
+ " ,Parent =" + getParent() + ", this =" + this);
//TODO
//zhangle add start
if(null != getParent() && visibility == View.VISIBLE){
Log.d(TAG,"setVisibility mStatusBarColor=" + mStatusBarColor );
/**
* 进入非桌面应用时使用IPC:AIDL 来通知状态栏变色.桌面应用比较特殊,需要根据壁纸上方的颜色来适配
*/
//sysUiVisibility &= 0x00000010;
//getDecorView().setSystemUiVisibility(getSystemUiVisibility() & 0x00000010);
mHandler.post(new Runnable() {
@Override
public void run() {
try {
IStatusBarService statusbar = getStatusBarService();
if (statusbar != null) {
statusbar.updateStatusBarColor(mStatusBarColor == -1);
}
} catch (RemoteException e) {
// re-acquire status bar service next time it is needed.
mStatusBarService = null;
}
}
});
//zhangle add end
}
}
4. Notification的添加和显示
在应用里面想发送一个通知Notification需要调用NotificationManager的notify()方法来实现. NotificationManager将主要的工作都交给了NotificationManagerService.framework层的NotificationManagerService和SystemUI之间的联系是通过NotificationListenerService和NotificationListeners来实现的.在SystemUI这个应用里面实现监听通知的增加和更新是通过NotificationListenerService调用registerAsSystemService方法将SystemUI创建的这个匿名内部类(NotificationListenerService)和framework层的Notification Manager 服务绑定到一起,framework在更新通知的时候会回调SystemUI中匿名内部类相关的方法,最终到达状态栏通知视图的变更.相关的时序图如下.
// Set up the initial notification state.
try {
mNotificationListener.registerAsSystemService(mContext,
new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
UserHandle.USER_ALL);
} catch (RemoteException e) {
Log.e(TAG, "Unable to register notification listener", e);
}
private final NotificationListenerService mNotificationListener =
new NotificationListenerService() {
@Override
public void onListenerConnected() {
if (DEBUG) Log.d(TAG, "onListenerConnected");
final StatusBarNotification[] notifications = getActiveNotifications();
final RankingMap currentRanking = getCurrentRanking();
mHandler.post(new Runnable() {
@Override
public void run() {
for (StatusBarNotification sbn : notifications) {
addNotification(sbn, currentRanking);
}
}
});
}
@Override
public void onNotificationPosted(final StatusBarNotification sbn,
final RankingMap rankingMap) {
if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
mHandler.post(new Runnable() {
@Override
public void run() {
Notification n = sbn.getNotification();
boolean isUpdate = mNotificationData.get(sbn.getKey()) != null
|| isHeadsUp(sbn.getKey());
// Ignore children of notifications that have a summary, since we're not
// going to show them anyway. This is true also when the summary is canceled,
// because children are automatically canceled by NoMan in that case.
if (n.isGroupChild() &&
mNotificationData.isGroupWithSummary(sbn.getGroupKey())) {//这里是处理Notification设置了GroupSummary属性的情况,因为这种情况下只显示一个,其他child 都不显示.
if (DEBUG) {
Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
}
// Remove existing notification to avoid stale data.
if (isUpdate) {
removeNotification(sbn.getKey(), rankingMap);
} else {
mNotificationData.updateRanking(rankingMap);
}
return;
}
if (isUpdate) {
updateNotification(sbn, rankingMap);
} else {
addNotification(sbn, rankingMap);
}
}
});
}
@Override
public void onNotificationRemoved(final StatusBarNotification sbn,
final RankingMap rankingMap) {
if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
mHandler.post(new Runnable() {
@Override
public void run() {
removeNotification(sbn.getKey(), rankingMap);
}
});
}
@Override
public void onNotificationRankingUpdate(final RankingMap rankingMap) {
if (DEBUG) Log.d(TAG, "onRankingUpdate");
mHandler.post(new Runnable() {
@Override
public void run() {
updateNotificationRanking(rankingMap);
}
});
}
};
private void updateNotificationShade() {
if (mStackScroller == null) return;
- //.............
ArrayList<View> toRemove = new ArrayList<View>();
for (int i=0; i< mStackScroller.getChildCount(); i++) {
View child = mStackScroller.getChildAt(i);
if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
toRemove.add(child);
}
}
for (View remove : toRemove) {
mStackScroller.removeView(remove);
}
for (int i=0; i<toShow.size(); i++) {
View v = toShow.get(i);
if (v.getParent() == null) {
mStackScroller.addView(v);
}
}
// So after all this work notifications still aren't sorted correctly.
// Let's do that now by advancing through toShow and mStackScroller in
// lock-step, making sure mStackScroller matches what we see in toShow.
int j = 0;
for (int i = 0; i < mStackScroller.getChildCount(); i++) {
View child = mStackScroller.getChildAt(i);
if (!(child instanceof ExpandableNotificationRow)) {
// We don't care about non-notification views.
continue;
}
if (child == toShow.get(j)) {
// Everything is well, advance both lists.
j++;
continue;
}
// Oops, wrong notification at this position. Put the right one
// here and advance both lists.
mStackScroller.changeViewPosition(toShow.get(j), i);
j++;
}
//.........
}
public class NotificationGroupSummaryActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_notification);
showNotifications();
}
private void showNotifications() {
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
showNotification1(notificationManager);
showNotification2(notificationManager);
showGroupSummaryNotification(notificationManager);
}
private void showNotification1(NotificationManager notificationManager) {
showSingleNotification(notificationManager, "title 1", "message 1", 1);
}
private void showNotification2(NotificationManager notificationManager) {
showSingleNotification(notificationManager, "title 2", "message 2", 2);
}
protected void showSingleNotification(NotificationManager notificationManager,
String title,
String message,
int notificationId) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentTitle(title)
.setContentText(message)
.setSmallIcon(R.drawable.ic_launcher)
.setGroupSummary(false)
.setGroup("group");
Notification notification = builder.build();
notificationManager.notify(notificationId, notification);
}
private void showGroupSummaryNotification(NotificationManager notificationManager) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentTitle("Dummy content title")
.setContentText("Dummy content text")
.setStyle(new NotificationCompat.InboxStyle()
.addLine("Line 1")
.addLine("Line 2")
.setSummaryText("Inbox summary text")
.setBigContentTitle("Big content title"))
.setNumber(2)
.setSmallIcon(R.drawable.ic_launcher)
.setCategory(Notification.CATEGORY_EVENT)
.setGroupSummary(true)
.setGroup("group");
Notification notification = builder.build();
notificationManager.notify(123456, notification);
}
}
4.1Notification滑动清除流程
Line 4679: D/NotificationStackScrollLayout( 7677): onInterceptTouchEvent action=0
Line 4757: D/NotificationStackScrollLayout( 7677): onInterceptTouchEvent action=2
Line 4799: D/NotificationStackScrollLayout( 7677): onInterceptTouchEvent action=2
Line 4811: D/NotificationStackScrollLayout( 7677): onTouchEvent action=2
Line 4841: D/NotificationStackScrollLayout( 7677): onTouchEvent action=2
Line 4881: D/NotificationStackScrollLayout( 7677): onTouchEvent action=2
Line 4909: D/NotificationStackScrollLayout( 7677): onTouchEvent action=2
Line 4945: D/NotificationStackScrollLayout( 7677): onTouchEvent action=2
Line 4953: D/NotificationStackScrollLayout( 7677): onTouchEvent action=1
public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_max_height);
mExpandHelper = new ExpandHelper(getContext(), this,
minHeight, maxHeight);
mExpandHelper.setEventSource(this);
mExpandHelper.setScrollAdapter(this);
mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, getContext());//创建
SwipeHelper,并指定方向为X方向,设置回调对象为本身mSwipeHelper.setLongPressListener(mLongPressListener);
initView(context);
}
public boolean onTouchEvent(MotionEvent ev) {
if (mLongPressSent) {
return true;
}
Log.d(TAG, "onTouchEvent action=" +ev.getAction());
if (!mDragging) {//判断是否在拖动,mDragging 会在action=move 的时候在onInterceptTouchEvent里面变成true
if (mCallback.getChildAtPosition(ev) != null) {
// We are dragging directly over a card, make sure that we also catch the gesture
// even if nobody else wants the touch event.
onInterceptTouchEvent(ev);
return true;
} else {
// We are not doing anything, make sure the long press callback
// is not still ticking like a bomb waiting to go off.
removeLongPressCallback();
return false;
}
}
mVelocityTracker.addMovement(ev);//添加事件,以便监听力度大小
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_OUTSIDE:
case MotionEvent.ACTION_MOVE://手指移动中
if (mCurrView != null) {//mCurrView就是我们点击的通知,具体获取是在onInterceptTouchEvent里面的
float delta = getPos(ev) - mInitialTouchPos;//和我们按下手指时相比的距离
float absDelta = Math.abs(delta);
if (absDelta >= getFalsingThreshold()) {
mTouchAboveFalsingThreshold = true;
}
// don't let items that can't be dismissed be dragged more than
// maxScrollDistance
if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissed(mCurrView)) {//如果当前通知的视图不能被清除
float size = getSize(mCurrAnimView);//通知的宽度
float maxScrollDistance = 0.15f * size;//最大可以滑动距离,因为这里的通知不能清除,所以将其能移动的范围缩小为0.15倍
if (absDelta >= size) {//如果移动的具体大于通知的宽度
delta = delta > 0 ? maxScrollDistance : -maxScrollDistance;
} else {
delta = maxScrollDistance * (float) Math.sin((delta/size)*(Math.PI/2));//修改实际将会移动的距离
}
}
setTranslation(mCurrAnimView, delta);//水平移动通知
updateSwipeProgressFromOffset(mCurrAnimView, mCanCurrViewBeDimissed);
}
break;
case MotionEvent.ACTION_UP://手指抬起
case MotionEvent.ACTION_CANCEL:
if (mCurrView != null) {
float maxVelocity = MAX_DISMISS_VELOCITY * mDensityScale;
mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, maxVelocity);
float escapeVelocity = SWIPE_ESCAPE_VELOCITY * mDensityScale;
float velocity = getVelocity(mVelocityTracker);
float perpendicularVelocity = getPerpendicularVelocity(mVelocityTracker);
// Decide whether to dismiss the current view
boolean childSwipedFarEnough = DISMISS_IF_SWIPED_FAR_ENOUGH &&
Math.abs(getTranslation(mCurrAnimView)) > 0.4 * getSize(mCurrAnimView);
boolean childSwipedFastEnough = (Math.abs(velocity) > escapeVelocity) &&
(Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
(velocity > 0) == (getTranslation(mCurrAnimView) > 0);
boolean falsingDetected = mCallback.isAntiFalsingNeeded()
&& !mTouchAboveFalsingThreshold;
boolean dismissChild = mCallback.canChildBeDismissed(mCurrView)
&& !falsingDetected && (childSwipedFastEnough || childSwipedFarEnough)
&& ev.getActionMasked() == MotionEvent.ACTION_UP;
if (dismissChild) {
// flingadingy
dismissChild(mCurrView, childSwipedFastEnough ? velocity : 0f);//以动画清除通知,根据力度大小来定义动画效果
} else {
// snappity
mCallback.onDragCancelled(mCurrView); //不能清除动画,就取消
snapChild(mCurrView, velocity);//动画的形式将通知弹回
}
}
break;
}
return true;
}
public boolean canChildBeDismissed(View v) {
final View veto = v.findViewById(R.id.veto);
return (veto != null && veto.getVisibility() != View.GONE);
}
private final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
new OnChildLocationsChangedListener() {
@Override
public void onChildLocationsChanged(
NotificationStackScrollLayout stackScrollLayout) {
if (mHandler.hasCallbacks(mVisibilityReporter)) {
// Visibilities will be reported when the existing
// callback is executed.
return;
}
// Calculate when we're allowed to run the visibility
// reporter. Note that this timestamp might already have
// passed. That's OK, the callback will just be executed
// ASAP.
long nextReportUptimeMs =
mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS;
mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs);
Log.d(TAG,"onChildLocationsChanged");
}
};
4.2 一键清除通知
mDismissView = (DismissView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_notification_dismiss_all, mStackScroller, false);
mDismissView.setOnButtonClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
clearAllNotifications();
}
});
private void clearAllNotifications() {
// animate-swipe all dismissable notifications, then animate the shade closed
int numChildren = mStackScroller.getChildCount();
final ArrayList<View> viewsToHide = new ArrayList<View>(numChildren);
for (int i = 0; i < numChildren; i++) {
final View child = mStackScroller.getChildAt(i);
if (mStackScroller.canChildBeDismissed(child)) {
if (child.getVisibility() == View.VISIBLE) {
viewsToHide.add(child);
}
}
}
if (viewsToHide.isEmpty()) {
animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
return;
}
addPostCollapseAction(new Runnable() {
@Override
public void run() {
try {
mBarService.onClearAllNotifications(mCurrentUserId);
} catch (Exception ex) { }
}
});
performDismissAllAnimations(viewsToHide);
}
4.3 Notification 的排序
public void filterAndSort() {
mSortedAndFiltered.clear();
mGroupsWithSummaries.clear();
final int N = mEntries.size();
for (int i = 0; i < N; i++) {
Entry entry = mEntries.valueAt(i);
StatusBarNotification sbn = entry.notification;
if (shouldFilterOut(sbn)) {
continue;
}
if (sbn.getNotification().isGroupSummary()) {
mGroupsWithSummaries.add(sbn.getGroupKey());
}
mSortedAndFiltered.add(entry);
}
// Second pass: Filter out group children with summary.
if (!mGroupsWithSummaries.isEmpty()) {
final int M = mSortedAndFiltered.size();
for (int i = M - 1; i >= 0; i--) {
Entry ent = mSortedAndFiltered.get(i);
StatusBarNotification sbn = ent.notification;
if (sbn.getNotification().isGroupChild() &&
mGroupsWithSummaries.contains(sbn.getGroupKey())) {
mSortedAndFiltered.remove(i);
}
}
}
Collections.sort(mSortedAndFiltered, mRankingComparator);
}
private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() {
private final Ranking mRankingA = new Ranking();
private final Ranking mRankingB = new Ranking();
@Override
public int compare(Entry a, Entry b) {
// Upsort current media notification.
String mediaNotification = mEnvironment.getCurrentMediaNotificationKey();
boolean aMedia = a.key.equals(mediaNotification);
boolean bMedia = b.key.equals(mediaNotification);
//Doov zhangle add start:calendar can be higher than music apps
Log.d(TAG,"compare mediaNotification=" +mediaNotification + " a.key=" + a.key + " b.key=" + b.key);
if(aMedia || bMedia){
if(a.notification.getPackageName().equals("com.android.calendar") && a.notification.getNotification().priority >= Notification.PRIORITY_MAX ){
return -1;//b is media
}else if(b.notification.getPackageName().equals("com.android.calendar") && b.notification.getNotification().priority >= Notification.PRIORITY_MAX ){
return 1;//a is media
}
}//Doov zhangle add end:
if (aMedia != bMedia) {
return aMedia ? -1 : 1;
}
final StatusBarNotification na = a.notification;
final StatusBarNotification nb = b.notification;
// Upsort PRIORITY_MAX system notifications
boolean aSystemMax = na.getNotification().priority >= Notification.PRIORITY_MAX &&
isSystemNotification(na);
boolean bSystemMax = nb.getNotification().priority >= Notification.PRIORITY_MAX &&
isSystemNotification(nb);
if (aSystemMax != bSystemMax) {
return aSystemMax ? -1 : 1;
}
// RankingMap as received from NoMan.
if (mRankingMap != null) {
mRankingMap.getRanking(a.key, mRankingA);
mRankingMap.getRanking(b.key, mRankingB);
return mRankingA.getRank() - mRankingB.getRank();
}
int d = nb.getScore() - na.getScore();
if (a.interruption != b.interruption) {
return a.interruption ? -1 : 1;
} else if (d != 0) {
return d;
} else {
return (int) (nb.getNotification().when - na.getNotification().when);
}
}
};
4.4Heads Up Notification
notification=new NotificationCompat.Builder(MainActivity.this)
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setSmallIcon(R.drawable.ic_launcher)
.setFullScreenIntent(pendingIntent, false)
.setContentTitle("这是标题")
.setContentText("这是内容")
.addAction(R.drawable.ic_launcher, "菜单1", peddingIntent1)
.build();
notificationManager.notify(1, notification);
- setUsesChronometer (boolean ) 设置这个为 true 以后 heads-up会将 action 也显示出来.
- Android L 使用如下方法就可以了
Notification.Builder builder = new Notification.Builder(this);
builder.setVisibility(Notification.VISIBILITY_PUBLIC)
.setSmallIcon(R.drawable.ic_launcher)
.setFullScreenIntent(contentIntent, false)
.setPriority(Notification.PRIORITY_MAX)
.setContentTitle("这是标题")
.setContentText("这是内容")
.setDeleteIntent(contentIntent)
.addAction(R.drawable.ic_launcher, "菜单1", contentIntent)
.build();
nm.notify(1, builder.getNotification());
@Override
public void addNotification(StatusBarNotification notification, RankingMap ranking) {
if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey());
if (mUseHeadsUp && shouldInterrupt(notification)) {
if (DEBUG) Log.d(TAG, "launching notification in heads up mode");
Entry interruptionCandidate = new Entry(notification, null);
ViewGroup holder = mHeadsUpNotificationView.getHolder();
if (inflateViewsForHeadsUp(interruptionCandidate, holder)) {
// 1. Populate mHeadsUpNotificationView
mHeadsUpNotificationView.showNotification(interruptionCandidate);
// do not show the notification in the shade, yet.
return;
}
}
- //.....
}
public boolean showNotification(NotificationData.Entry headsUp) {
if (mHeadsUp != null && headsUp != null && !mHeadsUp.key.equals(headsUp.key)) {
// bump any previous heads up back to the shade
release();
}
mHeadsUp = headsUp;
if (mContentHolder != null) {
mContentHolder.removeAllViews();
}
if (mHeadsUp != null) {
mMostRecentPackageName = mHeadsUp.notification.getPackageName();
mHeadsUp.row.setSystemExpanded(true);
mHeadsUp.row.setSensitive(false);
mHeadsUp.row.setHeadsUp(true);
mHeadsUp.row.setHideSensitive(
false, false /* animated */, 0 /* delay */, 0 /* duration */);
if (mContentHolder == null) {
// too soon!
return false;
}
mContentHolder.setX(0);
mContentHolder.setVisibility(View.VISIBLE);
mContentHolder.setAlpha(mMaxAlpha);
mContentHolder.addView(mHeadsUp.row);
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
mSwipeHelper.snapChild(mContentHolder, 1f);
mStartTouchTime = SystemClock.elapsedRealtime() + mTouchSensitivityDelay;
mHeadsUp.setInterruption();
- // 2. Animate mHeadsUpNotificationView in
mBar.scheduleHeadsUpOpen();
// 3. Set alarm to age the notification off
mBar.resetHeadsUpDecayTimer();
}
return true;
}
4.5 长按通知动画效果
/*mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById(
R.id.notification_stack_scroller);*///zhangle delete
mStackScroller = mNotificationPanel.getmNotificationStackScroller();//zhangle add
mStackScroller.setLongPressListener(getNotificationLongClicker());
mStackScroller.setPhoneStatusBar(this);
mKeyguardIconOverflowContainer =
(NotificationOverflowContainer) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
mKeyguardIconOverflowContainer.setOnActivatedListener(this);
mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
mStackScroller.addView(mKeyguardIconOverflowContainer);
SpeedBumpView speedBump = (SpeedBumpView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_notification_speed_bump, mStackScroller, false);
mStackScroller.setSpeedBumpView(speedBump);
mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_no_notifications, mStackScroller, false);
mStackScroller.setEmptyShadeView(mEmptyShadeView);
mDismissView = (DismissView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_notification_dismiss_all, mStackScroller, false);
mDismissView.setOnButtonClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
clearAllNotifications();
}
});
mStackScroller.setDismissView(mDismissView);
mExpandedContents = mStackScroller;
4.6 通知点击打开
5.近期任务Recents
在解锁的情况下长按Home键就可以直接调出近期使用过的应用,要清除一个应用退出,只要左滑或者右滑就可以.至于一键清除功能,google 默认是没有的,需要各家厂商自己去添加.
近期任务栏主要的几个类如下,需要注意的是AndroidL和AndroidKK在这一部分的代码路径是完全不同的.L保存了KK的代码目录,但是没有去使用,而是在com.android.systemui.recents里面重新添加了相关的代码.
com.android.systemui.recents下面相关的核心类
RecentsActivity.java
TaskStackView.java
RecentsView.java
TaskStackViewTouchHandler.java
SwipeHelper.java
还有一个主要的类是
frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
5.1视图的打开
启动的时序图如下:
长按Home 键启动近期任务,肯定是有地方来判断长按这个动作的.具体代码是在PhoneWindowManager.java 里面
在interceptKeyBeforeDispatching 里面通过KeyEvent 来判断是长按事件之后就会调用handleLongPressOnHome() 方法,然后转调toggleRecentApps() 方法.
private void handleLongPressOnHome() {
Log.d(TAG,"handleLongPressOnHome mLongPressOnHomeBehavior=" + mLongPressOnHomeBehavior);
if (mLongPressOnHomeBehavior != LONG_PRESS_HOME_NOTHING) {
mHomeConsumed = true;
performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_SYSTEM_UI) {
toggleRecentApps();
} else if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_ASSIST) {
launchAssistAction();
}
}
}
private void toggleRecentApps() {
Log.d(TAG,"toggleRecentApps");
mPreloadedRecentApps = false; // preloading no longer needs to be canceled
if(getIsSuperSaveMode()) return;//zhangle add
try {
IStatusBarService statusbar = getStatusBarService();
if (statusbar != null) {
statusbar.toggleRecentApps();
}
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException when toggling recent apps", e);
// re-acquire status bar service next time it is needed.
mStatusBarService = null;
}
}
其中mLongPressOnHomeBehavior 等于1 的情况下长按home 键就是打开近期任务等于2的情况就是打开搜索页面.
这个值是从com.android.internal.R.integer.config_longPressOnHomeBehavior里面读取的.
其中toggleRecentApps() 会调用StatusBarManagerService 的toggleRecentApps的方法,然后转调CommandQueue 的toggleRecentApps方法. CommandQueue通过回调BaseStatusBar 的toggleRecentApps方法,然后调用Recents的toggleRecents方法,到这里才会启动RecentsActivity.java.
上面提到的第7步实际会发现是4.4效果的近期任务视图.并不是卡片式的.5.0 默认使用的卡片式的任务管理.
4.4 的时序图步骤:1-7
5.0 的时序步骤:1-6 然后 8-11
5.2 单个任务的滑动清除和点击进入
在近期任务页面,左右滑动就可以进行清除单个应用.点击一下就可以重新进入到所选择的应用.
在RecentsActivity的onStart()方法里面调用自己的updateRecentsTasks方法,通过调用创建RecentsTaskLoader 实例来判断当前有未清除的任务.如果有的话,就将获取到的TaskStack 数组用来生成RecentsView视图,其实我们看到的近期任务真个视图几乎都是RecentsView.这个RecentsView视图是有多个TaskStackView组成的.而一个应用的视图就是一个TaskStackView.
在生成RecentsView 的视图的时候, RecentsView 会先清除之前保存的TaskStackView,再根据新传入的TaskStack 来生成TaskStackView来组建RecentsView视图.
每一个TaskStackView 在创建的时候,都会实例化一个TaskStackViewTouchHandler和TaskStackViewScroller对象.其中触摸相关的事件主要是由TaskStackViewTouchHandler 来处理的. TaskStackViewTouchHandler 在处理相关事件的时候将事件交给了SwipeHelper来处理.
SwipeHelper.java
public boolean onTouchEvent(MotionEvent ev) {
if (!mDragging) {
if (!onInterceptTouchEvent(ev)) {
return mCanCurrViewBeDimissed;
}
}
mVelocityTracker.addMovement(ev);
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_OUTSIDE:
case MotionEvent.ACTION_MOVE:
if (mCurrView != null) {
float delta = getPos(ev) - mInitialTouchPos;
setSwipeAmount(delta);
mCallback.onSwipeChanged(mCurrView, delta);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mCurrView != null) {
endSwipe(mVelocityTracker);
}
break;
}
return true;
}
SwipeHelper通过VelocityTracker来跟踪触屏事件,在检测到是手指抬起的的时候会调用endSwipe根据滑动的力度和距离及方向来决定是清除选择应用还是取消当前的清除动作.
private void endSwipe(VelocityTracker velocityTracker) {
velocityTracker.computeCurrentVelocity(1000 /* px/sec */);
float velocity = getVelocity(velocityTracker);
float perpendicularVelocity = getPerpendicularVelocity(velocityTracker);
float escapeVelocity = SWIPE_ESCAPE_VELOCITY * mDensityScale;
float translation = getTranslation(mCurrView);
// Decide whether to dismiss the current view
boolean childSwipedFarEnough = DISMISS_IF_SWIPED_FAR_ENOUGH &&
Math.abs(translation) > 0.6 * getSize(mCurrView);
boolean childSwipedFastEnough = (Math.abs(velocity) > escapeVelocity) &&
(Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
(velocity > 0) == (translation > 0);
boolean dismissChild = mCallback.canChildBeDismissed(mCurrView)
&& isValidSwipeDirection(translation)
&& (childSwipedFastEnough || childSwipedFarEnough);
if (dismissChild) {
// flingadingy
dismissChild(mCurrView, childSwipedFastEnough ? velocity : 0f);
} else {
// snappity
mCallback.onDragCancelled(mCurrView);
snapChild(mCurrView, velocity);
}
}
上面代码中dismissChild()就是清除动作和效果动画效果的代码.最终SwipeHelper通过回调TaskStackViewTouchHandler的onChildDismissed,和TaskStackView 的onTaskViewDismissed来清除任务的.
以上分析了滑动清除的动作下面就来看看点击进入应用的动作相关的流程.看起来有点复杂,其实主要是利用内部接口类来进行回调.
由于Android L在手机重启之后还是会保留之前启动为手动清除的应用,所以在下面第10步的时候,是有种情况要判断的.
5.3 近期任务的一键清除功能
public void removeAllTaskStacksAndNotify() {
if(myHandler == null){
myHandler = new MyHandler();
}
if(mWindowManager == null){
mWindowManager = (WindowManager) getContext()
.getSystemService(Context.WINDOW_SERVICE);
}
int childCount = getChildCount();
Log.d(TAG,"removeAllTaskStacksAndNotify childCount=" + childCount);
if(childCount>0){
mCb.onAnimationStart();
}
RemoveTask mRemoveTask = new RemoveTask();
mRemoveTask.execute();
long startTime = System.currentTimeMillis();
TaskStackView taskStackView = null;
for (int i = childCount - 1; i >= 0; i--) {
View child = getChildAt(i);
if (null != child && child != mSearchBar && child instanceof TaskStackView ) {
taskStackView = (TaskStackView)child;
int tasks = taskStackView.getChildCount();
Log.d(TAG,"removeAllTaskStacksAndNotify tasks=" + tasks);
//将显示出来的近期任务视图依次右滑,从下到上
/*for (int j = tasks-1; j >= 0; j--) {
propertyValuesHolder(taskStackView.getChildAt(j),tasks-j,j==0);
}*/
//将显示出来的近期任务视图依次右滑,从上到下
for (int j = 0; j < tasks; j++) {
propertyValuesHolder(taskStackView.getChildAt(j),j,j==tasks-1);
}
}
}
long endTime = System.currentTimeMillis();
Log.d(TAG,"removeAllTaskStacksAndNotify time=" + (endTime-startTime));
if(childCount <= 0){
myHandler.sendMessageDelayed(myHandler.obtainMessage(myHandler.ANIMATIONEND), 0);
}
}
class MyHandler extends Handler{
final static int ANIMATIONEND = 10;
final static int TASKCLEANED = 11;
boolean isAnimationEnd = false;
boolean isTaskCleaned = false;
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case ANIMATIONEND:
isAnimationEnd = true;
break;
case TASKCLEANED:
isTaskCleaned = true;
break;
default:
break;
}
if(isAnimationEnd && isTaskCleaned){
mCb.onAllViewClean();
isTaskCleaned = isAnimationEnd = false;
}
}
}
private final int DURATION_TIME = 150;
private WindowManager mWindowManager = null;
public void propertyValuesHolder(View view,final int i,final boolean last){
float h = view.getHeight();
float w = view.getWidth();
float x = view.getX();
float y = view.getY();
float width_screen = mWindowManager.getDefaultDisplay().getWidth();
boolean ori = isScreenLand();
Log.d("zhangle","propertyValuesHolder x=" + x + " y=" +y + " w=" + w + " h=" +h + " width_screen=" +width_screen + " ori=" + ori);
/*PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1f, 0f, 1f);
PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1f, 0.5f, 1f);
PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1f,0.5f, 1f);
PropertyValuesHolder y_tra = PropertyValuesHolder.ofFloat("y", y,-1500f);*/
PropertyValuesHolder x_tra = PropertyValuesHolder.ofFloat("x",width_screen);
ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(view,x_tra).setDuration(DURATION_TIME*(ori?2:1));
oa.setInterpolator(new LinearInterpolator());
oa.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationRepeat(Animator animation) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationEnd(Animator animation) {
if(last){
myHandler.sendMessageDelayed(myHandler.obtainMessage(myHandler.ANIMATIONEND), 0);
}
}
@Override
public void onAnimationCancel(Animator animation) {
// TODO Auto-generated method stub
}
} );
oa.setStartDelay((i+1)*70);
oa.start();
}
public boolean isScreenLand() {
Configuration mConfiguration = this.getResources().getConfiguration();
int ori = mConfiguration.orientation ; //获取屏幕方向
if(ori == mConfiguration.ORIENTATION_LANDSCAPE){//横屏
return true;
}else if(ori == mConfiguration.ORIENTATION_PORTRAIT){//竖屏
return false;
}
return false;
}
private void removeRecentsTask() {
if(null != mStacks){
try {
int mNum = mStacks.size();
Log.d("zhangle", "RecentsView removeAllTaskStacksAndNotify mNum=" + mNum);
for (int i = 0; i < mNum; i++) {
TaskStack taskStack = mStacks.get(i);
ArrayList<Task> list = taskStack.getTasks();
Iterator<Task> iterator = list.iterator();//fix bug java.util.ConcurrentModificationException
/* for (Task task : list) {*/
while(iterator.hasNext()){
Task t = iterator.next();
Log.d("zhangle", "RecentsView onTaskViewDismissed t="+ t.key.toString());
// Remove any stored data from the loader. We currently don't bother notifying the views
// that the data has been unloaded because at the point we call onTaskViewDismissed(), the views
// either don't need to be updated, or have already been removed.
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
loader.deleteTaskData(t, false);
iterator.remove();
// Remove the old task from activity manager
RecentsTaskLoader.getInstance().getSystemServicesProxy().removeTask(t.key.id);
}
}
} catch (Exception e) {
Log.d("zhangle", "RecentsView removeAllTaskStacksAndNotify error" );
e.printStackTrace();
}
}
}
class RemoveTask extends AsyncTask{
long startTime, endTime;
@Override
protected Object doInBackground(Object... params) {
Log.d(TAG,"doInBackground");
removeRecentsTask();
return null;
}
@Override
protected void onPreExecute() {
Log.d(TAG,"onPreExecute");
startTime = System.currentTimeMillis();
super.onPreExecute();
}
@Override
protected void onPostExecute(Object result) {
endTime = System.currentTimeMillis();
Log.d(TAG,"RemoveTask time=" + (endTime-startTime));
super.onPostExecute(result);
myHandler.sendMessageDelayed(myHandler.obtainMessage(myHandler.TASKCLEANED), 0);
}
}
5.4 打开近期任务列表时动画是怎么加的?
protected void onStart() {
super.onStart();
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
SystemServicesProxy ssp = loader.getSystemServicesProxy();
AlternateRecentsComponent.notifyVisibilityChanged(this, ssp, true);
// Register the broadcast receiver to handle messages from our service
IntentFilter filter = new IntentFilter();
filter.addAction(AlternateRecentsComponent.ACTION_HIDE_RECENTS_ACTIVITY);
filter.addAction(AlternateRecentsComponent.ACTION_TOGGLE_RECENTS_ACTIVITY);
filter.addAction(AlternateRecentsComponent.ACTION_START_ENTER_ANIMATION);
registerReceiver(mServiceBroadcastReceiver, filter);
// Register any broadcast receivers for the task loader
loader.registerReceivers(this, mRecentsView);
// Update the recent tasks
updateRecentsTasks(getIntent());
// If this is a new instance from a configuration change, then we have to manually trigger
// the enter animation state
if (mConfig.launchedHasConfigurationChanged) {//这里是false
onEnterAnimationTriggered();
}
}
static void visibilityChanged(boolean visible) {
if (sRecentsComponentCallbacks != null) {
sRecentsComponentCallbacks.onVisibilityChanged(visible);
} else {
Xlog.w(TAG, "notifyVisibilityChanged : callback is null");
}
}
@Override
public void onVisibilityChanged(boolean visible) {
// Update the recents visibility flag
if (visible) {
mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
} else {
mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE;
}
notifyUiVisibilityChanged(mSystemUiVisibility);
}
private void notifyUiVisibilityChanged(int vis) {
try {
mWindowManagerService.statusBarVisibilityChanged(vis);
} catch (RemoteException ex) {
}
}
final BroadcastReceiver mServiceBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(AlternateRecentsComponent.ACTION_HIDE_RECENTS_ACTIVITY)) {
- .....
} else if (action.equals(AlternateRecentsComponent.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
// If we are toggling Recents, then first unfilter any filtered stacks first
dismissRecentsToFocusedTaskOrHome(true);
} else if (action.equals(AlternateRecentsComponent.ACTION_START_ENTER_ANIMATION)) {
// Trigger the enter animation
onEnterAnimationTriggered();
// Notify the fallback receiver that we have successfully got the broadcast
// See AlternateRecentsComponent.onAnimationStarted()
setResultCode(Activity.RESULT_OK);
}
}
};
public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
// We have to increment/decrement the post animation trigger in case there are no children
// to ensure that it runs
ctx.postAnimationTrigger.increment();
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child != mSearchBar) {
TaskStackView stackView = (TaskStackView) child;
stackView.startEnterRecentsAnimation(ctx);
}
}
ctx.postAnimationTrigger.decrement();
}
void startEnterRecentsAnimation(final ViewAnimation.TaskViewEnterContext ctx) {
final TaskViewTransform transform = ctx.currentTaskTransform;
int startDelay = 0;
if (mConfig.launchedFromAppWithThumbnail) {
- //....
} else if (mConfig.launchedFromHome) {
// Animate the tasks up
int frontIndex = (ctx.currentStackViewCount - ctx.currentStackViewIndex - 1);
int delay = mConfig.transitionEnterFromHomeDelay +
frontIndex * mConfig.taskViewEnterFromHomeStaggerDelay;
setScaleX(transform.scale);
setScaleY(transform.scale);
if (!mConfig.fakeShadows) {
animate().translationZ(transform.translationZ);
}
animate()
.translationY(transform.translationY)
.setStartDelay(delay)
.setUpdateListener(ctx.updateListener)
.setInterpolator(mConfig.quintOutInterpolator)
.setDuration(mConfig.taskViewEnterFromHomeDuration +
frontIndex * mConfig.taskViewEnterFromHomeStaggerDelay)
.withEndAction(new Runnable() {
@Override
public void run() {
// Decrement the post animation trigger
ctx.postAnimationTrigger.decrement();
}
})
.start();
ctx.postAnimationTrigger.increment();
startDelay = delay;
}
// Enable the focus animations from this point onwards so that they aren't affected by the
// window transitions
postDelayed(new Runnable() {
@Override
public void run() {
enableFocusAnimations();
}
}, startDelay);
}
5.5上下滑动效果的实现流程
case MotionEvent.ACTION_MOVE: {
if (mActivePointerId == INACTIVE_POINTER_ID) break;
mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
Log.d(TAG,"onTouchEvent_move mIsScrolling=" + mIsScrolling);
int activePointerIndex = ev.findPointerIndex(mActivePointerId);
int x = (int) ev.getX(activePointerIndex);
int y = (int) ev.getY(activePointerIndex);
int yTotal = Math.abs(y - mInitialMotionY);
float curP = mSv.mLayoutAlgorithm.screenYToCurveProgress(y);
float deltaP = mLastP - curP;
if (!mIsScrolling) {
if (yTotal > mScrollTouchSlop) {
mIsScrolling = true;
// Disallow parents from intercepting touch events
final ViewParent parent = mSv.getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
}
}
if (mIsScrolling) {
float curStackScroll = mScroller.getStackScroll();
float overScrollAmount = mScroller.getScrollAmountOutOfBounds(curStackScroll + deltaP);
if (Float.compare(overScrollAmount, 0f) != 0) {
// Bound the overscroll to a fixed amount, and inversely scale the y-movement
// relative to how close we are to the max overscroll
float maxOverScroll = mConfig.taskStackOverscrollPct;
deltaP *= (1f - (Math.min(maxOverScroll, overScrollAmount)
/ maxOverScroll));
}
Log.d(TAG,"onTouchEvent_move (curStackScroll + deltaP)=" + (curStackScroll + deltaP));
mScroller.setStackScroll(curStackScroll + deltaP);//TaskStackView ->invalidate
}
mLastMotionX = x;
mLastMotionY = y;
Log.d(TAG,"onTouchEvent_move mLastMotionY=" + mLastMotionY);
mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
mTotalPMotion += Math.abs(deltaP);
break;
}
@Override
public void computeScroll() {
Log.d(TAG,"computeScroll");
mStackScroller.computeScroll();
// Synchronize the views
synchronizeStackViewsWithModel();
clipTaskViews();
// Notify accessibility
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
}
6.锁屏页面通知的显示
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
// Update Clock Pivot
mKeyguardStatusView.setPivotX(getWidth() / 2);
/// M: We use "getHeight()" instead of "getTextSize()" since the ClockView is implemented by our own and does not have "getTextSize()".
/// Our own ClockView will display AM/PM by default.
mKeyguardStatusView.setPivotY((FONT_HEIGHT - CAP_HEIGHT) / 2048f * mClockView.getHeight());
// Calculate quick setting heights.
int oldMaxHeight = mQsMaxExpansionHeight;
mQsMinExpansionHeight = mKeyguardShowing ? 0 : mHeader.getCollapsedHeight() + mQsPeekHeight;
//mQsMaxExpansionHeight = mHeader.getExpandedHeight() + mQsContainer.getHeight(); //zhangle delete for status bar
mQsMaxExpansionHeight = mHeader.getCollapsedHeight() + mQsContainer.getHeight();//zhangle add for status bar
if(DEBUG){
Log.d(TAG,"onLayout mQsExpanded=" + mQsExpanded +
" mQsFullyExpanded=" +mQsFullyExpanded +
" oldMaxHeight=" + oldMaxHeight+
" mQsMaxExpansionHeight=" + mQsMaxExpansionHeight+
" getExpandedHeight=" + getExpandedHeight()+
" mQsMinExpansionHeight=" + mQsMinExpansionHeight);
}
positionClockAndNotifications();
if (mQsExpanded && mQsFullyExpanded) {
mQsExpansionHeight = mQsMaxExpansionHeight;
requestScrollerTopPaddingUpdate(false /* animate */);
requestPanelHeightUpdate();
// Size has changed, start an animation.
if (mQsMaxExpansionHeight != oldMaxHeight) {
startQsSizeChangeAnimation(oldMaxHeight, mQsMaxExpansionHeight);
}
} else if (!mQsExpanded) {
setQsExpansion(mQsMinExpansionHeight + mLastOverscroll);
}
mNotificationStackScroller.setStackHeight(getExpandedHeight());
updateHeader();
mNotificationStackScroller.updateIsSmallScreen(
mHeader.getCollapsedHeight() + mQsPeekHeight);
// If we are running a size change animation, the animation takes care of the height of
// the container. However, if we are not animating, we always need to make the QS container
// the desired height so when closing the QS detail, it stays smaller after the size change
// animation is finished but the detail view is still being animated away (this animation
// takes longer than the size change animation).
if (mQsSizeChangeAnimator == null) {
mQsContainer.setHeightOverride(mQsContainer.getDesiredHeight());
}
}
private void positionClockAndNotifications() {
boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending();
if(DEBUG){
Log.d(TAG , "positionClockAndNotifications ------ ");
}
int stackScrollerPadding;
if (mStatusBarState != StatusBarState.KEYGUARD) {
int bottom = mHeader.getCollapsedHeight();
stackScrollerPadding = mStatusBarState == StatusBarState.SHADE
? bottom + mQsPeekHeight + mNotificationTopPadding
: mKeyguardStatusBar.getHeight() + mNotificationTopPadding;
mTopPaddingAdjustment = 0;
} else {
mClockPositionAlgorithm.setup(
mStatusBar.getMaxKeyguardNotifications(),
getMaxPanelHeight(),
getExpandedHeight(),
mNotificationStackScroller.getNotGoneChildCount(),
getHeight(),
mKeyguardStatusView.getHeight(),
mEmptyDragAmount);
mClockPositionAlgorithm.run(mClockPositionResult);
if (animate || mClockAnimator != null) {
startClockAnimation(mClockPositionResult.clockY);
} else {
mKeyguardStatusView.setY(mClockPositionResult.clockY);
}
updateClock(mClockPositionResult.clockAlpha, mClockPositionResult.clockScale);
stackScrollerPadding = mClockPositionResult.stackScrollerPadding;
mTopPaddingAdjustment = mClockPositionResult.stackScrollerPaddingAdjustment;
}
mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
requestScrollerTopPaddingUpdate(animate);
}
mNotificationStackScroller.(stackScrollerPadding);
requestScrollerTopPaddingUpdate(animate);
public void updateTopPadding(float qsHeight, int scrollY, boolean animate,
boolean ignoreIntrinsicPadding) {
Log.d(TAG,"updateTopPadding qsHeight=" + qsHeight +
" scrollY=" + scrollY + " animate=" + animate +
" mNotificationTopPadding=" + mNotificationTopPadding +
" ignoreIntrinsicPadding=" + ignoreIntrinsicPadding);
float start = qsHeight - scrollY + mNotificationTopPadding;
float stackHeight = getHeight() - start;
int minStackHeight = getMinStackHeight();
if (stackHeight <= minStackHeight) {
float overflow = minStackHeight - stackHeight;
stackHeight = minStackHeight;
start = getHeight() - stackHeight;
mTopPaddingOverflow = overflow;
} else {
mTopPaddingOverflow = 0;
}
Log.d(TAG,"updateTopPadding start=" + start + " clampPadding((int) start=" +clampPadding((int) start) +
" mLastSetStackHeight=" + mLastSetStackHeight);
setTopPadding(ignoreIntrinsicPadding ? (int) start : clampPadding((int) start),
animate);
setStackHeight(mLastSetStackHeight);
}
7.下滑通知栏,清除通知的led 灯
8.锁屏的启动
public void systemReady() {
mKeyguardDelegate = new KeyguardServiceDelegate(mContext);
mKeyguardDelegate.onSystemReady();
.....
}
@Override
public void systemBooted() {
boolean bindKeyguardNow = false;
synchronized (mLock) {
// Time to bind Keyguard; take care to only bind it once, either here if ready or
// in systemReady if not.
if (mKeyguardDelegate != null) {
bindKeyguardNow = true;
} else {
// Because mKeyguardDelegate is null, we know that the synchronized block in
// systemReady didn't run yet and setting this will actually have an effect.
mDeferBindKeyguard = true;
}
}
if (bindKeyguardNow) {
mKeyguardDelegate.bindService(mContext);
mKeyguardDelegate.onBootCompleted();
}
synchronized (mLock) {
mSystemBooted = true;
}
startedWakingUp();
screenTurningOn(null);
}
@Override
public void onCreate() {
((SystemUIApplication) getApplication()).startServicesIfNeeded();
mKeyguardViewMediator =
((SystemUIApplication) getApplication()).getComponent(KeyguardViewMediator.class);
}
public void hide(boolean destroyView) {
cancelShowRunnable();
if (mKeyguardView != null) {
mKeyguardView.setOnDismissAction(null);
mKeyguardView.cleanUp();
}
if (destroyView) {
if (DEBUG) Log.d(TAG, "call removeView()") ;
removeView();
} else if (mRoot != null) {
if (DEBUG) Log.d(TAG, "just set keyguard Invisible.") ;
mRoot.setVisibility(View.INVISIBLE);
}
}
9.熄屏和亮屏
private void handleWakefulnessChange(final int wakefulness, boolean interactive,
final int reason) {
// Tell the activity manager about changes in wakefulness, not just interactivity.
// It needs more granularity than other components.
mHandler.post(new Runnable() {
@Override
public void run() {
mActivityManagerInternal.onWakefulnessChanged(wakefulness);
}
});
// Handle changes in the overall interactive state.
boolean interactiveChanged = false;
synchronized (mLock) {
// Broadcast interactive state changes.
if (interactive) {
// Waking up...
interactiveChanged = (mActualInteractiveState != INTERACTIVE_STATE_AWAKE);
if (interactiveChanged) {
mActualInteractiveState = INTERACTIVE_STATE_AWAKE;
mPendingWakeUpBroadcast = true;
mHandler.post(new Runnable() {
@Override
public void run() {
EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0);
if (DEBUG) {
Slog.d(TAG, "onInteractiveChangeStarted: mPolicy.wakingUp()");
}
mPolicy.wakingUp();//亮屏
}
});
updatePendingBroadcastLocked();
}
} else {
// Going to sleep...
// This is a good time to make transitions that we don't want the user to see,
// such as bringing the key guard to focus. There's no guarantee for this,
// however because the user could turn the device on again at any time.
// Some things may need to be protected by other mechanisms that defer screen on.
interactiveChanged = (mActualInteractiveState != INTERACTIVE_STATE_ASLEEP);
if (interactiveChanged) {
mActualInteractiveState = INTERACTIVE_STATE_ASLEEP;
mPendingGoToSleepBroadcast = true;
if (mUserActivityPending) {
mUserActivityPending = false;
mHandler.removeMessages(MSG_USER_ACTIVITY);
}
mHandler.post(new Runnable() {
@Override
public void run() {
int why = WindowManagerPolicy.OFF_BECAUSE_OF_USER;
switch (reason) {
case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
why = WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN;
break;
case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
why = WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT;
break;
case PowerManager.GO_TO_SLEEP_REASON_PROXIMITY:
why = WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR;
break;
}
EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, why, 0, 0);
if (DEBUG) {
Slog.d(TAG, "mPolicy.goingToSleep: " + why);
}
mPolicy.goingToSleep(why);//熄屏
}
});
updatePendingBroadcastLocked();
}
}
}
// Notify battery stats.
if (interactiveChanged) {
try {
mBatteryStats.noteInteractive(interactive);
} catch (RemoteException ex) { }
}
}
9.1熄屏
9.2.亮屏
final ShowListener mKeyguardDelegateCallback = new ShowListener() {
@Override
public void onShown(IBinder windowToken) {
if (DEBUG_WAKEUP) Slog.d(TAG, "mKeyguardDelegate.ShowListener.onShown.");
mHandler.sendEmptyMessage(MSG_KEYGUARD_DRAWN_COMPLETE);
}
};
10.上滑解锁
Line 1087: D/NotificationPanelView( 1139): onInterceptTouchEvent_npv mBlockTouches=false mIsExpanding=false action=0
Line 1131: D/PanelView0( 1139): onInterceptTouchEvent event=0 mInstantExpanding=false
Line 1141: D/NotificationStackScrollLayout( 1139): onInterceptTouchEvent action=0
Line 1145: D/com.android.systemui.SwipeHelper( 1139): onInterceptTouchEvent action=0
Line 1149: D/NotificationStackScrollLayout( 1139): onInterceptTouchEvent swipeWantsIt=false scrollWantsIt=false expandWantsIt=false
Line 1153: D/NotificationStackScrollLayout( 1139): onTouchEvent action=0
Line 1157: D/com.android.systemui.SwipeHelper( 1139): onTouchEvent action=0
Line 1161: D/NotificationStackScrollLayout( 1139): onTouchEvent horizontalSwipeWantsIt=false scrollerWantsIt=false expandWantsIt=false
Line 1169: D/NotificationPanelView( 1139): onTouchEvent_npv action=0 mBlockTouches=false
Line 1177: D/NotificationPanelView( 1139): onTouchEvent_npv mExpandedHeight=968.0 mQsTracking=true mQsExpandImmediate=false mIsExpanding=false mKeyguardShowing=true mConflictingQsExpansionGesture=false
Line 1255: D/PanelView0( 1139): onTouchEvent mTracking=false waitForTouchSlop=true action=0
Line 1273: D/NotificationPanelView( 1139): onTouchEvent_npv action=2 mBlockTouches=false
Line 1277: D/NotificationPanelView( 1139): onTouchEvent_npv mExpandedHeight=968.0 mQsTracking=true mQsExpandImmediate=false mIsExpanding=false mKeyguardShowing=true mConflictingQsExpansionGesture=false
Line 1297: D/PanelView0( 1139): onTouchEvent mTracking=false waitForTouchSlop=true action=2
Line 1321: D/NotificationPanelView( 1139): onTouchEvent_npv action=2 mBlockTouches=false
Line 1325: D/NotificationPanelView( 1139): onTouchEvent_npv mExpandedHeight=968.0 mQsTracking=true mQsExpandImmediate=false mIsExpanding=false mKeyguardShowing=true mConflictingQsExpansionGesture=false
Line 1347: D/PanelView0( 1139): onTouchEvent mTracking=false waitForTouchSlop=true action=2
Line 1369: D/NotificationPanelView( 1139): onTouchEvent_npv action=2 mBlockTouches=false
Line 1373: D/NotificationPanelView( 1139): onTouchEvent_npv mExpandedHeight=968.0 mQsTracking=true mQsExpandImmediate=false mIsExpanding=false mKeyguardShowing=true mConflictingQsExpansionGesture=false
Line 1393: D/PanelView0( 1139): onTouchEvent mTracking=false waitForTouchSlop=true action=2
Line 1399: D/PanelView0( 1139): onTouchEvent_move_newHeight=968.0
Line 1453: D/NotificationPanelView( 1139): onTouchEvent_npv action=2 mBlockTouches=false
Line 1457: D/NotificationPanelView( 1139): onTouchEvent_npv mExpandedHeight=968.0 mQsTracking=true mQsExpandImmediate=false mIsExpanding=true mKeyguardShowing=true mConflictingQsExpansionGesture=false
Line 1477: D/PanelView0( 1139): onTouchEvent mTracking=true waitForTouchSlop=true action=2
Line 1479: D/PanelView0( 1139): onTouchEvent_move_newHeight=925.3512
Line 1537: D/NotificationPanelView( 1139): onTouchEvent_npv action=2 mBlockTouches=false
Line 1541: D/NotificationPanelView( 1139): onTouchEvent_npv mExpandedHeight=925.3512 mQsTracking=true mQsExpandImmediate=false mIsExpanding=true mKeyguardShowing=true mConflictingQsExpansionGesture=false
Line 1561: D/PanelView0( 1139): onTouchEvent mTracking=true waitForTouchSlop=true action=2
Line 1563: D/PanelView0( 1139): onTouchEvent_move_newHeight=883.5237
Line 1611: D/NotificationPanelView( 1139): onTouchEvent_npv action=2 mBlockTouches=false
Line 1617: D/NotificationPanelView( 1139): onTouchEvent_npv mExpandedHeight=883.5237 mQsTracking=true mQsExpandImmediate=false mIsExpanding=true mKeyguardShowing=true mConflictingQsExpansionGesture=false
Line 1645: D/PanelView0( 1139): onTouchEvent mTracking=true waitForTouchSlop=true action=2
Line 1647: D/PanelView0( 1139): onTouchEvent_move_newHeight=827.3082
Line 1705: D/NotificationPanelView( 1139): onTouchEvent_npv action=2 mBlockTouches=false
Line 1709: D/NotificationPanelView( 1139): onTouchEvent_npv mExpandedHeight=827.3082 mQsTracking=true mQsExpandImmediate=false mIsExpanding=true mKeyguardShowing=true mConflictingQsExpansionGesture=false
Line 1733: D/PanelView0( 1139): onTouchEvent mTracking=true waitForTouchSlop=true action=2
Line 1735: D/PanelView0( 1139): onTouchEvent_move_newHeight=761.88367
Line 1775: D/NotificationPanelView( 1139): onTouchEvent_npv action=2 mBlockTouches=false
Line 1783: D/NotificationPanelView( 1139): onTouchEvent_npv mExpandedHeight=761.88367 mQsTracking=true mQsExpandImmediate=false mIsExpanding=true mKeyguardShowing=true mConflictingQsExpansionGesture=false
Line 1817: D/PanelView0( 1139): onTouchEvent mTracking=true waitForTouchSlop=true action=2
Line 1819: D/PanelView0( 1139): onTouchEvent_move_newHeight=683.01904
Line 1873: D/NotificationPanelView( 1139): onTouchEvent_npv action=2 mBlockTouches=false
Line 1877: D/NotificationPanelView( 1139): onTouchEvent_npv mExpandedHeight=683.01904 mQsTracking=true mQsExpandImmediate=false mIsExpanding=true mKeyguardShowing=true mConflictingQsExpansionGesture=false
Line 1897: D/PanelView0( 1139): onTouchEvent mTracking=true waitForTouchSlop=true action=2
Line 1899: D/PanelView0( 1139): onTouchEvent_move_newHeight=592.3085
Line 1945: D/NotificationPanelView( 1139): onTouchEvent_npv action=2 mBlockTouches=false
Line 1953: D/NotificationPanelView( 1139): onTouchEvent_npv mExpandedHeight=592.3085 mQsTracking=true mQsExpandImmediate=false mIsExpanding=true mKeyguardShowing=true mConflictingQsExpansionGesture=false
Line 1981: D/PanelView0( 1139): onTouchEvent mTracking=true waitForTouchSlop=true action=2
Line 1983: D/PanelView0( 1139): onTouchEvent_move_newHeight=491.14014
Line 2029: D/NotificationPanelView( 1139): onTouchEvent_npv action=2 mBlockTouches=false
Line 2035: D/NotificationPanelView( 1139): onTouchEvent_npv mExpandedHeight=491.14014 mQsTracking=true mQsExpandImmediate=false mIsExpanding=true mKeyguardShowing=true mConflictingQsExpansionGesture=false
Line 2065: D/PanelView0( 1139): onTouchEvent mTracking=true waitForTouchSlop=true action=2
Line 2067: D/PanelView0( 1139): onTouchEvent_move_newHeight=380.05933
Line 2111: D/NotificationPanelView( 1139): onTouchEvent_npv action=2 mBlockTouches=false
Line 2119: D/NotificationPanelView( 1139): onTouchEvent_npv mExpandedHeight=380.05933 mQsTracking=true mQsExpandImmediate=false mIsExpanding=true mKeyguardShowing=true mConflictingQsExpansionGesture=false
Line 2149: D/PanelView0( 1139): onTouchEvent mTracking=true waitForTouchSlop=true action=2
Line 2151: D/PanelView0( 1139): onTouchEvent_move_newHeight=264.91766
Line 2197: D/NotificationPanelView( 1139): onTouchEvent_npv action=2 mBlockTouches=false
Line 2203: D/NotificationPanelView( 1139): onTouchEvent_npv mExpandedHeight=264.91766 mQsTracking=true mQsExpandImmediate=false mIsExpanding=true mKeyguardShowing=true mConflictingQsExpansionGesture=false
Line 2249: D/PanelView0( 1139): onTouchEvent mTracking=true waitForTouchSlop=true action=2
Line 2251: D/PanelView0( 1139): onTouchEvent_move_newHeight=138.93573
Line 2293: D/NotificationPanelView( 1139): onTouchEvent_npv action=1 mBlockTouches=false
Line 2297: D/NotificationPanelView( 1139): onTouchEvent_npv mExpandedHeight=138.93573 mQsTracking=true mQsExpandImmediate=false mIsExpanding=true mKeyguardShowing=true mConflictingQsExpansionGesture=false
Line 2317: D/PanelView0( 1139): onTouchEvent mTracking=true waitForTouchSlop=true action=1
11.滑动进入相机/电话
- Android L SystemUI 流程简要分析
- android SystemUI 流程分析
- android SystemUI 流程分析
- android SystemUI 流程分析
- Android L Settings 简要分析
- Android L Settings 简要分析
- Android L Settings 简要分析
- android 6.0 SystemUI源码分析(2)-SystemUI启动流程
- android 6.0 SystemUI源码分析(2)-SystemUI启动流程
- android4 SystemUI 流程分析
- android L 关机流程分析
- android systemui流程
- android SystemUI浅析之SystemUI启动流程
- android 6.0 SystemUI源码分析(4)-StatusBar显示流程
- Android之SystemUI加载流程和NavigationBar的分析
- android 6.0 SystemUI源码分析(4)-StatusBar显示流程
- Android 5.0 状态栏(SystemUI)系统图标加载流程简单分析
- Android系统应用---SystemUI之一:SystemUI概述和创建启动流程分析
- linux命令(1)
- jobdu1431
- SQL基础
- NFS实践及自动挂载问题
- 备案期间网站是否能正常访问
- Android L SystemUI 流程简要分析
- 你怎样看待梁定郊的贿谢之行.
- java并发库之Executors常用的创建ExecutorService的几个方法说明
- mongoDB学习笔记二
- HM编码器代码阅读(29)——码率控制
- java的锁机制
- Spotlight 监控工具使用
- kafka+zookeeper入门搭建
- 2. 命令 tar