这里的文章主要是介绍关于我在开发和debug一些Android系统框架代码中遇到的问题,有不妥之处大家一起讨论。
开发环境:Android O、P 、Q。
开发工具:AndroidStudio、codeblock。
@Override
public boolean isKeyguardLocked() {
return keyguardOn();
}
/** Window flag: special flag to let windows be shown when the screen
* is locked. This will let application windows take precedence over
* key guard or any other lock screens. Can be used with
* {@link #FLAG_KEEP_SCREEN_ON} to turn screen on and display windows
* directly before showing the key guard window. Can be used with
* {@link #FLAG_DISMISS_KEYGUARD} to automatically fully dismisss
* non-secure keyguards. This flag only applies to the top-most
* full-screen window.
* @deprecated Use {@link android.R.attr#showWhenLocked} or
* {@link android.app.Activity#setShowWhenLocked(boolean)} instead to prevent an
* unintentional double life-cycle event.
*/
@Deprecated
public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;
// SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
void adjustStatusBarLocked() {
if (mStatusBarManager == null) {
mStatusBarManager = (StatusBarManager)
mContext.getSystemService(Context.STATUS_BAR_SERVICE);
}
if (mStatusBarManager == null) {
Log.w(TAG, "Could not get status bar manager");
} else {
// Disable aspects of the system/status/navigation bars that must not be re-enabled by
// windows that appear on top, ever
int flags = StatusBarManager.DISABLE_NONE;
if (mShowing) {
// Permanently disable components not available when keyguard is enabled
// (like recents). Temporary enable/disable (e.g. the "back" button) are
// done in KeyguardHostView.
flags |= StatusBarManager.DISABLE_RECENT;//默认屏蔽Recent键。
boolean isDisableSearch = false;
// if (mLockScreenMediatorExt != null) {
// isDisableSearch = mLockScreenMediatorExt.disableSearch(mContext);
// }
/// M: [ALPS00604438] Disable search view for alarm boot
if (PowerOffAlarmManager.isAlarmBoot() || isDisableSearch) {
flags |= StatusBarManager.DISABLE_SEARCH;
}
}
if (isShowingAndNotOccluded()) {
flags |= StatusBarManager.DISABLE_HOME;
}
mStatusBarManager.disable(flags);
}
}
Android P 上是默认打开
// SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
private void adjustStatusBarLocked() {
adjustStatusBarLocked(false /* forceHideHomeRecentsButtons */);
}
private void adjustStatusBarLocked(boolean forceHideHomeRecentsButtons) {
... ...
int flags = StatusBarManager.DISABLE_NONE;
/// M: [ALPS00604438] Disable search view for alarm boot
if (mShowing && PowerOffAlarmManager.isAlarmBoot()) {
flags |= StatusBarManager.DISABLE_SEARCH;
}
if (forceHideHomeRecentsButtons || isShowingAndNotOccluded()) {
flags |= StatusBarManager.DISABLE_HOME | StatusBarManager.DISABLE_RECENT;
}
mStatusBarManager.disable(flags);
}
}
Android Q 上与P基本相同,但没有disable_search.
private void adjustStatusBarLocked() {
1907 adjustStatusBarLocked(false /* forceHideHomeRecentsButtons */);
1908 }
1909
1910 private void adjustStatusBarLocked(boolean forceHideHomeRecentsButtons) {
1911 if (mStatusBarManager == null) {
1912 mStatusBarManager = (StatusBarManager)
1913 mContext.getSystemService(Context.STATUS_BAR_SERVICE);
1914 }
1915 if (mStatusBarManager == null) {
1916 Log.w(TAG, "Could not get status bar manager");
1917 } else {
1918 // Disable aspects of the system/status/navigation bars that must not be re-enabled by
1919 // windows that appear on top, ever
1920 int flags = StatusBarManager.DISABLE_NONE;
1921 if (forceHideHomeRecentsButtons || isShowingAndNotOccluded()) {
1922 flags |= StatusBarManager.DISABLE_HOME | StatusBarManager.DISABLE_RECENT;
1923 }
1931 mStatusBarManager.disable(flags);
1932 }
1933 }
系统的systemui的颜色排除我们自己设定颜色外,系统是在窗口计算的过程中回去更新systemuiflag,主要在 PhoneWindowManager这个类中的 updateSystemUiVisibilityLw(…) 这其中还有flag的计算。
当你的窗口使用了toast这种东西,窗口生命周期会被toast延长,在窗口计算的过程中 windowstate 会在toast消失后消失。
窗口的flag public static final int FLAG_SLIPPERY = 0x20000000; 是允许move事件传递给隔壁老王窗口。
// frameworks/native/services/inputflinger/InputDispatcher.cpp
int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
const MotionEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
bool* outConflictingPointerActions) {
enum InjectionPermission {
INJECTION_PERMISSION_UNKNOWN,
INJECTION_PERMISSION_GRANTED,
INJECTION_PERMISSION_DENIED
};
...
...
// Check whether touches should slip outside of the current foreground window.
if (maskedAction == AMOTION_EVENT_ACTION_MOVE//一定是move事件。
&& entry->pointerCount == 1
&& mTempTouchState.isSlippery()) {//判断符合条件
//得到坐标值
int32_t x = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
int32_t y = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
//分别找到原配和小三
sp<InputWindowHandle> oldTouchedWindowHandle =
mTempTouchState.getFirstForegroundWindowHandle();
sp<InputWindowHandle> newTouchedWindowHandle =
findTouchedWindowAtLocked(displayId, x, y);
if (oldTouchedWindowHandle != newTouchedWindowHandle
&& newTouchedWindowHandle != NULL) {
#if DEBUG_FOCUS
ALOGD("Touch is slipping out of window %s into window %s.",
oldTouchedWindowHandle->getName().c_str(),
newTouchedWindowHandle->getName().c_str());
#endif
// Make a slippery exit from the old window.休了原配
mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, BitSet32(0));
// Make a slippery entrance into the new window.//小三进门考核
if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
isSplit = true;
}
int32_t targetFlags = InputTarget::FLAG_FOREGROUND
| InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER;
if (isSplit) {
targetFlags |= InputTarget::FLAG_SPLIT;
}
if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {
targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
}
BitSet32 pointerIds;
if (isSplit) {
pointerIds.markBit(entry->pointerProperties[0].id);
}
//小三进门
mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
}
}
....
sp InputDispatcher::findTouchedWindowAtLocked(int32_t displayId,
int32_t x, int32_t y) {
// Traverse windows from front to back to find touched window.
size_t numWindows = mWindowHandles.size();
for (size_t i = 0; i < numWindows; i++) {//遍历window集合
sp windowHandle = mWindowHandles.itemAt(i);
const InputWindowInfo* windowInfo = windowHandle->getInfo();
if (windowInfo->displayId == displayId) {
int32_t flags = windowInfo->layoutParamsFlags;
if (windowInfo->visible) {//不可见不参与
if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {//没有nottouchable属性
//如果是isTouchModal不管落没落在窗口上都给窗口,否则必须落在窗口上,不然就给下一层窗口。
bool isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
| InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
// Found window.
return windowHandle;
}
}
}
}
}
return NULL;
}
结合not_focusable和not_touch_modal我们可以自由定制窗口某个区域可点击,其他区域传下去。
英文可自己翻译
//home/dongchunyang/code_dcy/android_source/Q_r/frameworks/base/core/java/android/view/ViewTreeObserver.java
/**
* Option for {@link #setTouchableInsets(int)}: the entire window frame
* can be touched.
*/
public static final int TOUCHABLE_INSETS_FRAME = 0;
/**
* Option for {@link #setTouchableInsets(int)}: the area inside of
* the content insets can be touched.
*/
public static final int TOUCHABLE_INSETS_CONTENT = 1;
/**
* Option for {@link #setTouchableInsets(int)}: the area inside of
* the visible insets can be touched.
*/
public static final int TOUCHABLE_INSETS_VISIBLE = 2;
/**
* Option for {@link #setTouchableInsets(int)}: the area inside of
* the provided touchable region in {@link #touchableRegion} can be touched.
*/
@UnsupportedAppUsage
public static final int TOUCHABLE_INSETS_REGION = 3;
/**
* Set which parts of the window can be touched: either
* {@link #TOUCHABLE_INSETS_FRAME}, {@link #TOUCHABLE_INSETS_CONTENT},
* {@link #TOUCHABLE_INSETS_VISIBLE}, or {@link #TOUCHABLE_INSETS_REGION}.
*/
@UnsupportedAppUsage
public void setTouchableInsets(int val) {
mTouchableInsets = val;
}
上面第3条中有这么个判断
if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
//这个windowinfo 就是我们的window,而这个touchableRegion就是我们在java层给window设定好的信息。
bool InputWindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const {
return touchableRegion.contains(x,y);
}
看一下栗子
OnComputeInternalInsetsListener 是当视图树发生改变时调用的。
private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener = info -> {
// When the nav bar is in 2-button or 3-button mode, or when IME is visible in fully
// gestural mode, the entire nav bar should be touchable.
if (!isGesturalMode(mNavBarMode) || mImeVisible) {
info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);//触摸区域是整个窗口
return;
}
info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);//触摸区域取决于全局变量touchableRegion
ButtonDispatcher imeSwitchButton = getImeSwitchButton();
if (imeSwitchButton.getVisibility() == VISIBLE) {
// If the IME is not up, but the ime switch button is visible, then make sure that
// button is touchable
int[] loc = new int[2];
View buttonView = imeSwitchButton.getCurrentView();
buttonView.getLocationInWindow(loc);
info.touchableRegion.set(loc[0], loc[1], loc[0] + buttonView.getWidth(),
loc[1] + buttonView.getHeight());//修改区域。
return;
}
info.touchableRegion.setEmpty();//置空,全不可点
};
直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC
语法后生成一个完美的目录。
强调文本 强调文本
加粗文本 加粗文本
标记文本
删除文本
引用文本
H2O is是液体。
210 运算结果是 1024.
链接: link.
当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。
去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片
.
// An highlighted block
var foo = 'bar';
一个简单的表格是这么创建的:
项目 | Value |
---|---|
电脑 | $1600 |
手机 | $12 |
导管 | $1 |
使用:---------:
居中
使用:----------
居左
使用----------:
居右
第一列 | 第二列 | 第三列 |
---|---|---|
第一列文本居中 | 第二列文本居右 | 第三列文本居左 |
SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:
TYPE | ASCII | HTML |
---|---|---|
Single backticks | 'Isn't this fun?' |
‘Isn’t this fun?’ |
Quotes | "Isn't this fun?" |
“Isn’t this fun?” |
Dashes | -- is en-dash, --- is em-dash |
– is en-dash, — is em-dash |
一个具有注脚的文本。1
Markdown将文本转换为 HTML。
您可以使用渲染LaTeX数学表达式 KaTeX:
Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n−1)!∀n∈N 是通过欧拉积分
Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t   . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=∫0∞tz−1e−tdt.
你可以找到更多关于的信息 LaTeX 数学表达式here.
可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图::
这将产生一个流程图。:
我们依旧会支持flowchart的流程图:
如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。
如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。
注脚的解释 ↩︎