# 一.插件的概念
# 1.背景
原生 maml 所有代码都写在 manifest.xml 一个文件中,无模块化封装的概念,对于大型项目代码可读性可维护性都较差。主题编辑器 2.0 时引入了插件 1.0 系统,通过封装 maml 代码为一个个插件,结合编辑器点击的交互操作,可以将不同插件内部的逻辑以搭积木的方式最终编译到 manifest.xml 文件中,并生成原生 maml 可识别的代码。主题编辑器 3.0 项目需要对插件进行更符合设计师交互行为的可视化的操作,因此我们开发了新的 maml 插件 2.0 系统
# 2.编辑器的插件系统定义
是原生maml的超集
支持lockscreen、miwallpaper、clock_2x4等编辑器支持的百变框架的模块
主文件是和manifest.xml同级的main.xml,最终会编译成manifest.xml
拓展的语法在经过编辑器编译成manifest.xml之前并不能直接被手机识别
可以对maml模块的组件进行更高级的封装,能更清晰的组织代码的逻辑及复用组件
# 二、基本语法及使用
# 1.插件的声明
<?xml version="1.0" encoding="utf-8"?>
<Template>
<Props>
<item name="x" default="100" type="number" description="x坐标"/>
<item name="y" default ="200" type="number" description="y坐标"/>
<item name="text" default ="hello world" type="string" description="y坐标"/>
<item name="imgSrc" default ="a.png" type="bitmap" description="示例图片"/>
<item name="alpha" default ="255" type="number" description="图片透明度"/>
</Props>
<Text x="$x" y="$y" color="#ffffff" size="30" text="$text"/>
<Image y="800" src="$imgSrc" alpha="$alpha" />
</Template>
定义一个 Props 的标签,用于接收外部参数的接口,Props 标签内包含的 item 中每条数据设定一个 default 的默认值,用于外部参数少传时的默认参数。Props 标签中定义的数据通过【$】符号绑定到下面的元素对象中。type 与 description 属性为插件与编辑器交互的字段,编辑器通过不同的 type 类型在界面展示不同的输入样式。比如 number 类型对应一个输入框,color 类型对应一个颜色选择器
Props 标签非必须,看需求是否需要个性化定义该插件来决定要不要定义 Props 参数
# 2.插件的使用
假设上面定义的插件放置在 maml 模块 modules 目录下的 plugin1.xml 中,我们可以在主题包对应的模块 main.xml 文件中写入如下代码引用该插件,如不特殊说明,下面所有的代码都默认在 main 的 xml 通过指定src="modules/xxxx"
的方式引入,xxxx 为封装的模块的文件名(当然插件内部依然可以嵌套引入其他 Plugin,这里不做说明)
<?xml version="1.0" encoding="utf-8"?>
<Lockscreen version="1" frameRate="60" compiler="true">
<Plugin name="hello" src="modules/plugin1" y="500" text="foo" />
</Lockscreen>
注:插件在使用时,src 指定插件的路径,一般为文件的名称(无需后缀名),通常情况下还需要声明一个 name 属性,方便其他插件获取该插件的状态.其他属性则为声明插件时 Props 标签内定义的 name 属性绑定的参数,当然 Props 标签内未定义的只要是原生 Group 标签支持的属性也可以写,非 Props 定义的属性作用于整个插件包裹的内部代码,Props 定义的属性则作用于 Props 属性绑定的插件内部具体的位置上,若 props 中定了某属性,但使用时未传该属性,则按照 Props 中 default 的值作为默认值。
编译后生成的 manifest.xml 的代码如下
<?xml version="1.0" encoding="utf-8"?>
<Lockscreen version="1" frameRate="60" >
<Group name="hello">
<Text x="100" y="500" color="#ffffff" size="30" text="foo"/>
<Image y="800" src="a.png" alpha="255" />
</Group>
</Lockscreen>
如上,除了 y 和 text 属性为调用插件传入的值外,生成代码的其余属性均为默认值,插件内定义的代码都被包裹在一个 Group 中,方便后续对该插件的整体操作(添加动画、添加事件等)
# 三、作用域
# 1.插件中作用域的概念
原生 maml 所有变量都是全局变量,无作用域的概念,声明的变量会污染全局。插件 2.0 系统结合业务需求,将作用域的范围限定为单个插件,实现方式为插件内所有的元素在编译后都会在原有 name 后统一加一个 6 位的随机字符以私有化该对象,插件每次添加时都会生成不同的私有化名称映射表,并私有化内部元素名称,以解决不同插件及插件多次调用内命名空间可能冲突的问题。同时外部无法直接访问插件内部的变量。
如下定义一个插件
<?xml version="1.0" encoding="utf-8"?>
<Template>
<Props>
<item name="y" default ="200" type="number" description="y坐标"/>
</Props>
<Var name="text_x" expression="300"/>
<Text name="text" x="#text_x" y="$y" color="#ffffff" size="30" text="foo"/>
<Button w="1080" h="2160">
<Triggers>
<Trigger action="up">
<VariableCommand name="text_x" expression="500"/>
</Trigger>
</Triggers>
</Button>
</Template>
引用这个插件后编译完的代码如下:
<Group name="hello">
<Var name="text_x_48utrd" expression="300"/>
<Text name="text_48utrd" x="#text_x_48utrd" y="200" color="#ffffff" size="30" text="foo"/>
<Button w="1080" h="2160">
<Triggers>
<Trigger action="up">
<VariableCommand name="text_x_48utrd" expression="500"/>
</Trigger>
</Triggers>
</Button>
</Group >
编译完后除以下特殊元素外其余所有带 name 和 target 的属性都会自动重命名,避免多个插件内部变量名互相冲突
不重命名的特殊元素
<!-- 音乐封面 -->
<Image name="music_album_cover"/>
<!-- 音乐控件下一首按钮 -->
<Button name="music_next"/>
<!-- 音乐控件暂停按钮 -->
<Button name="music_pause"/>
<!-- 音乐控件播放按钮 -->
<Button name="music_play"/>
<!-- 音乐控件上一首按钮 -->
<Button name="music_prev"/>
<!-- 点击后打开时钟app按钮 -->
<Button name="clock_button"/>
<!-- Extra 标签不会重命名 -->
<Extra name="" type="" expression="" />
# 四、插槽
# 1.标准插槽
在使用插件时可以通过<Slots/>
标签设置插槽给插件让外部使用,外部可以向插件内部添加自定义的代码,如
<Template>
<Props>
<item name="x" default="0" type="number" description="x坐标"/>
<item name="y" default="0" type="number" description="y坐标"/>
</Props>
<Group x="$x-1080" y="$y">
<Slots slotName="负一屏"/>
</Group>
<Group x="$x" y="$y">
<Slots slotName="正一屏"/>
</Group>
</Template>
使用插槽 main.xml 中使用如下代码
<?xml version="1.0" encoding="utf-8"?>
<Lockscreen version="1" frameRate="60" screenWidth="1080" compiler="true">
<Plugin src="modules/mgroup" name="group1" x="300" y="100">
<Slot>
<Text color="#ffffff" size="50" textExp="'我是负一屏'"/>
</Slot>
<Slot>
<Text color="#ffffff" size="50" textExp="'我是正一屏'"/>
</Slot>
</Plugin >
</Lockscreen>
生成的代码如下,插件中 Slots 标签会被替换为外面 Slot 标签包裹的内容,并按顺序依次匹配
<?xml version="1.0" encoding="utf-8"?>
<Lockscreen version="1" frameRate="60" screenWidth="1080">
<Group name="group1">
<Group x="300-1080" y="100">
<Text color="#ffffff" size="50" textExp="'我是负一屏'"/>
</Group>
<Group x="300" y="100">
<Text color="#ffffff" size="50" textExp="'我是正一屏'"/>
</Group>
</Group>
</Lockscreen>
插件内部可以添加多个插槽位,插件内的插槽都是匿名的,插入时按照外部元素插入的顺序一一匹配,如果少插入则不添加. 插槽内可插入 maml 原生代码,也可插入另一个 Plugin 插件
# 2.动画插槽
动画插槽是一种特殊类型的插槽,可以用来给插件添加动画,如下实例,通过<Animations/>
标签指定动画添加到插件中的位置
定义一个插件
<?xml version="1.0" encoding="utf-8"?>
<Template>
<Props>
<item name="x" default="0" type="number" description="x坐标"/>
<item name="y" default="0" type="number" description="y坐标"/>
<item name="w" default="100" type="number" description="宽度"/>
<item name="h" default="100" type="number" description="高度"/>
</Props>
<Animations />
<Rectangle x="$x" y="$y" w="$w" h="$h" fillColor="#ffffff"/>
</Template >
使用时给插件插入一个缩放动画和位移动画
<?xml version="1.0" encoding="utf-8"?>
<Lockscreen version="1" frameRate="60" screenWidth="1080" compiler="true">
<Plugin src="modules/plugin1" name="foo">
<Animation>
<ScaleAnimation initPause="true" loop="false" tag="show1">
<Item value="0" time="0" />
<Item value="1" time="1000" />
</ScaleAnimation>
<PositionAnimation initPause="true" loop="false" tag="show2">
<Item x="10" y="0" time="100" />
<Item x="10" y="200" time="1000"/>
</PositionAnimation>
</Animation>
</Plugin >
</Lockscreen>
编译完后会在插件定义动画插槽的位置插入我们添加的动画,插件中 Animations 的内容会被替换为外面 Animation 包裹的内容,并按顺序匹配
<?xml version="1.0" encoding="utf-8"?>
<Lockscreen version="1" frameRate="60" screenWidth="1080">
<Group name="foo">
<ScaleAnimation initPause="true" loop="false" tag="show1">
<Item value="0" time="0" />
<Item value="1" time="1000" />
</ScaleAnimation>
<PositionAnimation initPause="true" loop="false" tag="show2">
<Item x="10" y="0" time="100" />
<Item x="10" y="200" time="1000"/>
</PositionAnimation>
<Rectangle x="0" y="0" w="100" h="100" fillColor="#ffffff"/>
</Group >
</Lockscreen>
动画插槽与普通插槽一样也可以同时添加多个插槽位,并按照顺序插入不同位置
注意:动画仅支持元素动画的添加
# 3.事件插槽
事件插槽用于给插件添加事件,如下,通过<Emits/>
标签指定事件添加到插件中的位置
<?xml version="1.0" encoding="utf-8"?>
<Template>
<Props>
<item name="x" default="0" type="number" description="x坐标"/>
<item name="y" default="0" type="number" description="y坐标"/>
<item name="w" default="100" type="number" description="宽度"/>
<item name="h" default="100" type="number" description="高度"/>
</Props>
<Emits x="$x" y="$y" w="$w" h="$h"/>
<Rectangle x="$x" y="$y" w="$w" h="$h" fillColor="#ffffff"/>
</Template >
外部调用
<?xml version="1.0" encoding="utf-8"?>
<Lockscreen version="1" frameRate="60" screenWidth="1080" compiler="true">
<Plugin src="modules/plugin1" name="foo">
<Emit>
<Trigger action="down">
<AnimationCommand target="foo" command="play" tags="show1"/>
</Trigger>
<Trigger action="up">
<AnimationCommand target="foo" command="play" tags="show2"/>
</Trigger>
</Emit>
</Plugin >
</Lockscreen>
编译完后的代码如下
<?xml version="1.0" encoding="utf-8"?>
<Lockscreen version="1" frameRate="60" screenWidth="1080">
<Group name="foo">
<Button>
<Triggers>
<Trigger action="down">
<AnimationCommand target="foo" command="play" tags="show1"/>
</Trigger>
<Trigger action="up">
<AnimationCommand target="foo" command="play" tags="show2"/>
</Trigger>
<Triggers>
</Button>
<Rectangle x="0" y="0" w="100" h="100" fillColor="#ffffff"/>
</Group >
</Lockscreen>
事件插槽实际是在定义的插槽位置插入了一个按钮用于响应触摸操作,事件插槽也可以设置多个插槽位,并按照插入的顺序一一匹配
# 五、全局状态管理
# 1.状态管理
有的时候我们需要在不同模块共享一些变量状态,我们可以封装一个全局状态管理的组件 如:
<?xml version="1.0" encoding="utf-8"?>
<Maml>
<Var name="rot" expression="10" type="number" const="true"/>
<Var name="text" expression="'hello world'" type="string" const="true"/>
</Maml>
假设我们上面封装的组件放在 modules 目录下的 store.xml 中,我们可以在 main.xml 引入这个管理全局状态的组件
<Store src="modules/store" />
注意 Store 组件只支持 Var 标签的变量,不支持其他 maml 标签,引用时通过<Store />
标签引入
如上代码,编译完后会生成如下代码
<?xml version="1.0" encoding="utf-8"?>
<Lockscreen version="1" frameRate="60" screenWidth="1080">
<Var name="state.rot" expression="10" type="number" const="true"/>
<Var name="state.text" expression="'hello world'" type="string" const="true"/>
</Lockscreen>
其他地方再调用该全局变量时可以通过#state.rot
和@state.text
来调用
要修改全局状态的值可以通过StoreCommit
命令来修改
<StoreCommit name="rot" expression="1000" />
StoreCommit 命令编译完的代码实际上会转成 VariableCommand 标签,以上代码编译完的结果如下:
<VariableCommand name="state.rot" expression="1000" />
# 2.全局变量
某种情况下,如果某个变量实在不想让其私有化重命名,而是作为一个全局变量暴露,可用$name$ 双$符将该元素的 name 属性包裹起来,如:
<Var name="$move_x$" expression="300"/>
使用时也需要将该变量用双$符包裹起来,如
<Text text="$#move_x$"/>
编译完的代码不会将改变量名私有化,如下:
<Var name="move_x" expression="300"/>
<Text text="#move_x"/>
通常我们建议用第一种 Store 的方法来管理全局状态,第二种方法声明的变量会尽可能克制使用
# 六、插件间的数据通信
获取某个插件的内部变量
# 1.先定义一个插件
<?xml version="1.0" encoding="utf-8"?>
<Template>
<Props>
<item name="x" default="0" type="number" description="x坐标"/>
<item name="y" default="0" type="number" description="y坐标"/>
<item name="w" default="100" type="number" description="宽度"/>
<item name="h" default="100" type="number" description="高度"/>
</Props>
<Var name="foo" expression="300" const="true"/>
<Var name="bar" expression="500" const="true"/>
<Button x="$x" y="$y" w="$w" h="$h">
<Triggers>
<Trigger action="down">
<VariableCommand name="foo" expression="#foo+1"/>
<VariableCommand name="bar" expression="#bar-1"/>
</Trigger>
<Triggers>
</Button>
</Template >
# 2.获取数据
在其他插件或 main.xml 中获取前面定义插件的内部变量值
<?xml version="1.0" encoding="utf-8"?>
<Lockscreen version="1" frameRate="60" screenWidth="1080" compiler="true">
<Plugin src="modules/plugin1" name="myPlugin"/>
<Rectangle x="get($myPlugin,#foo)" w="100" h="100" fillColor="#ffffff"/>
</Lockscreen>
如上:矩形的 x 坐标通过 get 方法获取到 name 为 myPlugin 的中间中#foo 变量的值
# 3.命令的绑定
通过 bind 属性绑定要执行某个插件内部定义的命令,如下实例
<?xml version="1.0" encoding="utf-8"?>
<Template>
<Props>
<item name="x" default="100" type="number" description="x坐标"/>
<item name="y" default="100" type="number" description="y坐标"/>
</Props>
<Var name="text_value" expression="'foo'" const="true" type="string"/>
<Text x="$x" y="$y" color="#ffffff" size="50" textExp="#text_value" >
</Template >
引用时
<?xml version="1.0" encoding="utf-8"?>
<Lockscreen version="1" frameRate="60" screenWidth="1080" compiler="true">
<Plugin src="modules/plugin1" name="myPlugin"/>
<Button w="1080" h="2160">
<Triggers>
<Trigger action="down">
<VariableCommand name="text_value" expression="'bar'" bind="myPlugin" type="string"/>
</Trigger>
<Triggers>
</Button>
</Lockscreen>
通过给 command 命令添加一个 bind 属性,绑定插件名,可以修改插件内部定义的变量,其他的 command 命令类似,都是添加一个 bind 属性
<!-- 播放name为myPlugin的插件的init_ani的动画 -->
<AnimationCommand target="init_ani" command="play(0,300)" bind="myPlugin"/>
<!-- 播放name为myPlugin的插件的test的动画 -->
<Command target="test.animation" value="play" bind="myPlugin"/>
<!-- 控制name为myPlugin的插件的test元素的可见性 -->
<Command target="test.visibility" value="true" bind="myPlugin"/>
...
# 七、公共方法
如果我们调用一个插件时,插件入场时需要执行一堆命令,插件退场时也要执行一堆命令,如果用上面 bind 的方法来操作插件内部的方法的话我们需要熟悉插件的源码,从而确定改执行什么 command。为解决这个问题我们可以给插件封装一个入场和退场的公共方法,方便外部调用
# 1.定义公共方法
插件内封装的钩子函数如下,插件内添加一个 PluginCommands 的标签
<PluginCommands>
<!-- 插件入场时执行的操作,为方便记忆,入场的action统一约定为init -->
<Trigger action="init">
<AnimationCommand target="ani" command="play(0,300)" />
</Trigger>
<!-- 插件退场时执行的操作,为方便记忆,入场的action约定为exit,其他的需要添加的action自行定义 -->
<Trigger action="exit">
<AnimationCommand target="ani" command="play(300,0)" />
</Trigger>
</PluginCommands>
# 2.调用公共方法
PluginCommands 用来定义外部调用插件内部动作的集合,最终会翻译成组操作 Group(可以理解为 function),里面的每一个 action 对应一个方法,暂时约定入场时的 action 为 init,退场时的 action 为 exit,方便记忆,外面调用时不用去看插件的源码就也能知道怎么调用。 外面调用的时候通过 PluginCommad 命令来调用,编译后 PluginCommad 命令会变成组操作的命令
<PluginCommand target="pluginName" command="init" />
<PluginCommand target="pluginName" command="exit" />
# 八、插件功能个性化定制
# 1、选择插件的功能
如果我们在插件内部定了了很多功能,但每次我们只需要其中的一个功能,我们可以通过一个 Select 标签来定义一个选择器,如:
<?xml version="1.0" encoding="utf-8"?>
<Template >
<Props>
<item name="x" default ="0" type="number" description="x坐标"/>
<item name="y" default ="0" type="number" description="y坐标"/>
<item name="w" default ="#screen_width" type="number" description="宽度"/>
<item name="h" default ="#screen_height" type="number" description="高度"/>
<item name="size" default ="300" type="number" description="距离"/>
<item name="type" default ="0" type="select" description="解锁方式,0上划,1下划,2左划,3右划"/>
</Props >
<!-- Select为模块选择器标签,通过index属性绑定上面类型为item的参数来指定最终合成时用哪个Item内部的代码,其他的代码不会被添加 -->
<Select index="$type">
<Item>
<!-- 上划解锁 -->
<Unlocker name="unlocker" alwaysShow="true" >
<StartPoint x="$x" y="$y" w="$w" h="$h" easeType="QuintEaseOut" easeTime="600"/>
<EndPoint x="$x" y="-$h-$size" w="$w" h="$h" >
<Path x="$x" y="$y" tolerance="2000">
<Position x="$x" y="$y" />
<Position x="$x" y="-#screen_height" />
</Path>
</EndPoint>
</Unlocker>
</Item>
<Item>
<!-- 下划解锁 -->
<Unlocker name="unlocker" alwaysShow="true" >
<StartPoint x="$x" y="$y" w="$w" h="$h" easeType="BounceEaseOut" easeTime="800"/>
<EndPoint x="$x" y="$size" w="$w" h="$h">
<Path x="$x" y="$y" tolerance="2000">
<Position x="$x" y="$y" />
<Position x="$x" y="$h" />
</Path>
</EndPoint>
</Unlocker>
</Item>
<Item>
<!-- 左划解锁 -->
<Unlocker name="unlocker" alwaysShow="true" >
<StartPoint x="$x" y="$y" w="1080" h="$h" easeType="QuintEaseOut" easeTime="300"/>
<EndPoint x="-$w-$size" y="$y" w="$w" h="$h">
<Path x="$x" y="$y" tolerance="1500">
<Position x="$x" y="$y" />
<Position x="-$w" y="$y" />
</Path>
</EndPoint>
</Unlocker>
</Item>
<Item>
<!-- 右划解锁 -->
<Unlocker name="unlocker" alwaysShow="true" >
<StartPoint x="$x" y="$y" w="$w" h="$h" easeType="BounceEaseOut" easeTime="800"/>
<EndPoint x="$size" y="$y" w="$w" h="$h">
<Path x="$x" y="$y" tolerance="1500">
<Position x="$x" y="$y" />
<Position x="1080" y="$y" />
</Path>
</EndPoint>
</Unlocker>
</Item>
</Select>
</Template>
使用时
<Plugin src="modules/unlock" type="3" name="test"/>
编译后只会添加右划解锁的代码,生成的最终代码如下
<?xml version="1.0" encoding="utf-8"?>
<Lockscreen version="1" frameRate="30" screenWidth="1080">
<Group name="test">
<Unlocker name="unlocker" alwaysShow="true" >
<StartPoint x="0" y="0" w="#screen_width" h="#screen_height" easeType="BounceEaseOut" easeTime="800"/>
<EndPoint x="300" y="0" w="#screen_width" h="#screen_height">
<Path x="0" y="0" tolerance="1500">
<Position x="0" y="0" />
<Position x="1080" y="0" />
</Path>
</EndPoint>
</Unlocker>
</Group >
</Lockscreen>
插件选择器所选择的功能是互斥的,若选择了其中某项功能,其他的功能则不会被添加,避免产生冗余代码
# 2、关闭插件的功能
通过传入参数,可移除掉插件定义的某项功能,编译后的代码不包含这部分功能的代码,保持代码的纯净性 如: 具体实现为插件中的代码绑定 inset 属性,外部调用时传入 true 或 false 来确定这部分的代码要不要添加
<?xml version="1.0" encoding="utf-8"?>
<Template>
<Props>
<item name="x" default="0" type="number" description="x坐标"/>
<item name="y" default="0" type="number" description="y坐标"/>
<item name="r" default="0" type="number" description="半径"/>
<item name="w" default="100" type="number" description="宽度"/>
<item name="h" default="100" type="number" description="高度"/>
<item name="circle" default="true" type="bool" description="添加圆"/>
</Props>
<Rectangle x="$x" y="$y" w="$w" h="$h" fillColor="#ffffff"/>
<Circle x="$x" y="$y" r="$r" fillColor="#ffffff" inset="$circle"/>
</Template >
调用时
<Plugin src="modules/plugin1" name="aaa" circle ="false"/>
编译完后的代码如下,只有矩形
<?xml version="1.0" encoding="utf-8"?>
<Lockscreen version="1" frameRate="30" screenWidth="1080">
<Group name="aaa">
<Rectangle x="0" y="0" w="100" h="100" fillColor="#ffffff"/>
</Group >
</Lockscreen>
# 九、一些小技巧
# 1.传入参数自动加引号
插件中通过两个【`】符绑定的参数 如 `$xxx`即可确保内部传入的参数自动添加单引号,并且传入的参数支持表达式
<?xml version="1.0" encoding="utf-8"?>
<Template>
<Props>
<item name="x" default="100" type="number" description="x坐标"/>
<item name="y" default="100" type="number" description="y坐标"/>
<item name="color" default="#ffffff" type="string" description="颜色"/>
<item name="size" default="50" type="number" description="文字大小"/>
<item name="text" default="foo" type="string" description="文字内容"/>
</Props>
<Text x="$x" y="$y" color="$color" size="$size" textExp="`$text`"/>
</Template >
使用时
<?xml version="1.0" encoding="utf-8"?>
<Lockscreen version="1" frameRate="30" screenWidth="1080" compiler="true">
<Plugin src="modules/plugin1" name="aaa" text="hello"/>
<Plugin src="modules/plugin1" y="200" name="bbb" text="hello+world+@aaa"/>
</Lockscreen>
编译后的代码如下
<?xml version="1.0" encoding="utf-8"?>
<Lockscreen version="1" frameRate="30" screenWidth="1080">
<Group name="aaa">
<Text x="100" y="100" color="#ffffff" size="50" textExp="'hello'"/>
</Group >
<Group name="bbb">
<Text x="100" y="200" color="#ffffff" size="50" textExp="'hello'+'world'+@aaa"/>
</Group >
</Lockscreen>
# 2.引入编辑器内置的插件
编辑器内置了许多插件,我们除了通过src="modules/xxxx"
来引入自定义的插件外,也可以直接引入编辑器内置的插件
<?xml version="1.0" encoding="utf-8"?>
<Lockscreen version="1" frameRate="30" screenWidth="1080" compiler="true">
<Plugin src="defaultScreen" name="defaultScreen" left_icon_src="left_icon.png" right_icon_src="right_icon.png"/>
</Lockscreen>