Make winscope compatible with flicker/CTS infra

Flickerlib is migrating to the same structure as the CTS tests. This cl
makes winscope compatible with this changes. It now parses traces with
the new information and add a "flickerObj" entry to each node with the
calculated vlaues from flickerlib

Test: yarn run dev and open a few WM traces
Bug: 167521440
Bug: 170372278
Change-Id: I4a116ccabe392c75e5b5049808d4837f865cb2c7
This commit is contained in:
Nataniel Borges
2020-10-15 12:22:03 +02:00
parent c1b2262941
commit 5e4ca356ae
19 changed files with 577 additions and 575 deletions

View File

@@ -252,7 +252,8 @@ export default {
this.item = item;
this.tree = this.generateTreeFromItem(item);
this.rects = [...item.rects].reverse();
const rects = item.rects //.toArray()
this.rects = [...rects].reverse();
this.bounds = item.bounds;
this.hierarchySelected = null;

View File

@@ -17,7 +17,7 @@
import { FILE_TYPES, DUMP_TYPES } from "@/decode.js";
import DumpBase from "./DumpBase";
import { WindowManagerTraceEntry } from '@/flickerlib';
import { WindowManagerTrace } from '@/flickerlib';
export default class WindowManager extends DumpBase {
wmDumpFile: any;
@@ -32,7 +32,7 @@ export default class WindowManager extends DumpBase {
return DUMP_TYPES.WINDOW_MANAGER;
}
static fromProto(proto): WindowManagerTraceEntry {
return WindowManagerTraceEntry.fromProto(proto);
static fromProto(proto): WindowManagerTrace {
return WindowManagerTrace.fromDump(proto);
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2020, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { nanosToString, TimeUnits } from "../utils/utils.js"
import { getWMPropertiesForDisplay } from './mixin'
import {
KeyguardControllerState,
RootWindowContainer,
WindowManagerPolicy,
WindowManagerState
} from "./common"
import WindowContainer from "./windows/WindowContainer"
WindowManagerState.fromProto = function ({proto, timestamp = 0, where = ""}): WindowManagerState {
var inputMethodWIndowAppToken = ""
if (proto.inputMethodWindow != null) {
proto.inputMethodWindow.hashCode.toString(16)
}
const rootWindowContainer = newRootWindowContainer(proto.rootWindowContainer)
const keyguardControllerState = newKeyguardControllerState(
proto.rootWindowContainer.keyguardController)
const entry = new WindowManagerState(
where,
newWindowManagerPolicy(proto.policy),
proto.focusedApp,
proto.focusedDisplayId,
proto.focusedWindow?.title ?? "",
inputMethodWIndowAppToken,
proto.rootWindowContainer.isHomeRecentsComponent,
proto.displayFrozen,
proto.rotation,
proto.lastOrientation,
proto.rootWindowContainer.pendingActivities.map(it => it.title),
rootWindowContainer,
keyguardControllerState,
timestamp = timestamp
)
entry.obj = getWMPropertiesForDisplay(proto)
entry.name = nanosToString(entry.timestamp, TimeUnits.MILLI_SECONDS)
entry.shortName = entry.name
entry.children = entry.root.childrenWindows.reverse()
entry.chips = []
entry.visible = true
return entry
}
function newWindowManagerPolicy(proto): WindowManagerPolicy {
return new WindowManagerPolicy(
proto.focusedAppToken || "",
proto.forceStatusBar,
proto.forceStatusBarFromKeyguard,
proto.keyguardDrawComplete,
proto.keyguardOccluded,
proto.keyguardOccludedChanged,
proto.keyguardOccludedPending,
proto.lastSystemUiFlags,
proto.orientation,
proto.rotation,
proto.rotationMode,
proto.screenOnFully,
proto.windowManagerDrawComplete
)
}
function newRootWindowContainer(proto): RootWindowContainer {
const windowContainer = WindowContainer.fromProto({proto: proto.windowContainer})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
}
const entry = new RootWindowContainer(windowContainer)
proto.windowContainer.children.reverse()
.map(it => WindowContainer.childrenFromProto(entry, it, /* isActivityInTree */ false))
.filter(it => it != null)
.forEach(it => windowContainer.childContainers.push(it))
return entry
}
function newKeyguardControllerState(proto): KeyguardControllerState {
const keyguardOccludedStates = {}
if (proto) {
proto.keyguardOccludedStates.forEach(it =>
keyguardOccludedStates[it.displayId] = it.keyguardOccluded)
}
return new KeyguardControllerState(
proto?.aodShowing ?? false,
proto?.keyguardShowing ?? false,
keyguardOccludedStates
)
}
export default WindowManagerState;

View File

@@ -14,23 +14,26 @@
* limitations under the License.
*/
import {
WindowManagerTrace,
} from "./common"
import WindowManagerTraceEntry from "./WindowManagerTraceEntry"
import { WindowManagerTrace } from "./common"
import WindowManagerState from "./WindowManagerState"
WindowManagerTrace.fromProto = function (proto) {
const entries = []
for (const entryProto of proto.entry) {
const transformedEntry = WindowManagerTraceEntry.fromProto(
entryProto.windowManagerService, entryProto.elapsedRealtimeNanos)
const transformedEntry = WindowManagerState.fromProto({
proto: entryProto.windowManagerService,
timestamp: entryProto.elapsedRealtimeNanos,
where: entryProto.where})
entries.push(transformedEntry)
}
const source = null;
const sourceChecksum = null;
}
const source = null
const sourceChecksum = null
return new WindowManagerTrace(entries, source, sourceChecksum)
}
WindowManagerTrace.fromDump = function(proto): WindowManagerTrace {
return WindowManagerState.fromProto({proto: proto})
}
export default WindowManagerTrace;

View File

@@ -1,113 +0,0 @@
/*
* Copyright 2020, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { nanosToString, TimeUnits } from "../utils/utils.js"
import { WindowManagerTraceEntry } from "./common"
import { applyMixins } from "./mixin"
import ITreeViewElement from './treeview/IClickableTreeViewElement'
import IClickableTreeViewElement from './treeview/IClickableTreeViewElement'
import Chip from './treeview/Chip'
import WindowContainer from "./windows/WindowContainer"
class WindowManagerTraceEntryMixin implements IClickableTreeViewElement {
common: any
kind: string
name: string
shortName: string
stableId: string
chips: Array<Chip>
children: Array<ITreeViewElement>
obj: any
rawTreeViewObject
timestamp: number
rootWindow
mixinConstructor(obj) {
const name = this.timestamp ? nanosToString(this.timestamp, TimeUnits.MILLI_SECONDS) : ""
this.kind = "entry"
this.name = name
this.shortName = name
this.stableId = "entry"
this.children = this.rootWindow.children
this.chips = []
this.obj = obj
}
static fromProto(proto, timestamp) {
const rootWindow =
WindowContainer.fromProto(proto.rootWindowContainer.windowContainer, null)
const windowManagerTraceEntry =
new WindowManagerTraceEntry(rootWindow, timestamp)
windowManagerTraceEntry.kind = 'service'
windowManagerTraceEntry.focusedApp = proto.focusedApp
windowManagerTraceEntry.focusedDisplayId = proto.focusedDisplayId
windowManagerTraceEntry.lastOrientation = proto.lastOrientation
windowManagerTraceEntry.policy = proto.policy
windowManagerTraceEntry.rotation = proto.rotation
windowManagerTraceEntry.displayFrozen = proto.displayFrozen
windowManagerTraceEntry.inputMethodWindow = proto.inputMethodWindow
// Remove anything that is part of the children elements
// allows for faster loading of properties and less information cluttering
// this applied to anywhere the proto is passed to be saved as .obj
const obj = Object.assign({}, proto)
obj.rootWindowContainer = {};
Object.assign(obj.rootWindowContainer,
proto.rootWindowContainer)
obj.rootWindowContainer.windowContainer = {};
Object.assign(obj.rootWindowContainer.windowContainer,
proto.rootWindowContainer.windowContainer)
delete obj.rootWindowContainer.windowContainer.children
windowManagerTraceEntry.mixinConstructor(obj)
return windowManagerTraceEntry
}
attachObject(obj) {
this.obj = obj
}
asRawTreeViewObject() {
// IMPORTANT: We want to always return the same tree view object and not
// generate a new one every time this function is called.
if (!this.rawTreeViewObject) {
const children = this.children.map(child => child.asRawTreeViewObject())
this.rawTreeViewObject = {
kind: this.kind,
name: this.name,
shortName: this.shortName,
stableId: this.stableId,
chips: this.chips,
obj: this.obj,
children,
ref: this,
}
}
return this.rawTreeViewObject;
}
}
applyMixins(WindowManagerTraceEntry, [WindowManagerTraceEntryMixin])
export default WindowManagerTraceEntry;

View File

@@ -17,42 +17,69 @@
// Imports all the compiled common Flicker library classes and exports them
// as clean es6 modules rather than having them be commonjs modules
const WindowManagerTrace = require('flicker').com.android.server.wm.flicker
.common.traces.windowmanager.WindowManagerTrace;
const WindowManagerTraceEntry = require('flicker').com.android.server.wm.
flicker.common.traces.windowmanager.WindowManagerTraceEntry;
const WindowManagerTrace = require('flicker').com.android.server.wm.flicker.
common.traces.windowmanager.WindowManagerTrace;
const WindowManagerState = require('flicker').com.android.server.wm.
flicker.common.traces.windowmanager.WindowManagerState;
const WindowContainer = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.WindowContainer;
const WindowState = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.WindowState;
const DisplayContent = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.DisplayContent;
const ActivityRecord = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.ActivityRecord;
const WindowToken = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.WindowToken;
const Activity = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.Activity;
const ActivityTask = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.ActivityTask;
const Configuration = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.Configuration;
const ConfigurationContainer = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.ConfigurationContainer;
const DisplayArea = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.DisplayArea;
const RootDisplayArea = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.RootDisplayArea;
const Task = require('flicker').com.android.server.wm.flicker.common.traces.
windowmanager.windows.Task;
const DisplayContent = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.DisplayContent;
const KeyguardControllerState = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.KeyguardControllerState;
const RootWindowContainer = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.RootWindowContainer;
const WindowConfiguration = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.WindowConfiguration;
const WindowContainer = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.WindowContainer;
const WindowContainerChild = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.WindowContainerChild;
const WindowManagerPolicy = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.WindowManagerPolicy;
const WindowState = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.WindowState;
const WindowToken = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.WindowToken;
const Rect = require('flicker').com.android.server.wm.flicker.common.Rect;
const Bounds = require('flicker').com.android.server.wm.flicker.common.Bounds;
function toRect(proto) {
if (proto == null) {
return new Rect(0, 0, 0, 0)
} else {
return new Rect(proto.left, proto.top, proto.right, proto.bottom)
}
}
export {
WindowManagerTrace,
WindowManagerTraceEntry,
WindowContainer,
WindowState,
DisplayContent,
ActivityRecord,
WindowToken,
Activity,
ActivityTask,
Configuration,
ConfigurationContainer,
DisplayArea,
RootDisplayArea,
Task,
DisplayContent,
KeyguardControllerState,
RootWindowContainer,
WindowConfiguration,
WindowContainer,
WindowContainerChild,
WindowState,
WindowToken,
WindowManagerPolicy,
WindowManagerTrace,
WindowManagerState,
Rect,
Bounds,
toRect
};

View File

@@ -14,12 +14,12 @@
* limitations under the License.
*/
import WindowManagerTraceEntry from './WindowManagerTraceEntry';
import WindowManagerState from './WindowManagerState';
import WindowManagerTrace from './WindowManagerTrace';
/**
* Entry point into the flickerlib for Winscope.
* Expose everything we want Winscope to have access to here.
*/
export {WindowManagerTraceEntry, WindowManagerTrace};
export {WindowManagerState, WindowManagerTrace};

View File

@@ -1,23 +1,44 @@
/**
* Injects all the properties (getters, setters, functions...) of a list of
* classes (baseCtors) into a class (derivedCtor).
* @param derivedCtor The constructor of the class we want to inject the
* properties into.
* @param baseCtors A list of consturctor of the classes we want to mixin the
* properties of into the derivedCtor.
/*
* Copyright 2020, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor).forEach(name => {
if (['length', 'name', 'prototype'].includes(name)) {
return;
}
Object.defineProperty(derivedCtor, name, Object.getOwnPropertyDescriptor(baseCtor, name))
})
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
Object.defineProperty(derivedCtor.prototype, name, Object.getOwnPropertyDescriptor(baseCtor.prototype, name))
})
});
/**
* Get the properties of a WM object for display.
*
* @param entry WM hierarchy element
* @param proto Associated proto object
*/
export function getWMPropertiesForDisplay(proto: any): any {
const obj = Object.assign({}, proto)
if (obj.children) delete obj.children
if (obj.childWindows) delete obj.childWindows
if (obj.childrenWindows) delete obj.childrenWindows
if (obj.childContainers) delete obj.childContainers
if (obj.identifier) delete obj.identifier
if (obj.windowToken) delete obj.windowToken
if (obj.rootDisplayArea) delete obj.rootDisplayArea
if (obj.rootWindowContainer) delete obj.rootWindowContainer
if (obj.windowContainer) delete obj.windowContainer
return obj
}
export function shortenName(name: any): string {
const classParts = (name + "").split(".")
if (classParts.length <= 3) {
return name
}
const className = classParts.slice(-1)[0] // last element
return `${classParts[0]}.${classParts[1]}.(...).${className}`
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2020, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { getWMPropertiesForDisplay, shortenName } from '../mixin'
import { Activity } from "../common"
import WindowContainer from "./WindowContainer"
Activity.fromProto = function (proto, parent: WindowContainer): Activity {
if (proto == null) {
return null
} else {
const windowContainer = WindowContainer.fromProto({proto: proto.windowToken.windowContainer,
identifierOverride: proto.identifier})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
}
const entry = new Activity(
proto.name,
proto.state,
proto.visible,
proto.frontOfTask,
proto.procId,
proto.translucent,
parent,
windowContainer
)
proto.windowToken.windowContainer.children.reverse()
.map(it => WindowContainer.childrenFromProto(entry, it, /* isActivityInTree */ true))
.filter(it => it != null)
.forEach(it => windowContainer.childContainers.push(it))
entry.obj = getWMPropertiesForDisplay(proto)
entry.shortName = shortenName(entry.name)
entry.children = entry.childrenWindows
return entry
}
}
export default Activity

View File

@@ -1,46 +0,0 @@
/*
* Copyright 2020, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
ActivityRecord,
} from "../common"
import { applyMixins } from '../mixin'
import WindowToken from "./WindowToken"
export class ActivityRecordMixin {
get kind() {
return "DisplayArea"
}
static fromProto(proto) {
const windowToken = WindowToken.fromProto(proto.windowToken)
const activityRecord = new ActivityRecord(windowToken)
const obj = Object.assign({}, proto)
delete proto.windowToken
Object.assign(obj, windowToken.obj)
activityRecord.attachObject(obj)
return activityRecord
}
}
applyMixins(ActivityRecord, [ActivityRecordMixin])
export default ActivityRecord

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2020, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { getWMPropertiesForDisplay, shortenName } from '../mixin'
import { ActivityTask, toRect } from "../common"
import WindowContainer from "./WindowContainer"
ActivityTask.fromProto = function (proto, isActivityInTree: Boolean): ActivityTask {
if (proto == null) {
return null
} else {
const windowContainer = WindowContainer.fromProto({proto: proto.windowContainer})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
}
const entry = new ActivityTask(
proto.activityType,
proto.fillsParent,
toRect(proto.bounds),
proto.id,
proto.rootTaskId,
proto.displayId,
toRect(proto.lastNonFullscreenBounds),
proto.realActivity,
proto.origActivity,
proto.resizeMode,
proto.resumedActivity?.title ?? "",
proto.animatingBounds,
proto.surfaceWidth,
proto.surfaceHeight,
proto.createdByOrganizer,
proto.minWidth,
proto.minHeight,
windowContainer
)
proto.windowContainer.children.reverse()
.map(it => WindowContainer.childrenFromProto(entry, it, isActivityInTree))
.filter(it => it != null)
.forEach(it => windowContainer.childContainers.push(it))
entry.obj = getWMPropertiesForDisplay(proto)
entry.shortName = shortenName(entry.name)
entry.children = entry.childrenWindows
return entry
}
}
export default ActivityTask

View File

@@ -14,34 +14,30 @@
* limitations under the License.
*/
import {
DisplayArea,
} from "../common"
import { applyMixins } from '../mixin'
import { getWMPropertiesForDisplay, shortenName } from '../mixin'
import { DisplayArea } from "../common"
import WindowContainer from "./WindowContainer"
export class DisplayAreaMixin {
get kind() {
return "DisplayArea"
DisplayArea.fromProto = function (proto, isActivityInTree: Boolean): DisplayArea {
if (proto == null) {
return null
} else {
const windowContainer = WindowContainer.fromProto({proto: proto.windowContainer, nameOverride: proto.name})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
}
const entry = new DisplayArea(proto.isTaskDisplayArea, windowContainer)
static fromProto(proto) {
const windowContainer = WindowContainer.fromProto(proto.windowContainer,
null)
proto.windowContainer.children.reverse()
.map(it => WindowContainer.childrenFromProto(entry, it, isActivityInTree))
.filter(it => it != null)
.forEach(it => windowContainer.childContainers.push(it))
const displayArea = new DisplayArea(windowContainer)
const obj = Object.assign({}, proto)
delete obj.windowContainer
Object.assign(obj, windowContainer.obj)
displayArea.attachObject(obj)
return displayArea
entry.obj = getWMPropertiesForDisplay(proto)
entry.shortName = shortenName(entry.name)
entry.children = entry.childrenWindows
return entry
}
}
applyMixins(DisplayArea, [DisplayAreaMixin])
export default DisplayArea

View File

@@ -14,50 +14,57 @@
* limitations under the License.
*/
import {
DisplayContent,
Bounds
} from "../common"
import { applyMixins } from '../mixin'
import { getWMPropertiesForDisplay, shortenName } from '../mixin'
import { toRect, DisplayContent, Rect } from "../common"
import WindowContainer from "./WindowContainer"
import DisplayArea from "./DisplayArea"
export class DisplayContentMixin {
get kind() {
return "DisplayContent"
}
static fromProto(proto) {
let rootDisplayArea;
if (proto.rootDisplayArea.windowContainer == null) {
// For backward compatibility
const windowContainer = WindowContainer.fromProto(proto.windowContainer,
null)
rootDisplayArea = new DisplayArea(windowContainer)
DisplayContent.fromProto = function (proto, isActivityInTree: Boolean): DisplayContent {
if (proto == null) {
return null
} else {
// New protos should always be using this
rootDisplayArea = DisplayArea.fromProto(proto.rootDisplayArea)
const windowContainer = WindowContainer.fromProto({proto: proto.rootDisplayArea.windowContainer,
nameOverride: proto.displayInfo?.name ?? null})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
}
const bounds = new Bounds(
proto.displayInfo.logicalWidth || 0,
proto.displayInfo.logicalHeight || 0,
const displayRectWidth = proto.displayInfo?.logicalWidth ?? 0
const displayRectHeight = proto.displayInfo?.logicalHeight ?? 0
const appRectWidth = proto.displayInfo?.appWidth ?? 0
const appRectHeight = proto.displayInfo?.appHeight ?? 0
const defaultBounds = proto.pinnedStackController?.defaultBounds ?? null
const movementBounds = proto.pinnedStackController?.movementBounds ?? null
const entry = new DisplayContent(
proto.id,
proto.focusedRootTaskId,
proto.resumedActivity?.title ?? "",
proto.singleTaskInstance,
toRect(defaultBounds),
toRect(movementBounds),
new Rect(0, 0, displayRectWidth, displayRectHeight),
new Rect(0, 0, appRectWidth, appRectHeight),
proto.dpi,
proto.displayInfo?.flags ?? 0,
toRect(proto.displayFrames.stableBounds),
proto.surfaceSize,
proto.focusedApp,
proto.appTransition?.lastUsedAppTransition ?? "",
proto.appTransition?.appTransitionState ?? "",
windowContainer
)
const displayContent = new DisplayContent(rootDisplayArea, bounds)
proto.rootDisplayArea.windowContainer.children.reverse()
.map(it => WindowContainer.childrenFromProto(entry, it, isActivityInTree))
.filter(it => it != null)
.forEach(it => windowContainer.childContainers.push(it))
const obj = Object.assign({}, proto)
delete obj.windowContainer
delete obj.rootDisplayArea
Object.assign(obj, rootDisplayArea.obj)
displayContent.attachObject(obj)
return displayContent
entry.obj = getWMPropertiesForDisplay(proto)
entry.shortName = shortenName(entry.name)
entry.children = entry.childrenWindows
return entry
}
}
applyMixins(DisplayContent, [DisplayContentMixin])
export default DisplayContent

View File

@@ -1,22 +0,0 @@
/*
* Copyright 2020, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { RootDisplayArea } from "../common"
import DisplayArea from "./DisplayArea"
RootDisplayArea.fromProto = DisplayArea.fromProto
export default DisplayArea

View File

@@ -1,45 +0,0 @@
/*
* Copyright 2020, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Task } from "../common"
import { applyMixins } from '../mixin'
import WindowContainer from "./WindowContainer"
export class TaskMixin {
get kind() {
return "Task"
}
static fromProto(proto) {
const windowContainer = WindowContainer.fromProto(proto.windowContainer,
null)
const task = new Task(windowContainer)
const obj = Object.assign({}, proto)
delete obj.windowContainer
Object.assign(obj, windowContainer.obj)
task.attachObject(obj)
return task
}
}
applyMixins(Task, [TaskMixin])
export default Task

View File

@@ -14,192 +14,105 @@
* limitations under the License.
*/
import { WindowContainer } from "../common"
import { getWMPropertiesForDisplay, shortenName } from '../mixin'
import { applyMixins } from '../mixin'
import IClickableTreeViewElement from '../treeview/IClickableTreeViewElement'
import { TreeViewObject } from '../treeview/types'
import {
Configuration,
ConfigurationContainer,
toRect,
WindowConfiguration,
WindowContainer,
WindowContainerChild,
} from "../common"
import Activity from "./Activity"
import DisplayArea from "./DisplayArea"
import DisplayContent from "./DisplayContent"
import Task from "./Task"
import ActivityRecord from "./ActivityRecord"
import WindowToken from "./WindowToken"
import ActivityTask from "./ActivityTask"
import WindowState from "./WindowState"
import WindowToken from "./WindowToken"
import { CompatibleFeatures } from '@/utils/compatibility.js'
export class WindowContainerMixin implements IClickableTreeViewElement {
title: string
windowHashCode: number
childrenWindows
visible: boolean
rawTreeViewObject
_obj: { name: string }
_children
get kind() {
return "WindowContainer"
}
get name() {
if (this.obj && this.obj.name) {
return this.obj.name
}
if (["WindowContainer", "Task"].includes(this.title)) {
WindowContainer.fromProto = function ({
proto,
nameOverride = null,
identifierOverride = null,
tokenOverride = null
}): WindowContainer {
if (proto == null) {
return null
}
} else {
const identifier = identifierOverride ?? proto.identifier
var name = nameOverride ?? identifier?.title ?? ""
var token = tokenOverride?.toString(16) ?? identifier?.hashCode?.toString(16) ?? ""
return `${removeRedundancyInName(this.title)}@${this.windowHashCode}`
}
const entry = new WindowContainer(
name,
token,
proto.orientation,
proto.visible,
newConfigurationContainer(proto.configurationContainer)
)
get shortName() {
return this.name ? shortenName(this.name) : null
}
get stableId() {
return this.windowHashCode
}
get children() {
return this._children ?? this.childrenWindows
}
set children(children) {
this._children = children
}
get chips() {
return []
}
set obj(obj) {
this._obj = obj
}
get obj() {
return this._obj
}
static fromProto(proto, parent_identifier) {
const children = proto.children.map(
child => transformWindowContainerChildProto(child))
// A kind of hacky way to check if the proto is from a device which
// didn't have the changes required for the diff viewer to work properly
// We required that all elements have a stable identifier in order to do the
// diff properly. In theory properties diff would still work, but are
// currently disabled. If required in the future don't hesitate to make
// the required changes.
if (!proto.identifier) {
CompatibleFeatures.DiffVisualization = false;
}
const fallback_title = parent_identifier?.title ?? ""
const fallback_hashCode = parent_identifier?.hashCode ?? ""
const title = proto.identifier?.title ?? fallback_title
const hashCode = proto.identifier?.hashCode ?? fallback_hashCode
const visible = proto.visible
const windowContainer =
new WindowContainer(children, title, hashCode, visible)
const obj = Object.assign({}, proto)
// we remove the children property from the object to avoid it showing the
// the properties view of the element as we can always see those elements'
// properties by changing the target element in the hierarchy tree view.
delete obj.children
windowContainer.attachObject(obj)
return windowContainer
}
attachObject(obj) {
this._obj = obj
}
asRawTreeViewObject(): TreeViewObject {
// IMPORTANT: We want to always return the same tree view object and not
// generate a new one every time this function is called.
if (!this.rawTreeViewObject) {
const children = this.children.map(child => child.asRawTreeViewObject())
this.rawTreeViewObject = {
kind: this.kind,
name: this.name,
shortName: this.shortName,
stableId: this.stableId,
chips: this.chips,
obj: this.obj,
children,
ref: this,
};
}
return this.rawTreeViewObject;
entry.obj = getWMPropertiesForDisplay(proto)
entry.shortName = shortenName(entry.name)
return entry
}
}
applyMixins(WindowContainer, [WindowContainerMixin])
function transformWindowContainerChildProto(proto) {
if (proto.displayArea != null) {
return DisplayArea.fromProto(proto.displayArea)
}
if (proto.displayContent != null) {
return DisplayContent.fromProto(proto.displayContent)
}
if (proto.task != null) {
return Task.fromProto(proto.task)
}
if (proto.activity != null) {
return ActivityRecord.fromProto(proto.activity)
}
if (proto.windowToken != null) {
return WindowToken.fromProto(proto.windowToken)
}
if (proto.window != null) {
return WindowState.fromProto(proto.window)
}
throw new Error("Unhandled WindowContainerChildProto case...")
WindowContainer.childrenFromProto = function(parent: WindowContainer, proto, isActivityInTree: Boolean): WindowContainerChild {
return new WindowContainerChild(
DisplayContent.fromProto(proto.displayContent, isActivityInTree),
DisplayArea.fromProto(proto.displayArea, isActivityInTree),
ActivityTask.fromProto(proto.task, isActivityInTree),
Activity.fromProto(proto.activity, parent),
WindowToken.fromProto(proto.windowToken, isActivityInTree),
WindowState.fromProto(proto.window, isActivityInTree),
WindowContainer.fromProto({proto: proto.windowContainer})
)
}
function removeRedundancyInName(name: string): string {
if (!name.includes('/')) {
return name
}
function newConfigurationContainer(proto): ConfigurationContainer {
const entry = new ConfigurationContainer(
newConfiguration(proto?.overrideConfiguration ?? null),
newConfiguration(proto?.fullConfiguration ?? null),
newConfiguration(proto?.mergedOverrideConfiguration ?? null)
)
const split = name.split('/')
const pkg = split[0]
var clazz = split.slice(1).join("/")
if (clazz.startsWith("$pkg.")) {
clazz = clazz.slice(pkg.length + 1)
return "$pkg/$clazz"
}
return name
entry.kind = "ConfigurationContainer"
entry.obj = entry
return entry
}
function shortenName(name: string): string {
const classParts = name.split(".")
function newConfiguration(proto): Configuration {
var windowConfiguration = null
if (classParts.length <= 3) {
return name
if (proto != null && proto.windowConfiguration != null) {
windowConfiguration = newWindowConfiguration(proto.windowConfiguration)
}
const className = classParts.slice(-1)[0] // last element
return new Configuration(
windowConfiguration,
proto?.densityDpi ?? 0,
proto?.orientation ?? 0,
proto?.screenHeightDp ?? 0,
proto?.screenHeightDp ?? 0,
proto?.smallestScreenWidthDp ?? 0,
proto?.screenLayout ?? 0,
proto?.uiMode ?? 0
)
}
return `${classParts[0]}.${classParts[1]}.(...).${className}`
function newWindowConfiguration(proto): WindowConfiguration {
return new WindowConfiguration(
toRect(proto.appBounds),
toRect(proto.bounds),
toRect(proto.maxBounds),
proto.windowingMode,
proto.activityType
)
}
export default WindowContainer

View File

@@ -14,43 +14,75 @@
* limitations under the License.
*/
import { WindowState, Rect } from "../common"
import { getWMPropertiesForDisplay, shortenName } from '../mixin'
import { toRect, WindowState } from "../common"
import { VISIBLE_CHIP } from '../treeview/Chips'
import { applyMixins } from '../mixin'
import WindowContainer from "./WindowContainer"
export class WindowStateMixin {
visible: boolean
get kind() {
return "WindowState"
WindowState.fromProto = function (proto, isActivityInTree: Boolean): WindowState {
if (proto == null) {
return null
} else {
const identifierName = proto.windowContainer.identifier?.title ?? proto.identifier?.title ?? ""
var windowType = 0
if (identifierName.startsWith(WindowState.STARTING_WINDOW_PREFIX)) {
windowType = WindowState.WINDOW_TYPE_STARTING
} else if (proto.animatingExit) {
windowType = WindowState.WINDOW_TYPE_EXITING
} else if (identifierName.startsWith(WindowState.DEBUGGER_WINDOW_PREFIX)) {
windowType = WindowState.WINDOW_TYPE_STARTING
}
static fromProto(proto) {
const windowContainer = WindowContainer.fromProto(proto.windowContainer,
proto.identifier)
var nameOverride = identifierName
const frame = (proto.windowFrames ?? proto).frame ?? {}
const rect = new Rect(frame.left ?? 0, frame.top ?? 0, frame.right ?? 0, frame.bottom ?? 0)
const windowState =
new WindowState(windowContainer, /* childWindows */[], rect)
const obj = Object.assign({}, proto)
Object.assign(obj, windowContainer.obj)
delete obj.windowContainer
windowState.attachObject(obj)
return windowState
if (identifierName.startsWith(WindowState.STARTING_WINDOW_PREFIX)) {
nameOverride = identifierName.substring(WindowState.STARTING_WINDOW_PREFIX.length)
} else if (identifierName.startsWith(WindowState.DEBUGGER_WINDOW_PREFIX)) {
nameOverride = identifierName.substring(WindowState.DEBUGGER_WINDOW_PREFIX.length)
}
get chips() {
return this.visible ? [VISIBLE_CHIP] : []
const windowContainer = WindowContainer.fromProto({
proto: proto.windowContainer,
nameOverride: nameOverride,
identifierOverride: proto.identifier})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
}
proto.windowContainer.children.reverse()
.map(it => WindowContainer.childrenFromProto(entry, it, isActivityInTree))
.filter(it => it != null)
.forEach(it => windowContainer.childContainers.push(it))
const entry = new WindowState(
proto.attributes?.type ?? 0,
proto.displayId,
proto.stackId,
proto.animator?.surface?.layer ?? 0,
proto.animator?.surface?.shown ?? false,
windowType,
toRect(proto.windowFrames?.frame ?? null),
toRect(proto.windowFrames?.containingFrame ?? null),
toRect(proto.windowFrames?.parentFrame ?? null),
toRect(proto.windowFrames?.contentFrame ?? null),
toRect(proto.windowFrames?.contentInsets ?? null),
toRect(proto.surfaceInsets),
toRect(proto.givenContentInsets),
toRect(proto.animator?.lastClipRect ?? null),
windowContainer,
/* isAppWindow */ isActivityInTree
)
entry.obj = getWMPropertiesForDisplay(proto)
entry.shortName = shortenName(entry.name)
entry.visible = entry.isVisible ?? false
entry.chips = entry.isVisible ? [VISIBLE_CHIP] : []
entry.children = entry.childrenWindows
if (entry.isVisible) {
entry.rect = entry.rects[0]
}
return entry
}
}
applyMixins(WindowState, [WindowStateMixin])
export default WindowState

View File

@@ -14,32 +14,30 @@
* limitations under the License.
*/
import { getWMPropertiesForDisplay, shortenName } from '../mixin'
import { WindowToken } from "../common"
import { applyMixins } from '../mixin'
import WindowContainer from "./WindowContainer"
export class WindowContainerMixin {
get kind() {
return "WindowToken"
WindowToken.fromProto = function (proto, isActivityInTree: Boolean): WindowToken {
if (proto == null) {
return null
} else {
const windowContainer = WindowContainer.fromProto({proto: proto.windowContainer, tokenOverride: proto.hashCode})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
}
const entry = new WindowToken(windowContainer)
static fromProto(proto) {
const windowContainer = WindowContainer.fromProto(proto.windowContainer,
null)
proto.windowContainer.children.reverse()
.map(it => WindowContainer.childrenFromProto(entry, it, isActivityInTree))
.filter(it => it != null)
.forEach(it => windowContainer.childContainers.push(it))
const windowToken = new WindowToken(windowContainer)
const obj = Object.assign({}, proto)
Object.assign(obj, windowContainer.obj)
delete obj.windowContainer
windowToken.attachObject(obj)
return windowToken
entry.obj = getWMPropertiesForDisplay(proto)
entry.shortName = shortenName(entry.name)
entry.children = entry.childrenWindows
return entry
}
}
applyMixins(WindowToken, [WindowContainerMixin])
export default WindowToken

View File

@@ -373,6 +373,11 @@ function getIntFlagsAsStrings(intFlags, annotationType) {
const mapping = intDefMapping[annotationType].values;
if (mapping.length == 0) {
console.warn("No mapping for type", annotationType)
return intFlags + ""
}
// Will only contain bits that have not been associated with a flag.
let leftOver = intFlags;