防止通話中的錯誤操作,變更通知/快速設置面板操作
1. Description
■ Current conclusion
romve the function of that the pull-down animation will start if a click event happened in the head of screen
■ My analysis
The original problem of Android.
There are three main places for the click event to start the pull-down animation:
- MotionEvent.ACTION_DOWN ,if Phine is Fully Collapsed && HeadsUpManager has not Pinned HeadsUp && StatusBaris Bouncer Showing(),the pull-down animation will run
- MotionEvent.ACTION_CANCEL, involved in endMotionEvent():
a, if the distance between x/y in which the on touch down event and x/y in which the on touch up event is greater than 8 dp && the velocity withgetYVelocity()is bigger than 0,the pull-down animation will run
b, if the distance between x/y in which the on touch down event and x/y in which the on touch up event is greater than 8 dp && the time of on touch down to on touch up less than 500ms,the pull-down animation will run
wo need to
■Submit URL
2. Analysis
功能定位:該功能主要是涉及到systemUI的狀態欄
觸發點: 點擊狀態欄
現象: 顯示通知欄
解題思路:屏蔽狀態欄的點擊事件,只允許下拉才顯示通知面板
public class SystemBars extends SystemUI {
private static final String TAG = "SystemBars";
private static final boolean DEBUG = false;
private static final int WAIT_FOR_BARS_TO_DIE = 500;
// in-process fallback implementation, per the product config
private SystemUI mStatusBar;
@Override
public void start() {
if (DEBUG) Log.d(TAG, "start");
createStatusBarFromConfig();
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mStatusBar != null) {
mStatusBar.dump(fd, pw, args);
}
}
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 = (SystemUI) cls.newInstance();
} catch (Throwable t) {
throw andLog("Error creating status bar component: " + clsName, t);
}
mStatusBar.mContext = mContext;
mStatusBar.mComponents = mComponents;
if (mStatusBar instanceof StatusBar) {
SystemUIFactory.getInstance().getRootComponent()
.getStatusBarInjector()
.createStatusBar((StatusBar) mStatusBar);
}
mStatusBar.start();
if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
}
private RuntimeException andLog(String msg, Throwable t) {
Log.w(TAG, msg, t);
throw new RuntimeException(msg, t);
}
}
從上面代碼上看,和狀態欄相關的邏輯什麼都沒有做,它只是
- 通過配置文件查找到對應得狀態欄實際的實現類
- 通過反射獲得實際的實現類對象
- 執行start();
配置文件聲明,具體如下:
<!-- Component to be used as the status bar service. Must implement the IStatusBar interface. This name is in the ComponentName flattened format (package/class) -->
<string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar</string>
它這樣子的做法其實也很好理解,就是爲了適配不同產品,即如果編譯的產品是手機,就用StatusBar這個類,如果是車載系統/手錶就可能用其他的實現類了。
然後看看StatusBar的實現,因爲它比較大,所以摘錄部分
public class StatusBar extends SystemUI implements DemoMode,
ActivityStarter, OnUnlockMethodChangedListener,
OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
ColorExtractor.OnColorsChangedListener, ConfigurationListener,
StatusBarStateController.StateListener, ShadeController,
ActivityLaunchAnimator.Callback, AppOpsController.Callback {
@Override
public void start() {
/*
1. 獲得各個服務的對象,方便後續使用
2. 執行createAndAddWindows(result);開始繪製界面
*/
mGroupManager = Dependency.get(NotificationGroupManager.class);
mGroupAlertTransferHelper = Dependency.get(NotificationGroupAlertTransferHelper.class);
mVisualStabilityManager = Dependency.get(VisualStabilityManager.class);
mNotificationLogger = Dependency.get(NotificationLogger.class);
mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
mNotificationListener = Dependency.get(NotificationListener.class);
mNotificationListener.registerAsSystemService();
mNetworkController = Dependency.get(NetworkController.class);
mUserSwitcherController = Dependency.get(UserSwitcherController.class);
mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
mScreenLifecycle.addObserver(mScreenObserver);
mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
mBatteryController = Dependency.get(BatteryController.class);
mAssistManager = Dependency.get(AssistManager.class);
mUiModeManager = mContext.getSystemService(UiModeManager.class);
mLockscreenUserManager = Dependency.get(NotificationLockscreenUserManager.class);
mGutsManager = Dependency.get(NotificationGutsManager.class);
mMediaManager = Dependency.get(NotificationMediaManager.class);
mEntryManager = Dependency.get(NotificationEntryManager.class);
mBypassHeadsUpNotifier.setUp(mEntryManager);
mNotificationInterruptionStateProvider =Dependency.get(NotificationInterruptionStateProvider.class);
mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class);
mForegroundServiceController = Dependency.get(ForegroundServiceController.class);
mAppOpsController = Dependency.get(AppOpsController.class);
mZenController = Dependency.get(ZenModeController.class);
mKeyguardViewMediator = getComponent(KeyguardViewMediator.class);
mColorExtractor = Dependency.get(SysuiColorExtractor.class);
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
mNavigationBarController = Dependency.get(NavigationBarController.class);
mBubbleController = Dependency.get(BubbleController.class);
mBubbleController.setExpandListener(mBubbleExpandListener);
mActivityIntentHelper = new ActivityIntentHelper(mContext);
KeyguardSliceProvider sliceProvider = KeyguardSliceProvider.getAttachedInstance();
if (sliceProvider != null) {
sliceProvider.initDependencies(mMediaManager, mStatusBarStateController,mKeyguardBypassController, DozeParameters.getInstance(mContext));
} else {
Log.w(TAG, "Cannot init KeyguardSliceProvider dependencies");
}
mColorExtractor.addOnColorsChangedListener(this);
mStatusBarStateController.addCallback(this,SysuiStatusBarStateController.RANK_STATUS_BAR);
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mDreamManager = IDreamManager.Stub.asInterface(ServiceManager.checkService(DreamService.DREAM_SERVICE));
mDisplay = mWindowManager.getDefaultDisplay();
mDisplayId = mDisplay.getDisplayId();
updateDisplaySize();
mVibrateOnOpening = mContext.getResources().getBoolean(R.bool.config_vibrateOnIconAnimation);
mVibratorHelper = Dependency.get(VibratorHelper.class);
DateTimeView.setReceiverHandler(Dependency.get(Dependency.TIME_TICK_HANDLER));
putComponent(StatusBar.class, this);
// start old BaseStatusBar.start().
mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
mAccessibilityManager = (AccessibilityManager)mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
mKeyguardUpdateMonitor.setStatusBar(this);
mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
mBarService = IStatusBarService.Stub.asInterface(ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mRecents = getComponent(Recents.class);
mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
mFalsingManager = Dependency.get(FalsingManager.class);
// Connect in to the status bar manager service
mCommandQueue = getComponent(CommandQueue.class);
mCommandQueue.addCallback(this);
RegisterStatusBarResult result = null;
try {
result = mBarService.registerStatusBar(mCommandQueue);
} catch (RemoteException ex) {
ex.rethrowFromSystemServer();
}
createAndAddWindows(result);
// Make sure we always have the most current wallpaper info.
IntentFilter wallpaperChangedFilter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
mContext.registerReceiverAsUser(mWallpaperChangedReceiver, UserHandle.ALL,wallpaperChangedFilter, null /* broadcastPermission */, null /* scheduler */);
mWallpaperChangedReceiver.onReceive(mContext, null);
// Set up the initial notification state. This needs to happen before CommandQueue.disable()
setUpPresenter();
setSystemUiVisibility(mDisplayId, result.mSystemUiVisibility,result.mFullscreenStackSysUiVisibility, result.mDockedStackSysUiVisibility,0xffffffff, result.mFullscreenStackBounds, result.mDockedStackBounds,result.mNavbarColorManagedByIme);
// StatusBarManagerService has a back up of IME token and it's restored here.
setImeWindowStatus(mDisplayId, result.mImeToken, result.mImeWindowVis,result.mImeBackDisposition, result.mShowImeSwitcher);
// Set up the initial icon state
int numIcons = result.mIcons.size();
for (int i = 0; i < numIcons; i++) {
mCommandQueue.setIcon(result.mIcons.keyAt(i), result.mIcons.valueAt(i));
}
IntentFilter internalFilter = new IntentFilter();
internalFilter.addAction(BANNER_ACTION_CANCEL);
internalFilter.addAction(BANNER_ACTION_SETUP);
mContext.registerReceiver(mBannerActionBroadcastReceiver, internalFilter, PERMISSION_SELF,null);
IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface(ServiceManager.getService(Context.WALLPAPER_SERVICE));
try {
wallpaperManager.setInAmbientMode(false /* ambientMode */, 0L /* duration */);
} catch (RemoteException e) {
// Just pass, nothing critical.
}
// end old BaseStatusBar.start().
// Lastly, call to the icon policy to install/update all the icons.
mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController);
mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController);
mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
mUnlockMethodCache.addListener(this);
startKeyguard();
mKeyguardUpdateMonitor.registerCallback(mUpdateCallback);
putComponent(DozeHost.class, mDozeServiceHost);
mScreenPinningRequest = new ScreenPinningRequest(mContext);
Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(this)
Dependency.get(ConfigurationController.class).addCallback(this);
// set the initial view visibility
Dependency.get(InitController.class).addPostInitTask(this::updateAreThereNotifications);
int disabledFlags1 = result.mDisabledFlags1;
int disabledFlags2 = result.mDisabledFlags2;
Dependency.get(InitController.class).addPostInitTask(() -> setUpDisableFlags(disabledFlags1, disabledFlags2));
}
}
然後看看createAndAddWindows是如何繪製界面的
public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
makeStatusBarView(result);
mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
mStatusBarWindowController.add(mStatusBarWindow, getStatusBarHeight());
}
// ================================================================================
// Constructing the view ,這裏開始創建狀態欄
// ================================================================================
protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
final Context context = mContext;
updateDisplaySize(); // populates mDisplayMetrics
updateResources();
updateTheme();
inflateStatusBarWindow(context);
mStatusBarWindow.setService(this);
mStatusBarWindow.setFastFingerLauncher(mFastFingerLauncer);
mStatusBarWindow.setBypassController(mKeyguardBypassController);
mStatusBarWindow.setOnTouchListener(getStatusBarWindowTouchListener());
// TODO: Deal with the ugliness that comes from having some of the statusbar broken out into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel);/*mNotificationPanel 就是下拉出來的通知面板*/
mFingerIconView = mStatusBarWindow.findViewById(R.id.finger_icon);
mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller);
mZenController.addCallback(this);
NotificationListContainer notifListContainer = (NotificationListContainer) mStackScroller;
mNotificationLogger.setUpWithContainer(notifListContainer);
mNotificationIconAreaController = SystemUIFactory.getInstance().createNotificationIconAreaController(context, this,mWakeUpCoordinator,mKeyguardBypassController,mStatusBarStateController);
mWakeUpCoordinator.setIconAreaController(mNotificationIconAreaController);
inflateShelf();
mNotificationIconAreaController.setupShelf(mNotificationShelf);
mNotificationPanel.setOnReinflationListener(mNotificationIconAreaController::initAodIcons);
mNotificationPanel.addExpansionListener(mWakeUpCoordinator);
Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mNotificationIconAreaController);
// Allow plugins to reference DarkIconDispatcher and StatusBarStateController
Dependency.get(PluginDependencyProvider.class).allowPluginDependency(DarkIconDispatcher.class);
Dependency.get(PluginDependencyProvider.class)
.allowPluginDependency(StatusBarStateController.class);
FragmentHostManager.get(mStatusBarWindow)
.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
CollapsedStatusBarFragment statusBarFragment =
(CollapsedStatusBarFragment) fragment;
statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
PhoneStatusBarView oldStatusBarView = mStatusBarView;
mStatusBarView = (PhoneStatusBarView) fragment.getView();
mStatusBarView.setBar(this);
mStatusBarView.setPanel(mNotificationPanel);
mStatusBarView.setScrimController(mScrimController);
// CollapsedStatusBarFragment re-inflated PhoneStatusBarView and both of
// mStatusBarView.mExpanded and mStatusBarView.mBouncerShowing are false.
// PhoneStatusBarView's new instance will set to be gone in
// PanelBar.updateVisibility after calling mStatusBarView.setBouncerShowing
// that will trigger PanelBar.updateVisibility. If there is a heads up showing,
// it needs to notify PhoneStatusBarView's new instance to update the correct
// status by calling mNotificationPanel.notifyBarPanelExpansionChanged().
if (mHeadsUpManager.hasPinnedHeadsUp()) {
mNotificationPanel.notifyBarPanelExpansionChanged();
}
mStatusBarView.setBouncerShowing(mBouncerShowing);
if (oldStatusBarView != null) {
float fraction = oldStatusBarView.getExpansionFraction();
boolean expanded = oldStatusBarView.isExpanded();
mStatusBarView.panelExpansionChanged(fraction, expanded);
}
HeadsUpAppearanceController oldController = mHeadsUpAppearanceController;
if (mHeadsUpAppearanceController != null) {
// This view is being recreated, let's destroy the old one
mHeadsUpAppearanceController.destroy();
}
mHeadsUpAppearanceController = new HeadsUpAppearanceController(
mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow,
mStatusBarStateController, mKeyguardBypassController,
mWakeUpCoordinator);
mHeadsUpAppearanceController.readFrom(oldController);
mStatusBarWindow.setStatusBarView(mStatusBarView);
updateAreThereNotifications();
getStatusBarTransitions().setCutoutView(addCornerCutoutSpaceView());
checkBarModes();
}).getFragmentManager()
.beginTransaction()
.replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
CollapsedStatusBarFragment.TAG)
.commit();
mIconController = Dependency.get(StatusBarIconController.class);
mHeadsUpManager.setUp(mStatusBarWindow, mGroupManager, this, mVisualStabilityManager);
Dependency.get(ConfigurationController.class).addCallback(mHeadsUpManager);
mHeadsUpManager.addListener(this);
mHeadsUpManager.addListener(mNotificationPanel);
mHeadsUpManager.addListener(mGroupManager);
mHeadsUpManager.addListener(mGroupAlertTransferHelper);
mHeadsUpManager.addListener(mVisualStabilityManager);
mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager);
mNotificationLogger.setHeadsUpManager(mHeadsUpManager);
putComponent(HeadsUpManager.class, mHeadsUpManager);
createNavigationBar(result);
if (ENABLE_LOCKSCREEN_WALLPAPER) {
mLockscreenWallpaper = new LockscreenWallpaper(mContext, this, mHandler);
}
mKeyguardIndicationController =SystemUIFactory.getInstance().createKeyguardIndicationController(mContext, mStatusBarWindow.findViewById(R.id.keyguard_indication_area), mStatusBarWindow.findViewById(R.id.lock_icon));
mNotificationPanel.setKeyguardIndicationController(mKeyguardIndicationController);
KeyguardBottomAreaView fjBottomArea =(KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);
mKeyguardIndicationController =SystemUIFactory.getInstance().createKeyguardIndicationController(mContext,(ViewGroup) fjBottomArea.findViewById(R.id.keyguard_indication_area), mStatusBarWindow.findViewById(R.id.lock_icon)); mKeyguardIndicationController.setStatusBar(this);
mNotificationPanel.setKeyguardIndicationController(mKeyguardIndicationController);
mAmbientIndicationContainer = mStatusBarWindow.findViewById(
R.id.ambient_indication_container);
// TODO: Find better place for this callback.
mBatteryController.addCallback(new BatteryStateChangeCallback() {
@Override
public void onPowerSaveChanged(boolean isPowerSave) {
mHandler.post(mCheckBarModes);
if (mDozeServiceHost != null) {
mDozeServiceHost.firePowerSaveChanged(isPowerSave);
}
}
@Override
public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
// noop
}
});
mAutoHideController = Dependency.get(AutoHideController.class);
mAutoHideController.setStatusBar(this);
mLightBarController = Dependency.get(LightBarController.class);
ScrimView scrimBehind = mStatusBarWindow.findViewById(R.id.scrim_behind);
ScrimView scrimInFront = mStatusBarWindow.findViewById(R.id.scrim_in_front);
mScrimController = SystemUIFactory.getInstance().createScrimController(
scrimBehind, scrimInFront, mLockscreenWallpaper,
(state, alpha, color) -> mLightBarController.setScrimState(state, alpha, color),
scrimsVisible -> {
if (mStatusBarWindowController != null) {
mStatusBarWindowController.setScrimsVisibility(scrimsVisible);
}
if (mStatusBarWindow != null) {
mStatusBarWindow.onScrimVisibilityChanged(scrimsVisible);
}
}, DozeParameters.getInstance(mContext),
mContext.getSystemService(AlarmManager.class),
mKeyguardMonitor);
mNotificationPanel.initDependencies(this, mGroupManager, mNotificationShelf,
mHeadsUpManager, mNotificationIconAreaController, mScrimController);
mDozeScrimController = new DozeScrimController(DozeParameters.getInstance(context));
BackDropView backdrop = mStatusBarWindow.findViewById(R.id.backdrop);
mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front),backdrop.findViewById(R.id.backdrop_back), mScrimController, mLockscreenWallpaper);
// Other icons
mVolumeComponent = getComponent(VolumeComponent.class);
mNotificationPanel.setUserSetupComplete(mUserSetup);
if (UserManager.get(mContext).isUserSwitcherEnabled()) {
createUserSwitcher();
}
mNotificationPanel.setLaunchAffordanceListener(mStatusBarWindow::onShowingLaunchAffordanceChanged);
// Set up the quick settings tile panel
View container = mStatusBarWindow.findViewById(R.id.qs_frame);
if (container != null) {
FragmentHostManager fragmentHostManager = FragmentHostManager.get(container);
ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame,
Dependency.get(ExtensionController.class)
.newExtension(QS.class)
.withPlugin(QS.class)
.withDefault(this::createDefaultQSFragment)
.build());
mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow,
(visible) -> {
mBrightnessMirrorVisible = visible;
updateScrimController();
});
fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
QS qs = (QS) f;
if (qs instanceof QSFragment) {
mQSPanel = ((QSFragment) qs).getQsPanel();
mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
}
});
}
mReportRejectedTouch = mStatusBarWindow.findViewById(R.id.report_rejected_touch);
if (mReportRejectedTouch != null) {
updateReportRejectedTouchVisibility();
mReportRejectedTouch.setOnClickListener(v -> {
Uri session = mFalsingManager.reportRejectedTouch();
if (session == null) { return; }
StringWriter message = new StringWriter();
message.write("Build info: ");
message.write(SystemProperties.get("ro.build.description"));
message.write("\nSerial number: ");
message.write(SystemProperties.get("ro.serialno"));
message.write("\n");
PrintWriter falsingPw = new PrintWriter(message);
FalsingLog.dump(falsingPw);
falsingPw.flush();
startActivityDismissingKeyguard(Intent.createChooser(new Intent(Intent.ACTION_SEND).setType("*/*") .putExtra(Intent.EXTRA_SUBJECT, "Rejected touch report").putExtra(Intent.EXTRA_STREAM, session).putExtra(Intent.EXTRA_TEXT, message.toString()),"Share rejected touch report") .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
true /* onlyProvisioned */, true /* dismissShade */);
});
}
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
if (!pm.isScreenOn()) {
mBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
}
mGestureWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "GestureWakeLock");
mVibrator = mContext.getSystemService(Vibrator.class);
int[] pattern = mContext.getResources().getIntArray( R.array.config_cameraLaunchGestureVibePattern);
mCameraLaunchGestureVibePattern = new long[pattern.length];
for (int i = 0; i < pattern.length; i++) {
mCameraLaunchGestureVibePattern[i] = pattern[i];
}
// receive broadcasts
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG);
context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
IntentFilter demoFilter = new IntentFilter();
if (DEBUG_MEDIA_FAKE_ARTWORK) {
demoFilter.addAction(ACTION_FAKE_ARTWORK);
}
demoFilter.addAction(ACTION_DEMO);
context.registerReceiverAsUser(mDemoReceiver, UserHandle.ALL, demoFilter,android.Manifest.permission.DUMP, null);
// listen for USER_SETUP_COMPLETE setting (per-user)
mDeviceProvisionedController.addCallback(mUserSetupObserver);
mUserSetupObserver.onUserSetupChanged();
Typeface typeface = Typeface.defaultFromStyle(Typeface.NORMAL);
if(mStatusBarView != null) { //TODO(TS_FW-AF) : For build success so mark below code.
TextView clock = (TextView) mStatusBarView.findViewById(R.id.clock);
if( clock != null) {
clock.setTypeface(typeface);
}
}
if( mNotificationPanel != null ) {
List<TextView> textViews = getViewsGroupRecursive(mNotificationPanel);
for (TextView textView : textViews) {
if(textView.getId() != R.id.time_view){
textView.getPaint().setTypeface(typeface);
}
}
}
// disable profiling bars, since they overlap and clutter the output on app windows
ThreadedRenderer.overrideProperty("disableProfileBars", "true");
// Private API call to make the shadows look better for Recents
ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f));
mBiometricManager = mContext.getSystemService(BiometricManager.class);
if (mFingerIconView != null) {
mFingerIconView.setStatusBar(this);
if (hasEnrolledBiometrics()) {
mFingerIconView.setVisibility(View.VISIBLE);
} else {
mFingerIconView.setVisibility(View.GONE);
}
mFingerIconView.setVisibility(View.GONE);
}
}
然後轉入通知面板的實現類NotificationPanelView,主要看看它的onTouchEvent方法裏面的邏輯,觸摸屏幕的實現都是在裏面做的
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mBlockTouches || (mQs != null && mQs.isCustomizing())) {
Log.d(TAG, "onTouchEvent false mBlockTouches="+mBlockTouches);
return false;
}
// Do not allow panel expansion if bouncer is scrimmed, otherwise user would be able to pull down QS or expand the shade.
if (mStatusBar.isBouncerShowingScrimmed()) {
return false;
}
// Make sure the next touch won't the blocked after the current ends.
if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) {
mBlockingExpansionForCurrentTouch = false;
}
// When touch focus transfer happens, ACTION_DOWN->ACTION_UP may happen immediately without any ACTION_MOVE event.In such case, simply expand the panel instead of being stuck at the bottom bar.
if (mLastEventSynthesizedDown && event.getAction() == MotionEvent.ACTION_UP) {
expand(true /* animate */);
}
initDownStates(event);
Log.d(TAG, "onTouchEvent mIsExpanding="+mIsExpanding);
if (!mIsExpanding && !shouldQuickSettingsIntercept(mDownX, mDownY, 0)
&& mPulseExpansionHandler.onTouchEvent(event)) {
// We're expanding all the other ones shouldn't get this anymore
return true;
}
if (mListenForHeadsUp && !mHeadsUpTouchHelper.isTrackingHeadsUp()
&& mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
mIsExpansionFromHeadsUp = true;
MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_PEEK, 1);
}
boolean handled = false;
Log.d(TAG, "onTouchEvent mAffordanceHelper.isFinishingUnlockAnimationEnabled()="+mAffordanceHelper.isFinishingUnlockAnimationEnabled());
if (((mAffordanceHelper.isFinishingUnlockAnimationEnabled())
|| (!mIsExpanding || mHintAnimationRunning))
&& !mQsExpanded
&& mBarState != StatusBarState.SHADE
&& !mDozing) {
handled |= mAffordanceHelper.onTouchEvent(event);
}
if (mOnlyAffordanceInThisMotion) {
return true;
}
handled |= mHeadsUpTouchHelper.onTouchEvent(event);
if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQsTouch(event)) {
return true;
}
if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1);
updateVerticalPanelPosition(event.getX());
handled = true;
}
handled |= super.onTouchEvent(event);
// return !mDozing || mPulsing || handled;
Log.d(TAG, "onTouchEvent("+(!mDozing || mPulsing || handled)+" mDozing="+mDozing+" mPulsing="+mPulsing+" handled="+handled);
return !mDozing || handled;
}
從上面看到,onTouchEvent 的幾個常用見得行爲(MotionEvent.ACTION_DOWN、MotionEvent.ACTION_MOVE、MotionEvent.ACTION_UP)最終還是要傳給它的父類PanelView處理,所以在看看PanelView的onTouchEvent的處理流程:
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mInstantExpanding|| (mTouchDisabled && event.getActionMasked() != MotionEvent.ACTION_CANCEL)|| (mMotionAborted && event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
return false;
}
// If dragging should not expand the notifications shade, then return false.
if (!mNotificationsDragEnabled) {/*不展開通知陰影*/
if (mTracking) {
// Turn off tracking if it's on or the shade can get stuck in the down position.
onTrackingStopped(true /* expand */);
}
return false;
}
// On expanding, single mouse click expands the panel instead of dragging.
if (isFullyCollapsed() && event.isFromSource(InputDevice.SOURCE_MOUSE)) {
if (event.getAction() == MotionEvent.ACTION_UP) {
expand(true);
}
return true;
}
/*
* 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 show 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 x = event.getX(pointerIndex);
final float y = event.getY(pointerIndex);
/* 此處獲得點擊位置的x&&y座標*/
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mGestureWaitForTouchSlop = shouldGestureWaitForTouchSlop();
mIgnoreXTouchSlop = isFullyCollapsed() || shouldGestureIgnoreXTouchSlop(x, y);
}
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:/*處理按下的事件*/
startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
mJustPeeked = false;
mMinExpandHeight = 0.0f;
mPanelClosedOnDown = isFullyCollapsed();
mHasLayoutedSinceDown = false;
mUpdateFlingOnLayout = false;
mMotionAborted = false;
mPeekTouching = mPanelClosedOnDown;
mDownTime = SystemClock.uptimeMillis();
mTouchAboveFalsingThreshold = false;
mCollapsedAndHeadsUpOnDown = isFullyCollapsed()&& mHeadsUpManager.hasPinnedHeadsUp();
addMovement(event);/*這個方法跟着move的行爲*/
if (!mGestureWaitForTouchSlop || (mHeightAnimator != null && !mHintAnimationRunning)|| mPeekAnimator != null) {
mTouchSlopExceeded = (mHeightAnimator != null && !mHintAnimationRunning)|| mPeekAnimator != null || mTouchSlopExceededBeforeDown;
cancelHeightAnimator();
cancelPeek();
onTrackingStarted();
}
if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp()&& !mStatusBar.isBouncerShowing()) {
startOpening(event);/*if Phine is Fully Collapsed && HeadsUpManager has not Pinned HeadsUp && StatusBaris Bouncer Showing(),the pull-down animation will run*/
}
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);
startExpandMotion(newX, newY, true /* startTracking */, mExpandedHeight);
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
mMotionAborted = true;
endMotionEvent(event, x, y, true /* forceCancel */);
return false;
}
break;
case MotionEvent.ACTION_MOVE:
addMovement(event);
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) || mIgnoreXTouchSlop)) {/*滑動行爲處理,如果x方向或者y方向移動大於8dip 就認爲是在滑動*/
mTouchSlopExceeded = true;
if (mGestureWaitForTouchSlop && !mTracking && !mCollapsedAndHeadsUpOnDown) {
if (!mJustPeeked && mInitialOffsetOnTouch != 0f) {
startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
h = 0;
}
cancelHeightAnimator();
onTrackingStarted();
}
}
float newHeight = Math.max(0, h + mInitialOffsetOnTouch);/*計算新的展開高度*/
if (newHeight > mPeekHeight) {
if (mPeekAnimator != null) {
mPeekAnimator.cancel();/*取消下拉通知面板行爲*/
}
mJustPeeked = false;
} else if (mPeekAnimator == null && mJustPeeked) {
// The initial peek has finished, but we haven't dragged as far yet, lets speed it up by starting at the peek height.
mInitialOffsetOnTouch = mExpandedHeight;
mInitialTouchY = y;
mMinExpandHeight = mExpandedHeight;
mJustPeeked = false;
}
newHeight = Math.max(newHeight, mMinExpandHeight);
if (-h >= getFalsingThreshold()) {
mTouchAboveFalsingThreshold = true;
mUpwardsWhenTresholdReached = isDirectionUpwards(x, y);
}
if (!mJustPeeked && (!mGestureWaitForTouchSlop || mTracking) &&
!isTrackingBlocked()) {
setExpandedHeightInternal(newHeight);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (isFullyCollapsed() && event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
if (mTracking) {
onTrackingStopped(false);
}
return true;
}
addMovement(event);
endMotionEvent(event, x, y, false /* forceCancel */); /*這個方法會出來手指離開屏幕那一刻的觸摸事件*/
break;
}
return !mGestureWaitForTouchSlop || mTracking;
}
在該方法中:
- MotionEvent.ACTION_DOWN時,如果notification面板沒有展開,點擊則會展開notification面板
- 在放開屏幕是,會執行一個endMotionEvent邏輯,然後裏面也有展開notification面板操作
然後看看endMotionEvent方法的實現
private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
mTrackingPointer = -1;
if ((mTracking && mTouchSlopExceeded)|| Math.abs(x - mInitialTouchX) > mTouchSlop || Math.abs(y - mInitialTouchY) > mTouchSlop|| event.getActionMasked() == MotionEvent.ACTION_CANCEL|| forceCancel) {
mVelocityTracker.computeCurrentVelocity(1000);
float vel = mVelocityTracker.getYVelocity();
float vectorVel = (float) Math.hypot(
mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
if (isOnClickdToStartPeekAnimationDisable && (Math.abs(x - mInitialTouchX) > 2 * mTouchSlop || Math.abs(y - mInitialTouchY) > 2 * mTouchSlop)) {
Log.d(TAG, "a move event has happened! mTouchSlop is double length!");
mLongTouchSlop = true;
}
boolean expand = flingExpands(vel, vectorVel, x, y)
|| event.getActionMasked() == MotionEvent.ACTION_CANCEL
|| forceCancel;
DozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
mStatusBar.isFalsingThresholdNeeded(),
mStatusBar.isWakeUpComingFromTouch());
// Log collapse gesture if on lock screen.
if (!expand && mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
float displayDensity = mStatusBar.getDisplayDensity();
int heightDp = (int) Math.abs((y - mInitialTouchY) / displayDensity);
int velocityDp = (int) Math.abs(vel / displayDensity);
mLockscreenGestureLogger.write(
MetricsEvent.ACTION_LS_UNLOCK,
heightDp, velocityDp);
}
fling(vel, expand, isFalseTouch(x, y));
onTrackingStopped(expand);
mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown;
if (mUpdateFlingOnLayout) {
mUpdateFlingVelocity = vel;
}
} else if (mPanelClosedOnDown && !mHeadsUpManager.hasPinnedHeadsUp() && !mTracking
&& !mStatusBar.isBouncerShowing() && !mKeyguardMonitor.isKeyguardFadingAway()) {
long timePassed = SystemClock.uptimeMillis() - mDownTime;
if (timePassed < ViewConfiguration.getLongPressTimeout() && !isOnClickdToStartPeekAnimationDisable) {
// Lets show the user that he can actually expand the panel
runPeekAnimation(PEEK_ANIMATION_DURATION, getPeekHeight(), true /* collapseWhenFinished */);
} else {
// We need to collapse the panel since we peeked to the small height.
postOnAnimation(mPostCollapseRunnable);
}
} else if (!mStatusBar.isBouncerShowing()) {
boolean expands = onEmptySpaceClick(mInitialTouchX);
onTrackingStopped(expands);
}
mVelocityTracker.clear();
mPeekTouching = false;
}
3. solution
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 76407e1..e88d968 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -124,6 +124,12 @@ public abstract class PanelView extends FrameLayout {
private final FalsingManager mFalsingManager;
private final VibratorHelper mVibratorHelper;
+ // fix bug Add-S
+ private boolean isOnClickdToStartPeekAnimationDisable =true;
+ private boolean mLongTouchSlop=false;
+ // fix bug Add-E
+
+
/**
* For PanelView fling perflock call
*/
@@ -352,10 +358,12 @@ public abstract class PanelView extends FrameLayout {
cancelPeek();
onTrackingStarted();
}
+ // fix bug Mod-S
if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp()
- && !mStatusBar.isBouncerShowing()) {
+ && !mStatusBar.isBouncerShowing() && !isOnClickdToStartPeekAnimationDisable) {
startOpening(event);
}
+ // fix bug Mod-E
break;
case MotionEvent.ACTION_POINTER_UP:
@@ -507,10 +515,17 @@ public abstract class PanelView extends FrameLayout {
float vel = mVelocityTracker.getYVelocity();
float vectorVel = (float) Math.hypot(
mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
-
+ // fix bug Add-S
+ if(isOnClickdToStartPeekAnimationDisable && (Math.abs(x - mInitialTouchX) > 2*mTouchSlop || Math.abs(y - mInitialTouchY) > 2*mTouchSlop)){
+ Log.d(TAG,"a move event has happened! mTouchSlop is double length!");
+ mLongTouchSlop=true;
+ }
+ // fix bug Add-E
boolean expand = flingExpands(vel, vectorVel, x, y)
|| event.getActionMasked() == MotionEvent.ACTION_CANCEL
|| forceCancel;
+
+
DozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
mStatusBar.isFalsingThresholdNeeded(),
mStatusBar.isWakeUpComingFromTouch());
@@ -532,13 +547,15 @@ public abstract class PanelView extends FrameLayout {
} else if (mPanelClosedOnDown && !mHeadsUpManager.hasPinnedHeadsUp() && !mTracking
&& !mStatusBar.isBouncerShowing() && !mKeyguardMonitor.isKeyguardFadingAway()) {
long timePassed = SystemClock.uptimeMillis() - mDownTime;
- if (timePassed < ViewConfiguration.getLongPressTimeout()) {
+ // fix bug Mod-S
+ if (timePassed < ViewConfiguration.getLongPressTimeout() && !isOnClickdToStartPeekAnimationDisable) {
// Lets show the user that he can actually expand the panel
runPeekAnimation(PEEK_ANIMATION_DURATION, getPeekHeight(), true /* collapseWhenFinished */);
} else {
// We need to collapse the panel since we peeked to the small height.
postOnAnimation(mPostCollapseRunnable);
}
+ // fix bug Mod-S
} else if (!mStatusBar.isBouncerShowing()) {
boolean expands = onEmptySpaceClick(mInitialTouchX);
onTrackingStopped(expands);
@@ -721,6 +738,13 @@ public abstract class PanelView extends FrameLayout {
if (Math.abs(vectorVel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
return shouldExpandWhenNotFlinging();
} else {
+ // fix bug Add-S
+ if(isOnClickdToStartPeekAnimationDisable && !mLongTouchSlop){
+ return false;
+ }else {
+ mLongTouchSlop=false;
+ }
+ // fix bug Add-E
return vel > 0;
}
}
@@ -762,9 +786,11 @@ public abstract class PanelView extends FrameLayout {
boolean expandBecauseOfFalsing) {
cancelPeek();
float target = expand ? getMaxPanelHeight() : 0;
+
if (!expand) {
mClosing = true;
}
+ Log.d(TAG,"fling,target ="+target +",vel="+vel+",expand="+expand+".collapseSpeedUpFactor="+collapseSpeedUpFactor+",expandBecauseOfFalsing="+expandBecauseOfFalsing);
flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
}
@@ -787,6 +813,7 @@ public abstract class PanelView extends FrameLayout {
ValueAnimator animator = createHeightAnimator(target);
if (expand) {
if (expandBecauseOfFalsing && vel < 0) {
+ Log.d(TAG,"expandBecauseOfFalsing && vel < 0");
vel = 0;
}
mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, getHeight());
@@ -804,6 +831,7 @@ public abstract class PanelView extends FrameLayout {
getHeight());
}
} else {
+ Log.d(TAG,"mFlingAnimationUtilsClosing!");
mFlingAnimationUtilsClosing
.apply(animator, mExpandedHeight, target, vel, getHeight());
}
4. summary
+".collapseSpeedUpFactor="+collapseSpeedUpFactor+",expandBecauseOfFalsing="+expandBecauseOfFalsing);
flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
}
@@ -787,6 +813,7 @@ public abstract class PanelView extends FrameLayout {
ValueAnimator animator = createHeightAnimator(target);
if (expand) {
if (expandBecauseOfFalsing && vel < 0) {
-
Log.d(TAG,"expandBecauseOfFalsing && vel < 0"); vel = 0; } mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, getHeight());
@@ -804,6 +831,7 @@ public abstract class PanelView extends FrameLayout {
getHeight());
}
} else {
-
Log.d(TAG,"mFlingAnimationUtilsClosing!"); mFlingAnimationUtilsClosing .apply(animator, mExpandedHeight, target, vel, getHeight()); }
## 4. summary
該問題主要涉及到狀態欄的onTouchEvent事件,只需要找到對應得處理邏輯就可以屏蔽點擊事件。