#Introducing MAML [10th Sept 2023] [TRANSLATION IN PROGRESS]
MAML Engine scripting language
MIUI Application Markup Language for MORE (MIUI MORE engine application markup language)Introduction
It was originally used for the ever-changing lock screen, using xml to describe the lock screen interface with a specific syntax. Later, the functions were continuously enhanced, and gradually evolved into a set of near-universal interface description language and graphics rendering engine, which can be used to develop user interfaces with various styles under certain requirements. It is convenient to change the interface style, animation and even interaction mode by changing the skin.MAML language is similar to Android's interface description xml. The difference is that Android describes a static interface, and changes to interface elements depend on java code. MAML describes a static interface + dynamic attributes. The UI is constantly refreshed on the timeline at a certain frame rate, and the UI display is updated in real time based on the calculation results of the variable expressions of the element attributes. The MAML language and runtime engine have been separated from the lock screen as MIUI's built-in general framework. In addition to displaying time and date, it also supports querying standard Content Providers to obtain various information such as weather. Display various elements such as pictures and texts, various animations, and interface interactive controls such as sliding and clicking, which are suitable for displaying information or interfaces with simple interactive operations. Such as clock, weather widget, alarm clock interface
The framework supports dynamic frame rate, it does not need to keep rendering at a fixed frame rate, stop rendering when there is no animation and update, and only takes up very little at this time Resources, use low frame rate rendering for slowly changing animations, and adjust to high frame rate full speed rendering immediately after high dynamic animation starts. When rendering at full speed, the full-screen frame rate can basically reach 60-120 frames. Used wisely, it can be both cool and cheap.
Application of MAML in the ever-changing lock screen
The lockscreen/advance of the ever-changing lock screen in the theme package, under the directory, the manifest.xml file is a description script.
<?xml version="1.0" encoding="utf-8"?>
<Lockscreen version="1" frameRate="30" screenWidth="1080" useHardwareCanvas="true">
<Image />
<Image />
<Unlocker/>
<Unlocker/>
<DateTime/>
…
<Text/>
</Lockscreen>
Attribute | Type | Expression/Variable | Description |
---|---|---|---|
frameRate | int | x/x | Specify the frame rate. If the animation is slow, you can specify a smaller value to save power. The default is 30. |
screenWidth | int | x/x |
Sets the screen width standard. If it is specified as 720, the positions of all elements in the lock screen will be written according to the layout of 720p, and phones with 1080p, 480p and other resolutions will automatically scale.
If it is specified as 1080px, the positions of all elements in the lock screen are written according to the layout of 1080px, for example: screenWidth="1080" |
displayDesktop | boolean | x/x | The default is false, perspective to the desktop, if there is no lock screen wallpaper or the lock screen wallpaper can be removed or transparent, you can see the desktop launcher or the application before the lock screen. (currently not supported). |
showSysWallpaper | boolean | x/x | The default is false, whether to display the desktop wallpaper on the lock screen. If you do not specify a lock screen wallpaper <Wallpaper/> after enabling it, the desktop wallpaper will be used as the lock screen wallpaper (Currently the system does not support it). |
useHardwareCanvas | boolean | x/x | Enable hardware acceleration for devices that support hardware acceleration. since MIUI13 |
Application of MAML in Variety Wallpapers and Dynamic Icons
- Variety Wallpapers
Various wallpapers are in the miwallpaper directory of the theme package, and the description file is also manifest.xml. The root node representation is basically the same as that of the Variety lock screen:
<MiWallpaper version="1" frameRate="30" screenWidth="1080" useHardwareCanvas="true">
<Image/>
<Group/>
…
</MiWallpaper>
screenWidth Indicates the default resolution of the ever-changing wallpaper
wallpaper_offset_pixel_x The number of pixels to offset (0 ~ -1*screen width)
wallpaper_offset_x Offset percentage (0 ~ 1.0)
To achieve the effect of elements sliding with fingers, you need to use #wallpaper*offset_pixel_x, #wallpaper_offset_x
The relationship between the two:
#wallpaper_offset_x * screen width = -1\_#wallpaper_offset_pixel_x
When sliding: on the first screen, wallpaper_offset_pixel_x = 0, wallpaper_offset_x = 0;
Swipe to the last screen, wallpaper_offset_pixel_x = -1*screen width, wallpaper_offset_x = 1.0
In order to adapt to models with different resolutions, it is recommended to use #wallpaper_offset_x.
Suggested design approach:
- The wallpaper is cut into double screen width (screen width refers to screenWidth)
- Wallpaper positioning: x="-#wallpaper_offset_x * screen width"
- Positioning of elements that need to follow the slide: x="-#wallpaper_offset_x * screen width + position relative to the wallpaper"
- Dynamic icons
Dynamic icons are in the theme package icons\fancy_icons\ directory, each dynamic icon is a folder, and the name of the folder is the corresponding The package name of the app. For example, the dynamic icon of the calendar is a folder called "com.android.calendar", which contains the manifest.xml description file.
<Icon version="1" frameRate="30" width="168" height="168" screenWidth="1080" useVariableUpdater="" hideApplicationMessage="">
<Image/>
<Text/>
<DateTime/>
…
</Icon>
hideApplicationMessage: The default is false, which hides the notification sign in the upper right corner.
useVariableUpdater: Specify which system variables are needed, currently including power and time, and you can specify the update cycle of time, if the time widget is updated every second, you need to specify the update cycle as 1 second, otherwise By default it will be updated every minute. If the widget or dynamic icon only displays the date, it will be updated once a day, and the update cycle can be specified as 1 day. Try to choose a longer update cycle to save system resources.
Currently supported tags: Battery, DateTime.Day, DateTime.Hour, DateTime.Minute, DateTime.Second
If a gadget does not need battery or time variables , you need to use useVariableUpdater="none" to specify not to update time variables.
If a widget shows system battery and changes background image every hour based on time: useVariableUpdater="Battery,DateTime.Hour"
If you need to refresh multiple times per second, you can use frameRate directly
# Model Adaptation
A manifest file , can run on multiple devices at the same time, you only need to set the resources and scaling ratios used on devices of different sizes or densities.
- Resource adaptation:
extraResources="sw1000-den320:den320:1.2,sw1000-den320::1.2,sw1000-den320-large:den320:1.2"
sw1000-den320:den320:1.2
On a model with a screen width of 1000 and a density of 320, the den320 folder is used Resources, and zoom in 1.2 times;
sw1000-den320::1.2
On the model with screen width 1000 and density 320, use the default resources, and zoom in 1.2 times)
extraResources="sw2000-den480:1.8, sw1000-den320-large:1.8
There is only a colon in the middle of these two sentences, which means that the resources used by the sw2000-den480 model are in sw2000-den480 Resources under this folder with the same name, and enlarged by 1.8 times)
- Layout adaptation:
extraScales="sw1000-den320:1.2,sw2000-den480-large:1.8"
Adaptation principle:
- First make a list of den and sw that appear in all your customizations (such as here
extraResources="sw1440-den440::0.916, sw720-den320::0.667, sw480-den240::0.444
, the involved dens are 440, 320, 240, plus a default 480; sw has 1440, 720, 480, plus a default 1080 (corresponding to den480) - Then when your theme is placed on a device, first take your device density and find it in the den list above , find the closest one, and if there are multiple sws in the same den, then use the screen width of the device to find the closest one among these several sws, and thus find the most suitable swXXX-denXXX
- Finally, the resource is the resource in the directory corresponding to this sw-den, and the scaling ratio Sr. The zoom ratio of the numbers in the code uses the Ss corresponding to sw-den.
- The last is the zoom ratio
If scaling by density (ie scaleByDensity="true"): The real scaling ratio of the resource is Sr * device den / found den, and the digital scaling ratio in the code is Ss * device den / found den
If scaling according to the screen width (ie scaleByDensity="false"): The real scaling ratio of the resource is Sr * device sw / found sw, and the digital scaling ratio in the code is Ss * device sw / found sw
#Getting Started Tutorial
Before reading this tutorial, it is recommended to take a look at the introductory tutorial: Getting started with the lock screen (opens new window)
#Wallpaper elements
Wallpaper element refers to the wallpaper set by the system. It is the same as the Image element except that the image source cannot be specified, and can have animation and other property controls. Without this element no wallpaper is displayed. There can be more than one.
Example:
<Wallpaper/>
<Wallpaper x="#screen_width/2" y="#screen_height/2" align="center" alignV="center" blur="100" alpha="255*#defaultScreen_x/2"/>
#Data types
Type | Description |
---|---|
int | Integer data type, representing integer values such as -2, 0, 1, 100, etc. |
number | A numeric data type that represents numeric values, including integers and floating point numbers. |
string | String data type, representing a sequence of characters, such as "hello", "world", etc. Strings usually consist of a sequence of characters, which can be letters, numbers, or symbols. |
boolean | Boolean data type, only two values: true (true) and false (false). It is used to represent logical values, such as the result of a conditional statement or the state of a switch. |
#Variables
The value of a variable can be changed during runtime
<!-- (Variables are defined with Var) -->
<Var name="" expression="" type="" const="" threshold=""/>
Attribute | Type | Expression/Variable | Description |
---|---|---|---|
name | string | x/x | Custom variable name |
expression | number/string | o/o | The expression or constant corresponding to the variable. Note: String constants need an extra set of single quotes, such as expression="'I am text'" |
type | number/string | x/x | Define numeric variables or string variables Default: number |
const | boolean | x/x | When it is true, the variable will not be recalculated after initialization, but it can be reassigned using commands. Reasonable use of const can improve operating efficiency. |
persist | boolean | x/x | Variable persistence. After specifying true, if no other value of the variable is re-given, then this value will always be saved, and will not be restored regardless of re-locking or re-applying the theme after unlocking; the default is false |
threshold | int | x/x | Threshold trigger, when the change of the variable value exceeds the set threshold, some commands can be triggered. |
Array
Add []
to the variable type to declare that this is an array type="number[]"
Number type array; type= "string[]"
text type array;
An array can be regarded as a box, which can store a series of data of the same type, such as a set of numbers or a set of strings. Each data item has a corresponding number, which is called the index of the array.
The value in the array can be obtained by name[index]
, for example, the value of #numVar[2]
below is 500, @strVar[0]
is Aquarius. It should be noted that the index order in []
is from left to right and starts from 0
<!-- number type -->
<Var name="numVar" type="number[]" const="true" expression="" values="100,150,500,550,800,850"/>
<!-- text type; note: the text type must contain expression="''" attribute -->
<Var name="strVar" type="string[]" const="true" expression="''" values="'Aquarius','Pisces','Aries','Taurus','Gemini','Cancer','Gemini','Virgo','Libra','Scorpio','Sagittarius','Capricorn'"/>
<!-- Threshold example: The following code means that every time the value of #time3 changes by 1, all commands in <Trigger/> will be executed. -->
<Var name="time3" expression="#minute%10" threshold="1">
<Trigger>
<Command target="time3_anim.visibility" value="true"/>
<Command target="time3_anim.animation" value="play"/>
<Command target="time3_anim_new.visibility" value="false"/>
<Command target="time_3_anim_new.visiblity" value="true" delay="400"/>
<Command target="time_3_anim_new.animation" value="play" delay="400"/>
</Trigger>
</Var>
#Global variables
Touch
#touch_x The x coordinate of the current touch point
#touch_y The y coordinate of the current touch point
#touch_begin_x The initial x-coordinate when the screen is pressed
#touch_begin_y The initial y coordinate when the screen is pressed
#touch_begin_time time when the screen was pressed
Screen
#screen_width screen width
#screen_height screen height
#view_width component width (only used in each plugin, such as clock)
#view_height component height
#raw_screen_width Physical width (the screen resolution of the current device, not affected by the screenWidth of the root node)
#raw_screen_height physical height
Date
#time current time, long
#time_sys system time in milliseconds
#year year
#month+1 month (value range is 1~12, 1 means January, 2 means February, and so on)
#date date (value range is 1~31, 1 means 1st, 2 means 2nd, and so on)
#day_of_week-1 week (0 means Sunday, 1 means Monday, 2 means Tuesday ... 6 means Saturday)
#hour12 12 hour clock
#hour24 24 hour clock
#minute minutes
#second seconds
#ampm 0:am, 1:pm
#time_format 0: 12 hour format, 1: 24 hour format
Lunar Calendar
#year_lunar Lunar year
#year_lunar1864 is used to calculate the zodiac
#month_lunar Lunar months start at 0
#month_lunar_leap Lunar Leap 0 no, 1 yes
#date_lunar Lunar dates count from 1
Charge
#battery_level Current battery level, 1~100
#battery_state 0 Normal 1 Charging 2 Low battery 3 Fully charged
<!-- MIUI 11 developer version support -->
#ChargeSpeed 0 normal charging, 1 fast charging, 2 super fast charging, 3 super fast charging (120w±)
#ChargeWireState 11 wired charging, 10 wireless charging, -1 not charging
Images
The following imageName needs to be replaced with the name of your own image tag,
For example you have a <Image name="img" />, when you use it, you need to replace imageName with img
#imageName.actual_x the x coordinate of the real-time position of the image (get (name="imageName") the x coordinate of the real-time position of the image)
#imageName.actual_y the y coordinate of the real-time position of the image
#imageName.actual_w image display width (get (name="imageName") image real-time width)
#imageName.actual_h image display height
#imageName.bmp_width the width of the image file (not affected by cropping and zooming, only find the specified image according to src, and detect the width of the file)
#imageName.bmp_height the height of the image file
Testo
The following textName needs to be replaced with the name of your own text label,
For example you have a <Text name="aa" />, when you use it, you need to replace textName with aa
#textName.text_width Text width, which can be used for typesetting (get (name="textName") text width)
#textName.text_height text height
Music
The following musicName needs to be replaced with the name of your own music label,
For example you have a <MusicControl name="music" />, you need to put replace musicName with music
#musicName.music_state The current playing state of the music: 0 paused 1 playing (musicName is the name you defined for the music module)
#musicName.user_rating_style Whether the playback source is the system default music APP: 0 is not 1 is
@musicName.package The current playback source package name
@musicName.class The class name of the current playback source
Unlock
#unlocker.move_x The offset of the unlocked part in the x direction
#unlocker.move_y The offset of the unlocked part in the y direction
#unlocker.move_dist The distance to move the unlocked part
#unlocker.state The state of the unlocked widget: 0 normal, 1 pressed, 2 reached
Fingerprint under screen
#fod_enable Whether the system has enabled the under-screen fingerprint: 0 is off, 1 is on
#fod_x fingerprint area x coordinate
#fod_y Fingerprint area y coordinate
#fod_width fingerprint area width
#fod_height fingerprint area height
#fod_state_msg Fingerprint state: 1 finger pressed, 2 finger lifted, 3 recognition failed, 4 recognition successful
Dark mode
#__darkmode_wallpaper Whether to enable dark mode and support dimming wallpaper: 0 is not enabled, 1 is enabled
#__darkmode Whether to enable dark mode: 0 is not enabled, 1 is enabled
#applied_light_wallpaper The main color of the wallpaper, only effective on the desktop clock: 0 dark, 1 light (not effective when there is miwallpaper module)
Other
#sms_unread_count unread text messages
#call_missed_count missed calls count
@next_alarm_time next alarm time
#volume_level volume now
#volume_level_old adjust the previous volume Value: 1-15 Determine whether to increase or decrease according to the comparison between the two
#volume_type 0 call volume, 1 system volume, 2 phone ringtone, SMS ringtone, 3 music player volume,
4 alarm volume, 5 notification volume, 6 Call volume when connected to Bluetooth,
7 System volume mandatory in some countries, 8 DTMF volume, 9 TTS volume, 10 FM volume
Note: Generally, you can only adjust 3 (volume player volume) under the lock screen.
volume_type>=0 means that the volume is being adjusted, and the value is -1 after the adjustment is completed,
and the volume display can be displayed or hidden according to this
#frame_rate current screen frame rate
@__miui_version_code MIUI version (MIUI9=6, MIUI10=8, MIUI11=9, MIUI12=10)
#Expressions
Operator | Priority | Description |
---|---|---|
+ | 4 | Add |
- | 4 | minus |
* | 3 | Multiply by |
/ | 3 | divided by |
% | 3 | Modulo (here is not a percentage, it is a modulo, pay attention to the difference between the two) |
^ | 10 | Bitwise XOR operation |
~ | 2 | Bitwise negation operation |
{{ | 5 | Left shift operator |
}} | 5 | Right shift operator |
! | 2 | Logical not, equivalent to the previous not |
== | 7 | equal to |
!= | 7 | not equal to |
** | 11 | and must be satisfied at the same time to be true |
|| | 12 | Or, if one of the two conditions is met, it will be true |
} | 6 | greater than |
}= | 6 | greater than or equal to |
{ | 6 | less than |
{= | 6 | less than or equal to |
<!--example 1: #num != #num-1-->
<Image src="icon.png" visibility="#num != #show"/>
<!--example 2: #num ** #num-1-->
<Image src="icon.png" visibility="#num ** #show"/>
<!--example 3: #num == #num-1-->
<Image src="icon.png" visibility="#num == #show"/>
In the table below: eq, ne, ge, gt, le, lt, not, etc. are old conditional judgments. Currently, both MIUI11 and MIUI12 supports the above new expression conditional judgments, and it is recommended to use the new expressions.
Expression | Description |
---|---|
eq(x, y) | x==y (x is equal to y, the result is 1, otherwise it is 0) |
ne(x, y) | x!=y (x is not equal to y, the result is 1, otherwise it is 0) |
ge(x, y) | x>=y (x is greater than or equal to y, the result is 1, otherwise it is 0) |
gt(x, y) | x>y (x is greater than y, the result is 1, otherwise 0) |
le(x, y) | x<=y (x is less than or equal to y, the result is 1, otherwise it is 0) |
lt(x, y) | x<y (x is less than y, the result is 1, otherwise 0) |
not(x) | Logical NOT; x=0, not(x) is 1; x=1, not(x) is 0 |
ifelse(x, y, z) | x>0 (if x is greater than 0, the result is y; otherwise it takes the value z) |
ifelse(x1, y1, x2, y2, ... , z) | |
+ | Strings can be concatenated; 'qwe'+'asd' The result is 'qweasd'
|
eqs(@string1, @string2) | string comparison function, such as: when @string1 and @string2 have the same result, the result of eqs(@string1, @string2) is 1, otherwise it is 0 |
#Built-in functions
sin, cos, tan, asin, acos, atan, sinh, cosh, sqrt, abs, min, max, pow
Expression | Description |
---|---|
len(number) | Get variable and string digits |
digit(number, number) | Get the number of the given numberdigit(12345, 2) = 4 (Note: the original number cannot exceed 10 digits, the subscript From right to left, and starting from 1) |
substr(original string, string start position, string length) | substr('It's so hot today',1,2) = 'Naive' (note: the character position is from left to right, and starts from 0) |
strIsEmpty(string variable) | Determine whether the string variable is empty strIsEmpty(@abc) If it is empty, it will be 1, if it is not empty, it will be 0 |
isnull (numeric variable) | Judge whether the variable is empty isnull(#abc) If it is empty, it will be 1, if it is not empty, it will be 0 |
ceil() | Round up; such as: 6.1 or 6.99 both take the value 7 |
int() | Round down; such as: 6.1 or 6.99 both take the value of 6 |
round() | rounding off |
rand() | Take a random number between 0 and 1; if you need to randomly generate a random number from 0-100, you can write rand()*100
|
formatDate('string',#time_sys) | Date is formatted as a string; 'string' is written as 'HH:mm' |
strStartsWith('123456789','12') | Judge whether the string is at the beginning of a certain string, if it is, it will be 1, if not, it will be 0 |
strEndsWith('123456789','89') | |
strIndexOf(@string_a,'string_b') | The position where the character string_b first appears in the string @string_a For example: strIndexOf('string','str')=0 |
strLastIndexOf(@string_a,'string_b') | The last position of character string_b in string @string_a such as: strLastIndexOf('starina','a')=6 |
strContains(@string_a,'string_b') | Whether the string @string_a contains the character string_b such as: strContains('string','str')=1 or (true) |
strReplaceAll(@string_a,'string_b','string_c') | Replace all string_b in @string_a with string_c (strReplaceAll('abc','a','1')='1bc', support regular expressions. |
preciseeval('@string_a',3) | Calculate the value of the string and be accurate to 3 decimal places, such as: preciseeval('5*5+0.333',3)==25.333, the smallest number of decimal places is 1 |
formatFloat('%.3f',#accx) | Format the decimal places and convert them into strings (%.3f means rounding to 3 decimal places). Unlike preciseeval, formatFloat will occupy a place, such as 3.000 |
strMatches(@str,'.[\+/-]$') | regular expression |
strTrim(' 123 ') | Cut off spaces, tabs, carriage returns ('123') at the beginning and end of the string |
strReplaceFirst('ABCdefABC','ABC','666') | Replace the first;666defABC |
strToLowerCase('ABCdef') | Convert to lowercase;abcdef |
strToUpperCase('ABCdef') | Convert to uppercase;ABCDEF |
#Lock screen elements
Add text to lock screen
<Text x="50" y="500" color="#ffffff" size="48" text="hello,world!" />
color supports hexadecimal color values: #FFFFFF; supports string variables, such as: @abc; supports function argb(255,255,255,255)
size text size
text text content
Insert picture on lock screen
<Image x="0" y="0" src="lock_bg.jpg" />
src image name path
Element related content detailed explanation
<Text x="50" y="500" align="center" alignV="center" color="#ffffff" size="48" text="hello,world!" alpha="255*0.8" visibility="1" />
<Image x="0" y="0" w="512" h="512" align="center" alignV="center" src="lock_bg.jpg" alpha="255*0.8" visibility="1" />
x y coordinates relative to the upper left corner of the screen
h w width and height (this need not be explained)
align coordinate point horizontal alignment left, center, right
alignV coordinate point vertical alignment top, center, bottom
alpha Transparency 0-255, less than or equal to 0 does not display
visibility supports expressions, it will be displayed when it is greater than 0
Dates and time
<!--Image type format-->
<Time x="540" y="400" align="center" alignV="center" src="time.png" space="0" format="HH:mm"/>
<!--Text style format-->
<DateTime x="540" y="400" align="center" alignV="center" size="200" color="#ffffff" formatExp="ifelse(#time_format,'HH:mm','h:mm')" fontFamily="miui-thin" />
space The gap between pictures, you can use this function to typesetting time pictures, support positive and negative values
format Standard date format; when the time type is a picture, you can leave it blank, and follow the system by default. Whether to display 24-hour format
formatExp date expression format
fontFamily specifies the font, and the fonts that can be used are listed in the "callable font preview" later
#Add unlock
Swipe a distance with Button to unlock
Although the amount of code is more than Unlocker, it is actually simpler and easier to understand than Unlocker, and it brings possibilities for future expansion, such as making gestures that do not conflict with sliding left and right and sliding up to unlock.
<!--Unlock text hints-->
<Text x="#screen_width/2" y="#screen_height-100-#unlockMove" align="center" alignV="center" color="#ffffff" size="42" text="向上滑动解锁"/>
<!--Unlock related variables, animations, buttons-->
<Group name="Unlock" >
<!--实时变量-->
<Var name="unlockMove" expression="ifelse(#unlockDown==1,max(#touch_begin_y-#touch_y,0),max(#touch_begin_y-#touch_y,0) { 300,max(#touch_begin_y-#touch_y,0)*(1-#unlockBack),0)" />
<!--animation-->
<Var name="unlockBack">
<VariableAnimation initPause="true" loop="false">
<Item value="0" time="0" easeType="BounceEaseOut" />
<Item value="1" time="300" />
</VariableAnimation>
</Var>
<!--unlock button-->
<Button w="#screen_width" h="#screen_height" >
<Triggers>
<Trigger action="down">
<VariableCommand name="unlockDown" expression="1"/>
</Trigger>
<Trigger action="up,cancel">
<VariableCommand name="unlockDown" expression="0" />
<Command target="unlockBack.animation" value="play" />
<!--condition execution condition, unlock when max(#touch_begin_y-#touch_y,0) is greater than or equal to 300px-->
<ExternCommand command="unlock" condition="max(#touch_begin_y-#touch_y,0) }= 300" />
</Trigger>
</Triggers>
</Button>
</Group>
Can't understand this unlock? It doesn't matter, the next chapter introduces the various uses and writing methods of
buttons, some variables are also used here.
If you don’t understand the variables, you can simply read them first in
global variables
Unlocker and Slider
The usage of Unlocker and Slider is the same, both activate certain operations by sliding, but Unlocker can be directly unlocked, and Slider needs to add an unlock command. Click here to view a more detailed introduction
<!--Unlock text hints-->
<Text x="#screen_width/2" y="#screen_height-100-#Unlocker.move_dist" align="center" alignV="center" color="#ffffff" size="42" text="slide up to unlock"/>
<!--name="Unlocker" unlock name-->
<Unlocker name="Unlocker">
<!--touch start area-->
<StartPoint x="0" y="0" w="1080" h="#screen_height" />
<!--Reach the unlocked area, pay attention to write the coordinate position-->
<EndPoint x="0" y="-#screen_height" w="1080" h="#screen_height-200">
<Path tolerance="800">
<!--unlock path-->
<Position x="0" y="0" />
<Position x="0" y="-#screen_height" />
</Path>
</EndPoint>
</Unlocker>
MAML practice questions: Try to make the following simple lock screen
Lock Screen PSD example (opens new window)
#Text introduction
Text
<Text x="50" y="500" align="center" alignV="center" color="#ffffff" size="48" textExp="'hello,world!'"/>
Attribute | Type | expression/variable | Description |
---|---|---|---|
x | number | o/o | Coordinates relative to the upper left corner of the screen |
y | number | o/o | Coordinates relative to the upper left corner of the screen |
color | string | x/o | Text color, supports constants: #ffffff; supports string variables, @abc; supports function argb(255,255,255,255)(argb(transparency, red, green, blue), not supported Expression. |
size | number | o/o | Text size |
bold | boolean | x/x | Bold, true means bold |
format | string/numbe | x/x | If the variable in paras is a numeric type, use %d, if it is a string type, use %s |
paras | string/number | o/o | If format is specified, the variable expressions corresponding to %d and %d need to be specified in paras. There can be multiple variable expressions separated by ","< /td> |
text | string/number | x/x | Text display content and subsequent parameters with Exp indicate that the parameter supports expression |
textExp | string/number | o/o | Text display content expression, you can directly call variables, etc. For example: "The current time is 9 o'clock" can be written as textExp="'The current time is'+#hour12+' Click '" |
width | number | o/o | Text width, when the text exceeds the specified width, it will be cut off. If multiple lines of display are specified, the lines will be displayed. If text scrolling is specified, the text will be scrolled and displayed at the specified position |
marqueeSpeed | number | x/x | Text scrolling speed, use marqueeSpeed="30" |
marqueeGap | number | x/x | Scroll interval. The interval between when the text appears again after it is displayed, defaults to the width of four Chinese characters |
rotation | number | o/o | Rotation angle |
multiLine | boolean | x/x | true/false Whether to support multi-line display, default false. Supports line breaks ( and \n) when enabled |
spacingMult | number | x/x | Line spacing multiple default 1 |
spacingAdd | number | x/x | Line spacing increase default 0 |
shadowDx | number | x/x | The offset distance of the horizontal shadow relative to the text |
shadowDy | number | x/x | The offset distance of the vertical shadow relative to the text |
shadowRadius | number | x/x | The blur radius of the shadow can achieve a blurry shadow effect |
align | string | x/x | Horizontal alignment of coordinate points left, center, right |
alignV | string | x/x | Vertical alignment of coordinate points top, center, bottom |
shadowColor | string | x/o | Shadow color, supports transparency |
alpha | number | o/o | Opacity 0 - 255 |
visibility | number/string | o/o | Element visibility supports the expression {=0 invisible,}0 visible |
text.text_width | number | x/x | The width of a certain line of text can be used for typesetting |
fontFamily | string | x/x | Supports specified system fonts; view: Font preview available |
Notice:
- Literals need to be quoted in single quotes in expressions, but numeric variables and string variables do not need to be; such as: 'hello, world!', @abc, #num;
- The alpha value can be written as a simple expression; such as alpha="255 * 0.8" or alpha="2.55 * 80", both representing 80% opacity
- visibility supports the expression {=0 invisible, }0 visible; even if the value is 0.00001, it is visible, in short, it is visible if it is greater than 0
A few examples:
<Text shadowDx="3" shadowDy="4" shadowRadius="6" shadowColor="#9c000000" .../>
<Text name="tt" .../> //#tt.text_width is the length of this text
Show next alarm time
<Text text="@next_alarm_time" .../>
<Text format="Next alarm: [%s] Battery: [%d%%]" paras="@next_alarm_time,#battery_level"/>
- show current time
<Text x="50" y="500" color="#ffffff" size="48" textExp="'The current time is '+#hour12"/>
or
<Text x="50" y="500" color="#ffffff" size="48" textExp="'Current Temperature '+#weather_temperature+'°'"/>
#hour12 As a system global variable, pass the current hour to maml,#weather_temperature The current temperature obtained for the weather interface, which will be discussed later/p>
- text scrolling
<Text x="50" y="500" w="300" color="#ffffff" size="50" marqueeSpeed="30" text="Always believe that good things are about to happen"/>
#Callable font preview
Mitype: only supports numeric types | style | Mi Lan Pro font: supports Chinese/English/numeric types | style |
---|---|---|---|
mitype-thin | 0123456789:%+-×÷ | mipro-thin | 0123456789:%+-×÷ |
mitype-extralight | 0123456789:%+-×÷ | mipro-extralight | 0123456789:%+-×÷ |
mitype-light | 0123456789:%+-×÷ | mipro-light | 0123456789:%+-×÷ |
mitype-normal | 0123456789:%+-×÷ | mipro-normal | 0123456789:%+-×÷ |
mitype-regular | 0123456789:%+-×÷ | mipro-regular | 0123456789:%+-×÷ |
mitype-medium | 0123456789:%+-×÷ | mipro-medium | 0123456789:%+-×÷ |
mitype-demibold | 0123456789:%+-×÷ | mipro-demibold | 0123456789:%+-×÷ |
mitype-semibold | 0123456789:%+-×÷ | mipro-semibold | 0123456789:%+-×÷ |
mitype-bold | 0123456789:%+-×÷ | mipro-bold | 0123456789:%+-×÷ |
mitype-heavy | 0123456789:%+-×÷ | mipro-heavy | 0123456789:%+-×÷ |
#Dates and time
Time
The time mentioned here is actually a picture combination composed of 10 digital pictures plus ":" pictures according to the time format.
<!--picture class writing-->
<Time x="540" y="400" align="center" alignV="center" src="time.png" space="0" format="HH:mm"/>
<!--text style-->
<DateTime x="540" y="400" align="center" alignV="center" size="200" color="#ffffff" formatExp="ifelse(#time_format,'HH:mm','h:mm')" fontFamily="mitype-light" />
src represents the prefix of the time picture, as shown below, use time_0.png, time_1.png, ... time_9.png, time_dot.png. The coordinate attribute supports variable expressions
space represents the gap between time pictures. We can use this function to layout time pictures. When using positive values, the distance between pictures becomes larger. At this time, the pictures can be cut into smaller sizes to save memory. For images with projections, write space as a negative value to overlap the projections to save space
#Date format
Display date in specified format:
formatExp: supports date expression format (formatExp="ifelse(#time_format,'HH:mm','h:mm')")
format: supports standard date format (format="HH:mm")
Difference between Text and DateTime:
DateTime is mainly used to display time in quick format and supports all parameters of Text.
DateTime directly uses the following parameters to display different time parameters.
Code | Definition | Example possible results |
---|---|---|
A | Twelve Zodiac Years | Rat, cow, sheep |
G | AD | AD |
Y | Chinese Character Year (Lunar Calendar) | 二〇一五 |
YY | Year of Ganzhi | Jia Zi |
yy | Numerical year (2 digits) | 20 |
yyyy | Digital Year | 2020 |
M | Month | 1 |
MM | Month (1 - 9 plus 0) | 01 |
MMM | Month (Chinese character) | 九 |
MMMM | Full month (Chinese characters) | 九月 |
N | Lunar month | right, two, three |
NN | Stem and Branch Month | Yichou |
NNNN | Complete lunar calendar date + solar terms | August 28 Autumnal Equinox (only applicable to DateTime) |
D | Day of the year | 168 |
d | Digital Day | 23 |
e | Lunar calendar day | Third grade |
ee | Stem and Support Day | Bingyin |
t | Twenty-four solar terms | Winter Solstice |
E | Day of the week | Wed |
EEEE | Day of the week | Wednesday |
EEEEE | Day of the week | 三 |
H | 24-hour clock | 0~23 |
h | 12-hour clock | 0~12 |
I | Time and Earthly Branches | Unitary |
II | Time and Earthly Branches | Ding You |
m | minutes | 6 |
mm | minutes (two digits) | 06 |
s | seconds | 6 |
ss | Seconds | 06 |
S | Hao seconds | 666 |
a | Morning and afternoon | Morning, Afternoon |
aa | Morning and afternoon (details) | Morning, afternoon, evening, early morning, night |
Z/ZZ/ZZZ | Time zone | +0800 |
ZZZZ | Time zone | GMT+08:00 |
ZZZZZ | Time zone | 08:00 |
zzzz | Time zone | China Standard Time |
DateTime examples
<!-- 23:42 on the 10th day of August, 2015 (lunar calendar)-->
<DateTime x="20" y="450" color="#000000" size="40" format="Y年N月eH时m分" />
<!-- Today's date: 2020年09月15日 -->
<DateTime x="540" y="400" align="center" alignV="center" size="200" color="#ffffff" formatExp="'Today's date: yyyy年MM月dd日'" />
DateTime can specify a value value. If not specified, the time will be calculated based on the current timestamp. When a timestamp is specified, the time will be calculated based on the specified timestamp.
<DateTime x="540" y="400" align="center" alignV="center" size="200" color="#ffffff" formatExp="'Tomorrow date: yyyy年MM月dd日'" value="#time_sys+86400000" />
#Use of groups
Group
<Group name="" x="" y="" w="" h="" clip="true" layered="true" visibility="">
<PositionAnimation/>
<SizeAnimation/>
<Image/>
<Time/>
<DateTime/>
<Text/>
</Group>
Group means a container, used to contain other elements, such as pictures, time, controls, etc.
It can be understood as a layer, which can easily adjust the position and size of multiple internal elements,
and add various animations like Image. The coordinates of internal elements are relative coordinates
(relative to group coordinates)
clip true/false, when it is true, the content beyond the w h mark range will be cropped and will not be displayed
(when there is clip and layered, w h must be added)
AutoScaleGroup
A new AutoScaleGroup element is added, which is inherited from the Group element. The difference is that the AutoScaleGroup element will record the initial w and h. When w or h changes later, the scale will be done proportionally. Since MIUI12.5
<AutoScaleGroup name="" x="" y="" w="" h="" clip="true" layered="true" visibility="">
<PositionAnimation/>
<SizeAnimation/>
<Image/>
<Time/>
<DateTime/>
<Text/>
</AutoScaleGroup>
Group command
If a certain group of operations will be called in many places, you can write this group of operations into an empty Group as a trigger action, and then it can be called in many places.
<!-- group command -->
<Group name="triggersContainer" >
<Triggers>
<Trigger action="content_1" >
<AnimationCommand target="numAni" command="play" />
<VariableCommand name="number001" expression="1"/>
</Trigger>
<Trigger action="content_2" >
<VariableCommand name="month_num" expression="#month_num+1" />
</Trigger>
</Triggers>
</Group>
<!-- Execute group commands -->
<Button x="540" y="0" w="540" h="540">
<Triggers>
<Trigger action="up">
<MethodCommand target="triggersContainer" method="performAction" paramTypes="String" params="'content_1'"/>
<MethodCommand target="triggersContainer" method="performAction" paramTypes="String" params="'content_2'"/>
</Trigger>
</Triggers>
</Button>
Custom function
Add function elements as functional calls. Since MIUI12.5
<!--Function is an element that imitates a function and contains various ActionCommands.-->
<Function name="XXXX">
<VariableCommand />
<XXXXCommand/>
<!-- Conditional execution command sub-elements Consequent (if) and Alternate (else) -->
<IfCommand ifCondition="XXXX">
<!-- Consequent execution when ifCondition is satisfied -->
<Consequent>
<VariableCommand />
<AnimationCommand />
<XXXXCommand/>
</Consequent>
<!-- Alternate Execution when ifCondition is not met -->
<Alternate>
<VariableCommand />
<AnimationCommand />
<XXXXCommand />
</Alternate>
</IfCommand>
</Function>
<!-- Add a command to call the Function -->
<FunctionCommand target="XXX" />
#Images
The picture component is used to display a picture on the lock screen interface and can specify various attributes.
<Image x="" y="" w="" h="" pivotX="" pivotY="" rotation="" src="" srcid="" alpha="" align="" antiAlias="" visibility=""/>
<Image x="" y="" w="" h="" pivotX="" pivotY="" pivotZ="" rotationX="" rotationY="" rotationZ="" src="" />
Attribute | Type | expression/variable | Description |
---|---|---|---|
x,y | number | o/o | Coordinates relative to the upper left corner of the screen |
w,h | number | o/o | width and height |
pivotX, pivotY | number | o/o | rotation center |
rotation | number | o/o | Rotation angle, 360 degrees in one circle |
rotationX | number | o/o | X-axis rotation angle, 360 degrees in one circle |
rotationY | number | o/o | Y-axis rotation angle, 360 degrees in a circle |
rotationZ | number | o/o | Z-axis rotation angle, 360 degrees in one circle |
src | string | x/x | Picture name path |
srcid | number/string | o/o | The suffix number of the picture sequence is generally represented by a variable. Different pictures can be displayed according to the variable. If src="pic.png" srcid="1", it will be displayed at the end Picture"pic_1.png" |
alpha | number | o/o | Transparency 0-255, less than or equal to 0 will not be displayed |
antiAlias | boolean | x/x | true/false anti-aliasing, if true, the image will not be aliased when deformed and rotated, but the speed will be slow |
srcExp | number/string | o/o | Image source expression |
srcFormat | string | x/x | Image source format srcParas uses %d for number type and %s for string type |
srcForamtExp | number/string | o/o | Image source format expression |
srcParas | number/string | o/o | Image source parameters, separate multiple parameters with, |
visibility | number | o/o | Supports expressions and displays when greater than 0 |
align/alignV | string | x/x | Alignment |
tint | string | x/o | Tint (dye);tint="#ffff0000" .
Supports variables but does not support expressions Since MIUI12. |
tintmode | int | o/o | Tone (dye) blending mode needs to be used together with tint, and the effect is the same as image blending xfermodeNum. Defaulttintmode="5" .
Added support for variables Since MIUI12. |
Several application examples of pictures:
<Image x="300" y="100" align="center" srcExp="'weather/weather_' + #weather_id + '.png'"/>
<Image x="300" y="100" align="center" src="weather/weather.png" srcid="#weather_id"/>
The above two pieces of code have the same effect. They will read the png file corresponding to the number weather_ in the weather directory. For example, the number passed by #weather_id is 3. The image weather_3.png in the weather directory will be read, which can be used to display corresponding icons according to different weather conditions. The acquisition of weather data will be discussed later. When srcid refers to a picture sequence, the picture must be named in a format similar to pic_0.png pic_1.png. srcExp directly splices the path of the picture. srcExp can be used together with srcid, such as:
<Image x="210" y="100" align="center" srcExp="'weather/'+#hour24}13+'/weather.png'" srcid="#weather_id"/>
#hour24}13 means that when the current hour (24-hour format, global variables will be discussed later) is greater than 13 o'clock, the condition is established, the output value is 1, so it refers to the sequence picture of weather_xx.png in the weather/1/ directory. Otherwise when it is less than or equal to 13 o'clock, the #hour24}13 condition is false, and the output value is 0. In this case, the sequence picture of weather_xx.png refers to the weather/0/ directory. That can be used to display different pictures in different time periods that is during the day or the night. For example you can write like this:
<Image x="300" y="100" align="center" srcExp="'weather/'+#hour24}=6**#hour24{18+'/weather.png'" srcid="#weather_id"/>
It means that the pictures in the weather/1/ directory will be displayed when the time is greater than or equal to 6 o'clock and less than 18 o'clock, and the pictures in the weather/0/ directory will be displayed at other times. Of course, you can also use other variables instead #hour24}=6* *#hour24{18 judgment, the use of variables and expressions will be discussed later
Digital image mapping
<ImageNumber number="" src=""/>
Attribute | Description |
---|---|
number | Numeric expression to display |
src | The file name of the image source, supports SourceAnimation |
If src="number.png"
then number_0.png number_1.png ... image files will be used to draw numbers.
Text image mapping
To put it simply, it is to display pictures together; for example, when you need to use pictures to display
'2019.03.27'
or 'Wednesday 03.27'
, this cannot be done using ImageNumber or Time controls,
either cannot display long characters, or cannot display two '.'. Using ImageChars can perfectly solve this problem.
<ImageChars x="540" y="300" align="center" alignV="center" src="num/string.png" string="formatDate('HH:mm MM.dd',#time_sys)" charNameMap=".spot,:colon,%pct, space"/>
attribute | Definition |
---|---|
string | String expression |
number | Numeric expression |
space | display interval |
charNameMap | Mapping list, separated by English commas, the first character of each item is the original character, and the following is the mapping string, which can be mapped to one or more characters |
The names of the mapped images in the example are:
Original character | Mapping name (picture suffix) | Definition |
---|---|---|
. | spot | Point |
: | colon | Colon |
% | pct | Percent |
(note the space here) | space | Empty |
Note:
- Because the names of pictures in the same directory are not English-sensitive, the uppercase English names are represented by two lowercase English characters to represent uppercase; for example: A picture name is num_aa.png
- Some characters cannot be used directly and need to be escaped before they can be used; for example: &
#Images overlay
<!-- Example: (image overlay should be used carefully, otherwise it will get stuck) -->
<Group x="300" y="500" w="200" h="200" layered="true">
<Image src="test.png"/>
<Image src="mask.png" xfermode="dst_in" />
</Group>
<!-- or -->
<Group x="300" y="500" w="200" h="200" layered="true">
<Image src="test.png"/>
<Image src="mask.png" xfermodeNum="6" />
</Group>
The results achieved by these two examples are the same, both are masking effects, that is, test.png is cropped according to the shape of mask.png.
layered is used in conjunction with xfermode in Group. This attribute is added to make xfermode only act on elements inside the Group.
xfermode mixing mode, possible values are: clear, src, dst, src_over, dst_over, src_in, dst_in, src_out, dst_out, src_atop, dst_atop, xor
xfermodeNum mixing mode supports variable expressions. The value of the expression corresponds to a mixed mode, src, dst, src_over,...xor takes the values 1,2,3...11 in sequence
Note:
- When using layered in Group, please be sure to specify the scope of action w h, otherwise it will not take effect (use carefully, otherwise it will get stuck)
- There can be }=2 pictures in a group. The last Image with xfermode will treat all the previous pictures in the group as a whole Image and mix them according to the value of xfermode
Correspondence table:
The two images that have been mixed in the above images are:
#Paint
The paint is definitely a magical function. When paired with the mixing function, it can create the effects of scratching prizes and cleaning glass.
Paint code:
<Paint x="0" y="0" w="1080" h="1920" color="#ff6600" weight="40" />
attribute | Definition |
---|---|
weight | Brush width, support expressions |
xfermode | Blending mode, refer to Image |
xfermodeNum | Width and height, define the area that this brush can paint |
Example with image blending:
<Wallpaper /> // Call the system wallpaper, here is the background image after you wipe your finger
<Group x="0" y="0" w="1080" h="1920" layered="true"> // Image overlay
<Image x="0" y="0" src="fg.png"/> // fg.png is the erased picture (Gaussian blur + water drop mist effect)
<Paint weight="50" w="1080" h="1920" xfermode="clear" /> // clear can produce an erasure effect
</Group>
Result:
#Image distortion
Sample theme: Shake demo [picture deformation].mtz (opens new window)
As shown in the picture:
Note: the array used to control the image node must be of type float[], so
But number[] will be used temporarily in subsequent calculations
For the time being, picture deformation requires you to calculate the scaling ratio at other resolutions yourself, example:
<Var name="img_w" expression="400" const="true" />
<Var name="img_h" expression="400" const="true" />
<Var name="img_m" expression="2" const="true" />
<Var name="img_n" expression="2" const="true" />
<!-- In the mesharr[] array, there must be X coordinates and Y coordinates at the same time, so when declaring the range is: (#img_m+1)*(#img_n+1)*2=18-->
<Var name="mesharr" type="float[]" size="18" />
<ExternalCommands>
<Trigger action="init">
<VariableCommand name="scale_wh" expression="int(#raw_screen_width/1.08)/1000" /> //(If you write in 1080p on the lock screen, use this calculation)
<!-- Mesh first -->
<LoopCommand count="(#img_m+1)*(#img_n+1)" indexName="__a">
<VariableCommand name="mesharr" type="float[]" index="#__a*2" expression="int(#img_w/#img_m)*(#__a%(#img_m+1))" />
<VariableCommand name="mesharr" type="float[]" index="#__a*2+1" expression="int(#img_h/#img_n)*int(#__a/(#img_m+1))" />
</LoopCommand>
<VariableCommand name="mesh5_x" expression="#mesharr[4*2]" />
<VariableCommand name="mesh5_y" expression="#mesharr[4*2+1]" />
</Trigger>
</ExternalCommands>
<Var name="mesharr" type="float[]" index="8" expression="#mesh5_x+int(#touch_x-#touch_begin_x)" />
<Var name="mesharr" type="float[]" index="9" expression="#mesh5_y+int(#touch_y-#touch_begin_y)" />
<Image x="0" y="0" scale="#scale_wh" src="bg3.jpg" mesh="2,2" meshVertsArr="mesharr" />
Attachment: img0927.mtz (opens new window)
#Geometric figures
Purpose: In some scenarios with high memory requirements, use the method of drawing geometric figures instead of <Image>
to reduce memory.
Rectangle (for rectangles and rounded rectangles)
<Rectangle x="10" y="20" w="100" h="200" cornerRadius="5,10" strokeColor="#ff00ff00" fillColor="#ff000000" weight="5" cap="round" dash="1,2,3,4" strokeAlign="center">
<!-- FillShaders: fill coloring; StrokeShaders: stroke coloring -->
<FillShaders>
<!-- linear gradient -->
<LinearGradient x="#screen_width" y="0" x1="0" y1="#screen_height" tile="clamp">
<GradientStop color="#050B2A" position="0"/>
<GradientStop color="#81425D" position="0.5"/>
<GradientStop color="#C87960" position="1"/>
</LinearGradient>
<!-- radial gradient -->
<RadialGradient x="" y="" rX="" rY="" tile="">
<GradientStop color="" position=""/>
<GradientStop color="" position=""/>
</RadialGradient>
<!-- sweep gradient -->
<SweepGradient x="" y="" rotation="" tile="">
<GradientStop color="" position=""/>
<GradientStop color="" position=""/>
</SweepGradient>
</FillShaders>
<StrokeShaders>
</StrokeShaders>
</Rectangle>
attribute | Type | Expression/variable | Definition |
---|---|---|---|
x y | number | o/o | Graph starting point; Rectangle is the upper left corner, and the rest of the geometric figures are the center point |
strokeColor | string | x/o | Stroke color. Multiple color specification methods are supported, as are all the attributes of the color class below. |
fillColor | string | x/o | Fill color |
weight | number | o/o | Stroke line width |
cap | string | x/x | Thread shape. butt wireless head (default), round (semicircle), squre (square) |
dash | number | x/x | Dotted line mode. Format "line length, interval, line length, interval...", e.g. dash="1,2,3,4": 1 pixel wide line, 2 pixel interval, 3 pixel line, 4 pixel Interval, repeat like this... |
strokeAlign | string | x/x | Stroke alignment, inner inner stroke, center center stroke, outer outer stroke (default) |
xfermode | int | o/o | Blending mode, same as <Image/> |
cornerRadius | number | x/x | Chamfer radius; format "x to radius, y to radius" |
cornerRadiusExp | number | o/o | Chamfer radius; support expression, format "x radius, y radius";<font color=#ff |
FillShaders and StrokeShaders
The fill shader has the same syntax as the stroke shader: linear gradient shading, radial gradient shading (radial gradient), scanning gradient shading (circular gradient), and bitmap shading are supported.
- LinearGradient linear gradient
Attribute | Type | expression/variable | Description |
---|---|---|---|
x y x1 y1 | number | o/o | Gradient axis (x, y) --> (x1, y1) |
tile | string | x/x | Spread mode; clamp stretch (default), mirror mirror, repeat repeat |
- RadialGradient radial gradient
Attribute | Type | expression/variable | Description |
---|---|---|---|
x y | number | o/o | center position |
rX rY | number | o/o | Radius in x y direction |
- SweepGradient sweep gradient
Attribute | Type | expression/variable | Description |
---|---|---|---|
x y | number | o/o | Center point position |
rotation | number | o/o | rotation angle |
- GradientStop gradient point; make a gradient between the colors of several specified gradient points
Attribute | Type | expression/variable | Description |
---|---|---|---|
color | string | x/o | Color of gradient points |
position | number | o/o | Gradient point position, floating point number from 0 to 1.0 |
Ellipse
<Ellipse x="10" y="20" w="100" h="200" strokeColor="#ff00ff00" fillColor="#ff000000" weight="5" cap="round" dash="1,2,3,4" strokeAlign="center" >
<StrokeShaders>
</StrokeShaders>
<FillShaders>
</FillShaders>
</Ellipse>
x y The center position of the ellipse; note: this is the center point of the ellipse, not the starting point of the upper left corner
w h The width and height of the ellipse
Circle
<Circle x="10" y="20" r="50" strokeColor="#ff00ff00" fillColor="#ff000000" weight="5" cap="round" dash="1,2,3,4" strokeAlign="center">
<StrokeShaders>
</StrokeShaders>
<FillShaders>
</FillShaders>
</Circle>
x y Circle center position
r radius
Arc
<Arc x="10" y="20" w="100" h="200" startAngle="10" sweep="50" close="true" strokeColor="#ff00ff00" fillColor="#ff000000" weight="5" cap="round" dash="1,2,3,4" strokeAlign="center">
<StrokeShaders>
</StrokeShaders>
<FillShaders>
</FillShaders>
</Arc>
x y w h Positioning is the same as Ellipse
startAngle starting angle
sweep sweep angle/sector angle
close true if it is closed, otherwise false
Line
<Line x="10" y="20" x1="100" y1="200" strokeColor="#ff00ff00" weight="5" cap="round" dash="1,2,3,4" strokeAlign="center">
<StrokeShaders>
</StrokeShaders>
</Line>
x y x1 y1 line starting point (x, y) / end point (x1, y1)
Notes:
- Alignment align alignV: Only Rectangle supports it, others do not support it. Their x and y all refer to the center point position, and no alignment is required.
- Fill: line graphics ignore fillColor and
<FillShaders>
; graphics with area support both stroke and fill, which are used for stroke and fill respectively.- Priority: When strokeColor and
<StrokeShaders>
exist at the same time,<StrokeShaders>
is used first; the same is true for fillColor and<FillShaders>
.- Stroke color: If a stroke is to appear, at least one of strokeColor and
<StrokeShaders>
must exist; if neither exists, there will be no stroke.- Gradient positioning:
<LinearGradient x="" y="">
... The x and y in are all positioned relative to the graphic element where they are located.
#Custom image
Select any image to display:
config.xml adds an ImagePicker entry, which allows users to select a picture on the phone and obtain its address into a variable (Note: Pictures that are too large may not be displayed, such as photos with too large pixels.)
<Config>
<Group text="custom image" summary="Please crop the image to the appropriate size and location to ensure the display effect. If the image is too large, please reduce it to the screen size first, otherwise it may not be displayed properly or memory usage may be consumed.">
<ImagePicker text="image 1" summary="Select image 1" id="img1"/>
<ImagePicker text="image 2" summary="Select image 2" id="img2"/>
</Group>
</Config>
Image in manifest.xml supports src of Uri type, that is, the image address selected in config, @img1 @img2
<!-- In the personalized lock screen settings, file management and photo album selection pictures do not take effect, so use the following code to solve the problem. -->
<Var name="img1" expression="ifelse(strContains(@img1,'com.android.fileexplorer'),strReplaceAll(@img1,'content://com.android.fileexplorer.myprovider/external_files/','file:///sdcard/'),strContains(@img1,'com.miui.gallery'),strReplaceAll(@img1,'content://com.miui.gallery.open/raw/%2Fstorage%2Femulated%2F0%2F','file:///sdcard/'),@img1)" type="string" const="true"/>
<Var name="img2" expression="ifelse(strContains(@img2,'com.android.fileexplorer'),strReplaceAll(@img2,'content://com.android.fileexplorer.myprovider/external_files/','file:///sdcard/'),strContains(@img2,'com.miui.gallery'),strReplaceAll(@img2,'content://com.miui.gallery.open/raw/%2Fstorage%2Femulated%2F0%2F','file:///sdcard/'),@img2)" type="string" const="true"/>
<Image x="300" y="300" w="300" h="300" src="@img1" srcType="Uri"/>
<Image x="300" y="800" w="300" h="300" src="@img2" srcType="Uri"/>
Clear the image (just leave the corresponding variable blank):
<Button x="300" y="300" w="60" h="60" visibility="#img1.has_bitmap">
<Triggers>
<Trigger action="up">
<VariableCommand name="img1" expression="''" type="string" persist="true"/>
</Trigger>
</Triggers>
</Button>
Sample file: image_picker.mtz (opens new window)
#Commonly used animations
Variable animation
<Var name="numAni">
<VariableAnimation initPause="true" loop="false">
<Item value="0" time="0" />
<Item value="100" time="300" />
</VariableAnimation>
</Var>
<!-- button trigger -->
<Button x="0" y="0" w="1080" h="#screen_height">
<Triggers>
<Trigger action="up">
<AnimationCommand target="numAni" command="play" />
</Trigger>
</Triggers>
</Button>
Attribute | Value | Description |
---|---|---|
initPause | true/false |
initPause="true" When there is no command to execute this animation, it stops in the initial state |
loop | true/false | Whether to loop, the default is true, when loop="false" , it will play once and then stop |
time | Number | Millisecond time, expressions are not supported. Absolute time |
dtime | Number or expression | Millisecond time, supports expressions (new). Relative time (relative to the previous time) |
The difference between dtime and time
<!-- The final value of the two animations are the same -->
<Var name="numAni_0">
<VariableAnimation initPause="false" loop="true">
<Item value="0" time="0" />
<Item value="100" time="1000" />
</VariableAnimation>
</Var>
<Var name="numAni_1">
<VariableAnimation initPause="false" loop="true">
<Item value="0" dtime="0" />
<Item value="50" dtime="500" />
<Item value="100" dtime="500" />
</VariableAnimation>
</Var>
Examples of flexible usage:
<Var name="numAni">
<VariableAnimation name="numAniVar" initPause="true" loop="false">
<Item value="0" time="0" />
<Item value="100" time="300" />
<Triggers>
<!-- When the animation is finished, if #numAni is equal to 100, execute the animation named moveAni -->
<Trigger action="end" condition="#numAni==100">
<AnimationCommand target="moveAni" command="play" />
</Trigger>
<!-- Or: when the animation time #numAniVar is equal to -1, execute the animation named moveAni -->
<Trigger action="end" condition="#numAniVar.current_frame==-1">
<AnimationCommand target="moveAni" command="play" />
</Trigger>
</Triggers>
</VariableAnimation>
</Var>
Array animation
<!-- Free to choose to play -->
<Array count="6" indexName="_ani">
<Var name="arrayAni" size="6" index="#_ani" type="number[]">
<VariableAnimation initPause="true" loop="false">
<Item value="0" time="0" easeType="CubicEaseOut"/>
<Item value="1" time="600"/>
</VariableAnimation>
</Var>
</Array>
<!-- Click trigger -->
<Button x="540" y="0" w="540" h="540">
<Triggers>
<Trigger action="up">
<!-- Play the first animation; targetIndex starts from 0 to 5, a total of 6 -->
<AnimationCommand target="arrayAni" targetIndex="0" command="play" />
<!-- play the second animation -->
<AnimationCommand target="arrayAni" targetIndex="1" command="play" delay="300"/>
</Trigger>
</Triggers>
</Button>
<!-- Play in sequence according to delay time -->
<Array count="10" indexName="_i">
<Var name="Ani" type="number[]" index="#_i" size="10">
<VariableAnimation tag="show1" loop="false" initPause="true">
<Item value="0" dtime="0" />
<Item value="0" dtime="#delay" easeType="BackEaseOut"/>
<Item value="1" dtime="500+#delay"/>
</VariableAnimation>
<VariableAnimation tag="show2" loop="false" initPause="true">
<Item value="1" dtime="0" />
<Item value="1" dtime="#delay" easeType="BackEaseOut"/>
<Item value="0" dtime="500+#delay"/>
</VariableAnimation>
</Var>
<!-- Click trigger -->
<Button x="0" y="0" w="1080" h="#screen_height">
<Triggers>
<Trigger action="up">
<!-- delay 50ms -->
<VariableCommand name="delay" expression="#_i*50"/>
<!-- Play animation -->
<AnimationCommand target="Ani" targetIndex="#_i" tags="show1" command="play" condition="#Ani[0]==0 ** #Ani[9]==0" />
<AnimationCommand target="Ani" targetIndex="#_i" tags="show2" command="play" condition="#Ani[0]==1 ** #Ani[9]==1" />
</Trigger>
</Triggers>
</Button>
<Text x="100+#Ani[#_i]*300" y="100+#_i*100" color="#ff0000" size="50" textExp="'Ani'+#_i+'= '+#Ani[#_i]" />
</Array>
#Element animation
All elements support animation (Image is used as an example below); animation is divided into:
image source, position, size, rotation, transparency
Each animation is independent of each other and plays in a loop. The animation is composed
of several key frames. The key frames include frame attributes and time.
Except for the picture source animation, other animations will find two adjacent key frames
based on the current time, and then calculate linear interpolation. current properties.
If the first frame time does not start from 0, the first frame with the default time of 0 is the original
attribute of the picture, and the time unit is milliseconds.
The position in position animation is the coordinate relative to the picture itself.
Source animation
<Image x="0" y="0" src="pic.png">
<SourcesAnimation>
<Source x="0" y="0" src="pic1.png" time="0"/>
<Source x="0" y="0" src="pic2.png" time="100"/>
<Source x="0" y="0" src="pic3.png" time="200"/>
</SourcesAnimation>
</Image>
Notice: Only images support image source animation
The picture source animation is slightly different, there is no interpolation; x, y are optional, indicating the relative position of the picture. The current picture is specified by finding the first point in the list that is greater than the current time.
Proportional scaling
<Image x="0" y="0" pivotX="50" pivotY="50" src="bg2.png">
<ScaleAnimation loop="false">
<Item value="0" time="0" />
<Item value="1" time="1000" />
</ScaleAnimation>
</Image>
<Image x="0" y="0" pivotX="50" pivotY="50" src="bg.png">
<!--Displacement-->
<PositionAnimation>
<Item x="10" y="0" time="100" />
<Item x="10" y="200" time="1000"/>
</PositionAnimation>
<!--Plane rotation-->
<RotationAnimation>
<Item value="0" time="0" />
<Item value="360" time="2000"/>
</RotationAnimation>
<!--Non-proportional scaling-->
<SizeAnimation>
<Item w="100" h="100" time="0"/>
<Item w="200" h="400" time="1000"/>
<Item w="100" h="100" time="2000"/>
</SizeAnimation>
<!--Transparency-->
<AlphaAnimation>
<Item value="255" time="0"/>
<Item value="100" time="1000"/>
<Item value="255" time="2000"/>
</AlphaAnimation>
</Image>
Example: The position animation indicates that it goes from the leftmost end to the rightmost end of the screen in 1 second, and stays for 1 second. The transparency animation indicates that the starting transparency is 175, and the transparency remains unchanged during the process from the leftmost end to the rightmost end, and gradually becomes opaque 0.5 seconds after reaching the rightmost end. Then for 500 milliseconds it becomes transparent and disappears. Then loop.
<Image x="0" y="#screen_height-177" src="charging_light.png" category="Charging">
<PositionAnimation>
<Item x="0" y="0" time="1000"/>
<Item x="1080" y="0" time="2000"/>
</PositionAnimation>
<AlphaAnimation>
<Item value="175" time="0"/>
<Item value="175" time="1000"/>
<Item value="255" time="1500"/>
<Item value="0" time="2000"/>
</AlphaAnimation>
</Image>
#Animation control
<Image name="wao" x="0" y="0" w="1080" h="114" src="dst.png" >
<PositionAnimation tag="show1" initPause="true" loop="false" >
<Item y="0" time="0" easeType="ExpoEaseOut"/>
<Item y="300" time="300"/>
</PositionAnimation>
</Image>
<Image name="goal" x="0" y="0" w="1080" h="114" src="dst.png" >
<PositionAnimation tag="show1" initPause="true" loop="false" >
<Item y="0" time="0" easeType="ExpoEaseOut"/>
<Item y="300" time="300"/>
</PositionAnimation>
<PositionAnimation tag="show2" initPause="true" loop="false">
<Item y="300" time="0" easeType="ExpoEaseOut"/>
<Item y="600" time="300"/>
</PositionAnimation>
</Image>
<Button x="0" y="0" w="1080" h="#screen_height">
<Triggers>
<Trigger action="up" >
<AnimationCommand target="wao" command="play(0,300)" />
<AnimationCommand target="goal" command="play" tags="show2" />
</Trigger>
</Triggers>
</Button>
Attributes | Description |
---|---|
AnimationCommand | This tag is now used when playing animations. The reason is that the old tags do not support tags. If you are nostalgic, you can remember two |
target | The name of the animation target to control |
Index |
Add index for array animation; both Index="#__ani" needs the name of the indexName attribute at the beginning of the index array.
See array introduction here: Array of Elements and array animation here: Array Animation |
targetIndex | Array animation index index, indicating that the array animation index value is 1; for example, targetIndex="1" means controlling the second animation of the array animation (index starts from 0) |
command | play (play from the beginning), pause (pause), resume (resume playing from the current position) |
tag | Use this tag to preset multiple animation effects for one element, and use tags to selectively play one of them |
play(startTime,endTime,loop,delay) All four parameters support expressions. You can only write the first parameter and ignore the following parameters.
startTime animation start time
endTime animation start time
loop Whether to loop playback, the default is 0, you need to write the number 0, 1 or a numerical expression, if it is 0, it means it will only play once and then stop.
delay Whether delay is supported, the default is 0, you need to write numbers 0, 1 or numerical expressions
Example | Description |
---|---|
play(100) | Play from time 100 to the end, no loop play |
play(100, 500) | Start playing from time 100 to 500, stop |
play(100,500,1) | Play from time 100 to 500, loop play |
#Easing function
<Image name="wao" x="0" y="0" w="1080" h="114" src="dst.png" >
<PositionAnimation tag="show1" initPause="true" loop="false" >
<Item y="0" time="0" easeType="ExpoEaseOut"/>
<Item y="300" time="300"/>
</PositionAnimation>
</Image>
There are two implementation methods for easing animation: easeType and easeExp
#Animation easing comparison table
SineEaseIn
SineEaseOut
SineEaseInOut
QuadEaseIn
QuadEaseOut
QuadEaseInOut
CubicEaseIn
CubicEaseOut
CubicEaseInOut
QuartEaseIn
QuartEaseOut
QuartEaseInOut
QuintEaseIn
QuintEaseOut
QuintEaseInOut
ExpoEaseIn
ExpoEaseOut
ExpoEaseInOut
CircEaseIn
CircEaseOut
CircEaseInOut
BackEaseIn
BackEaseOut
BackEaseInOut
ElasticEaseIn
ElasticEaseOut
ElasticEaseInOut
BounceEaseIn
BounceEaseOut
BounceEaseInOut
Click to view the details: Easing function cheat sheet
- BackEase can take 1 parameter, overshot
- ElasticEase can take 2 parameters, priod and amplitude
- The parameter is a constant, such as BackEaseIn(1.5) ElasticEase(2,3)
- Use easeExp to fill in expressions and reference a built-in variable #__ratio to customize the easing function.
- For example, easeType="QuadEaseIn" can also be implemented with
easeExp="#__ratio*#__ratio"
- easeType has no effect when there is easeExp
- The attribute applies from this frame to the next frame, and the last frame is not used.
<Var name="easeAni">
<VariableAnimation loop="false" initPause="true">
<Item value="0" time="0" easeExp="#__ratio*#__ratio"/>
<Item value="100" time="2000"/>
</VariableAnimation>
</Var>
#Command summary
Example:
<Button x="0" y="0" w="1080" h="#screen_height">
<Triggers>
<Trigger action="up">
<!-- Control the visibility of the corresponding name element. For example, there are -->
<!-- two ways to control the visibility of the element whose name is test_a. -->
<Command target="test_a" property="visibility" value="true/false" />
<Command target="test_a.visibility" value="true/false" />
<!-- Control the corresponding name to play animation. For example, there are two ways -->
<!-- to control the name to play animation for the test_b element. -->
<Command target="test_b.animation" value="play" />
<AnimationCommand target="test_b" command="play" />
<!-- Variable assignment, assign the variable name test1 to 0, pay attention to the variable type, -->
<VariableCommand name="test1" expression="0" />
<!-- Play sound files -->
<SoundCommand sound="famous.wav" volume="0.5" loop="false" keepCur="true" />
<!-- Turn off system charging animation -->
<ExternCommand command="disableChargeAnim" numPara="1"/>
<!-- Enable system charging animation -->
<ExternCommand command="disableChargeAnim" numPara="0"/>
<!-- Turn off under-screen fingerprint -->
<ExternCommand command="disableFod" numPara="1"/>
<!-- Enable under-screen fingerprint -->
<ExternCommand command="disableFod" numPara="0"/>
<!-- Turn off fingerprint recognition animation -->
<ExternCommand command="disableFodAnim" numPara="1"/>
<!-- Turn on fingerprint recognition animation -->
<ExternCommand command="disableFodAnim" numPara="0"/>
<!-- Unlock -->
<ExternCommand command="unlock" />
</Trigger>
</Triggers>
</Button>
#Visibility command
<Command target="test_a" property="visibility" value="true" />
<Command target="test_a.visibility" value="true" />
(Note: The above are just two different ways of writing, with the same effect)
Code | Description |
---|---|
Command | Basic commands can control other elements in the interface through the object name and object properties. What is usually controlled is the visibility and animation of the element. |
target | Control target name |
property | Attribute name, currently supports: visibility |
value | Attribute values, currently for boolean: true, false, toggle |
#Animation commands
<Command target="test_c.animation" value="play" />
<AnimationCommand target="test_c" command="play"/>
The above two lines are two ways of writing animations, with the same effect!
#Voice commands
<SoundCommand sound="famous.wav" volume="0.5" loop="false" keepCur="true" />
Code | Definition |
---|---|
sound | Fill in the sound file path name |
volume | Sound volume, a floating point number from 0 to 1 |
loop | Whether to loop, true/false, the default is false. |
keepCur | When playing this audio, whether to keep the currently playing sound, true/false, default false |
Note: The size of the sound file must not exceed 500kB and the duration must not exceed 10 seconds (sounds after 10 seconds will not be played).
#Variable command
<!--VariableCommand: Used to control the value of a variable (Var) -->
<VariableCommand name="w" expression="#screen_width" delay="100" condition="#switch" />
Code | Description |
---|---|
name | Variable name |
expression | Assignment expression |
condition | Conditional judgment, supports expressions. When the condition in condition is true, the command is executed; when it is false, the command is not executed. |
delay | Delay, measured in milliseconds. After reading the command, delay for a period of time before executing it |
delayCondition | It is a delayed judgment, and the judgment will be made after the delay time |
#Common commands
1. ExternCommand: Used to send commands to external programs
<ExternCommand command="command name" numPara="Parameter 1, numeric expression" strPara="Parameter 2, string expression"/>
<!-- Unlock command, unlock after 100 milliseconds -->
<ExternCommand command="unlock" delay="100" />
<!-- Clear the cache and execute it after the frame animation is played to release the memory. -->
<ExternCommand command="__clearResource" />
2. ExternalCommand: opposite to ExternCommand, it is used to receive external commands. Typical usage: In the lock screen, it is usually used to receive screen opening/closing commands to execute some commands. In desktop plug-ins, it is used to detect screen switching and execute Order
<!--resume is the command executed when the screen is opened, pause is the command executed when the screen is closed-->
<ExternalCommands>
<Trigger action="resume">
<Command target="target.animation" value="play"/>
</Trigger>
<Trigger action="pause">
<VariableCommand name="pause_time" expression="#time_sys"/>
</Trigger>
</ExternalCommands>
<!--Example of using desktop plug-in to switch screens-->
<ExternalCommands>
<Trigger action="resume">
<Command target="__root.animation" value="play"/>
</Trigger>
<Trigger action="pause">
<Command target="__root.animation" value="play"/>
</Trigger>
</ExternalCommands>
# Command to open an app
<IntentCommand action="android.intent.action.MAIN" package="com.android.thememanager" class="com.android.thememanager.ThemeResourceTabActivity" />
<ExternCommand command="unlock" delay="100" />
Remember to add the unlock command, otherwise it will just open in the background and you need to unlock it to see it.
Turn on the flashlight without unlocking:
<!-- It uses 2 images to state the on and off modes through #lightSwitch. By using srcid the 2 images are named: flashlight_0.png, flashlight_1.png -->
<Image x="800" y="#screen_height-150" src="flashlight.png" srcid="#lightSwitch"/>
<Button x="800" y="#screen_height-150" w="150" h="150" >
<Triggers>
<Trigger action="up">
<VariableCommand name="lightSwitch" expression="!(#lightSwitch)" />
<IntentCommand action="miui.intent.action.TOGGLE_TORCH" broadcast="true" >
<Extra name="miui.intent.extra.IS_ENABLE" type="boolean" expression="ifelse(int(@__miui_version_code)}=8,#lightSwitch,1)" />
</IntentCommand>
</Trigger>
</Triggers>
</Button>
Several commonly used ways to write system switches
1. Bluetooth value="on" value="off"
<Command target="Bluetooth" value="toggle"/>
2. Data value="on" value="off"
<Command target="Data" value="toggle"/>
<Command target="Data" value="on"/>
3. Normal/silent/vibration (3 status switches)
<Command target="RingMode" value="toggle"/>
<!--Same effect below as above-->
<Command target="RingMode" value="normal,silent,vibrate"/>
<!--Normal and silent switching only-->
<Command target="RingMode" value="normal,silent"/>
<!--Normal and vibration switching only-->
<Command target="RingMode" value="normal,vibrate"/>
<!--Only switch to mute, use other buttons to switch to other states-->
<Command target="RingMode" value="silent"/>
4.Wifi (toggle/on/off)
<Command target="Wifi" value="toggle"/>
Attribute | Description | Values |
---|---|---|
bluetooth_state | Bluetooth status | 0 off, 1 on, 2 connecting |
data_state | Data status | 0 off, 1 on |
ring_mode | Sound mode | 0 mute, 1 vibrate, 2 normal |
wifi_state | Wifi status | 0 disabled, 1 enabled, 2 problem, 3 connecting |
Note: The status variables of the above four switches must have corresponding buttons to display normally
Example
<!-- 4 commonly used switches in the system -->
<Group nmame="toggle_four" x="0" y="0" >
<!-- 1 Normal/silent switch, images are: ring_0.png、ring_1.png -->
<Image x="94" y="500" src="toggle/ring.png" srcid="ifelse(#ring_mode==2,0,1)"/>
<Button x="94" y="500" w="178" h="178">
<Triggers>
<Trigger action="up">
<Command target="RingMode" value="normal,silent"/>
</Trigger>
</Triggers>
</Button>
<!-- 2 Bluetooth switch, images are: bluetooth_0.png、bluetooth_1.png -->
<Image x="332" y="500" src="toggle/bluetooth.png" srcid="#bluetooth_state!=0"/>
<Button x="332" y="500" w="178" h="178">
<Triggers>
<Trigger action="up">
<Command target="Bluetooth" value="toggle"/>
</Trigger>
</Triggers>
</Button>
<!-- 3 Wifi switch, images are: wifi_0.png、wifi_1.png -->
<Image x="570" y="500" src="toggle/wifi.png" srcid="#wifi_state!=0"/>
<Button x="570" y="500" w="178" h="178">
<Triggers>
<Trigger action="up">
<Command target="Wifi" value="toggle"/>
</Trigger>
</Triggers>
</Button>
<!-- 4 Data switch, images are: data_0.png、data_1.png -->
<Image x="808" y="500" src="toggle/data.png" srcid="#data_state"/>
<Button x="808" y="500" w="178" h="178">
<Triggers>
<Trigger action="up">
<Command target="Data" value="toggle"/>
</Trigger>
</Triggers>
</Button>
</Group>
Sample attachment: Common switch examples lock915.mtz (opens new window)
#Charging animation
With the powerful charging methods of mobile phones, fast charging, super fast charging, wireless charging, wired charging, and the addition of various effects, do you also hope that the theme can achieve various effects? (Parameters are only supported in versions after MIUI11 development version, please pay attention to compatibility when crossing versions)
- Lock screen support shows/disables the default charging animation when entering the charging state.
Attribute | Description |
---|---|
disableChargeAnim | 0 to display, 1 not to display (default is 0) |
Example:
<!-- Turn off system charging animation -->
<ExternCommand command="disableChargeAnim" numPara="1"/>
<!-- Enable system charging animation -->
<ExternCommand command="disableChargeAnim" numPara="0"/>
- Get charging status
#ChargeSpeed 0 normal charging, 1 fast charging, 2 super fast charging, 3 extremely fast charging (120w±)
#ChargeWireState 11 wired charging, 10 wireless charging, -1 not charging
#Under-screen fingerprint
Currently, some Xiaomi phones already support the under-screen fingerprint function. When the under-screen fingerprint is turned on,
it may affect the display of content on the themed lock screen or bring a poor user experience.
For this reason, themed lock screen supports turning off the under-screen fingerprint function.
Note⚠️: It is only supported to turn off the fingerprint function when necessary interactions occur (such as locking
the screen and entering the camera, entering the negative screen, entering the lock screen game, etc.),
Prohibit turning off the fingerprint function in non-interactive scenarios by default.
The under-screen fingerprint function is enabled in the default lock screen state. The lock screen will reset this state after each unlock. Therefore, if you want to turn off the function after loading the lock screen, you need to send a command to the lock screen.
Control whether the under-screen fingerprint function and animation are turned on (the fingerprint icon will disappear if the function is turned off)
ExternCommand command | Description | Parameters |
---|---|---|
disableFod | Fingerprint switch | 0 to open, 1 to close |
disableFodAnim | Fingerprint recognition animation switch | 0 to open, 1 to close |
Example:
<Trigger>
<!-- Turn off under-screen fingerprint -->
<ExternCommand command="disableFod" numPara="1"/>
<!-- Turn off fingerprint recognition animation -->
<ExternCommand command="disableFodAnim" numPara="1"/>
</Trigger>
<Trigger>
<!-- Enable under-screen fingerprint -->
<ExternCommand command="disableFod" numPara="0"/>
<!-- Turn on fingerprint recognition animation -->
<ExternCommand command="disableFodAnim" numPara="0"/>
</Trigger>
Added fingerprint related global variables
Attribute | Definition |
---|---|
fod_enable | Whether the system has under-screen fingerprint enabled: 0 off, 1 on |
fod_x | Fingerprint area x coordinate |
fod_y | Fingerprint area y coordinate |
fod_width | Fingerprint area width |
fod_height | Fingerprint area height |
fod_state_msg | Fingerprint status: 1 finger pressed, 2 finger raised, 3 recognition failed, 4 recognition successful |
Fingerprint demo.mtz (opens new window)
#Control usage
Button
The usage of Trigger in button, Unlocker, Slider, and command group calls are basically the same, but each control has its own characteristics and different usage of commands under various conditions. Here I will explain in detail
#Button
Button elements can be used to receive click, press, double-click, move and other events, and can control other elements on the interface according to the definition of trigger.
Attribute | Definition |
---|---|
x, y, w, h | Specify coordinates and area size |
haptic | Vibrate when haptic="true", provided the user has not turned it off in the system settings |
alignChildren | true/false, the default is false, when it is true, the internal elements are arranged according to absolute coordinates, otherwise they are arranged in relative coordinates |
action | down (press); move (move); up (lift); double (double click) |
interceptTouch | Whether to intercept future touch events to avoid being captured by other Views, for example, in a free desktop widget, you can prevent the desktop from scrolling and entering edit mode when a touch operation is performed on the widget |
Normal | Normal state, the contained elements can only be displayed in this state |
Pressed | When pressed, the contained elements will only be displayed in this state |
Notice:
1. The trigger in the button is included by triggers
2. The coordinates of the Image and other elements in the Button are independent from the Button's own coordinates, and are relative to the Button's parent element.
<Button x="" y="" w="" h="" interceptTouch="true">
<Triggers>
<Trigger action="down">
<VariableCommand name="test1" expression="0" />
</Trigger>
<Trigger action="up">
<VariableCommand name="test2" expression="0" />
</Trigger>
<Trigger action="double">
<VariableCommand name="test3" expression="0" />
</Trigger>
</Triggers>
<Normal>
<Image/>
<Text/>
</Normal>
<Pressed>
<Image/>
<Text/>
</Pressed>
</Button>
Open application
<Button name="WeChatButton" x="#screen_width/2" y="#screen_height/2" w="182" h="182" alignChildren="true" >
<!-- Elements to display on Normal status -->
<Normal>
<!-- Write here the elements that are displayed normally, such as pictures, text, etc. -->
</Normal>
<!-- Elements to display on Pressed status -->
<Pressed>
<!-- Write here Press the displayed element, such as pictures, text, etc. -->
</Pressed>
<Triggers>
<Trigger action="up">
<!-- Execute the unlock command; remember to add the unlock command, otherwise it will just open in the background and you need to unlock it to see it. -->
<ExternCommand command="unlock" />
<!-- Start the application based on action, package name and class name -->
<IntentCommand action="android.intent.action.MAIN" package="com.tencent.mm" class="com.tencent.mm.ui.LauncherUI" />
</Trigger>
</Triggers>
</Button>
[condition attribute] When the condition is met, execute the command="unlock" command:
<Button w="#screen_width" h="#screen_height" >
<Triggers>
<!-- actions: down (press down), move (move), up (lift up), double (double click) -->
<Trigger action="up,cancel">
<!-- condition to satisfy: when #touch_begin_y-#touch_y is greater than or equal to 300, unlock -->
<ExternCommand command="unlock" condition="#touch_begin_y-#touch_y }= 300" />
</Trigger>
</Triggers>
</Button>
Use Button to make a swipe up gesture and unlock it
<!--Unlock text prompts-->
<Text x="#screen_width/2" y="#screen_height-100-#unlockMove" align="center" alignV="center" color="#ffffff" size="42" text="向上滑动解锁"/>
<!--Unlock related variables, animations, buttons-->
<Group name="unlock" >
<!--real time variables-->
<Var name="unlockMove" expression="ifelse(#unlockDown==1,max(#touch_begin_y-#touch_y,0),max(#touch_begin_y-#touch_y,0) { 300,max(#touch_begin_y-#touch_y,0)*(1-#unlockBack),0)" />
<!--animation-->
<Var name="unlockBack">
<VariableAnimation initPause="true" loop="false">
<Item value="0" time="0" easeType="BounceEaseOut" />
<Item value="1" time="300" />
</VariableAnimation>
</Var>
<!--Unlock button-->
<Button w="#screen_width" h="#screen_height" >
<Triggers>
<Trigger action="down">
<VariableCommand name="unlockDown" expression="1"/>
</Trigger>
<Trigger action="up,cancel">
<VariableCommand name="unlockDown" expression="0" />
<Command target="unlockBack.animation" value="play" />
<!--execution condition: unlock when max(#touch_begin_y-#touch_y,0) is greater than or equal to 300px-->
<ExternCommand command="unlock" condition="max(#-#touch_y,0) }= 300" />
</Trigger>
</Triggers>
</Button>
</Group>
unlockMove numeric variable;
unlockBack animation variable, when playing, the value is from 0-1;
Button The size of the button, and whether it is unlocked is determined by condition.
When down, unlockDown is equal to 1, when up, and when cancel, unlockDown is equal to 0.
Let’s start with the explanation of unlockMove. Don’t be scared by the variables. After simplification, it actually looks like this ifelse(x0,y0,x1,y1,0)
When unlockDown==1, the unlockMove value is equal to max(#touch_begin_y-#touch_y,0),
#touch_begin_y minus #touch_y equals the sliding distance,
When max(#touch_begin_y-#touch_y,0) is less than 300, the unlockMove value is equal to max(#touch_begin_y-#touch_y,0)*(1-#unlockBack), otherwise the result is 0
The command executed by the button
1. When down, unlockDown is equal to 1, when up, and when cancel, unlockDown is equal to 0.
2. Play animation unlockBack.animation
3. Condition: when max(#touch_begin_y-#touch_y,0) is greater than or equal to 300, unlock
Note:
- The trigger in the button is contained by triggers
- When alignChildren="true" is defined, the coordinates of the elements in Button are calculated based on the coordinates of the button, otherwise the coordinates are calculated independently
<ExternCommand command="unlock" />
Unlock command, remember to add it, otherwise it will just open in the background
#Slide
Unlocker, Slider
Please read "Lock Screen Getting Started" first
(opens new window)
The usage of Unlocker and Slider are the same. They both activate certain operations by sliding, but Unlocker can unlock directly.
Attribute | Description |
---|---|
StartPoint | Starting point |
EndPoint | Target point |
haptic | Vibrate when haptic="true", provided the user has not turned it off in the system settings |
alignChildren | true/false, when true, internal elements are arranged according to absolute coordinates |
bounceInitSpeed (old solution) | Rebound animation initial speed (distance unit is pixels, time unit is seconds), supports expressions |
bounceAcceleration (old solution) | Rebound animation acceleration (distance unit is pixels, time unit is seconds), supports expressions |
easeType="" or easeExp="" (new solution) | Easing type; viewable: Easing function |
easeTime=""(new plan) | Easing time |
alwaysShow | true/false, the default is false. When one Slider is visible, other Sliders disappear. |
normalSound(StartPoint below) | Specify the sound effects played in the normal state |
pressedSound(StartPoint below) | Specify the sound effect played in the press state |
reachedSound(EndPoint below) | The sound effect played after reaching the endpoint |
NormalState | Normal state |
PressedState | Pressed state |
ReachedState | Activation status |
Note:
- Unlocker and Slider have three states (NormalState, PressedState, ReachedState), and they can contain any interface elements, such as Image Text, etc., or they can not be specified. The relevant elements are only displayed in this state
- In the example, both the starting point (StartPoint) and the target point (EndPoint) can contain three states: NormalState, PressedState, and ReachedState, but the elements contained in the pressed state (PressedState) in the starting point (StartPoint) will change accordingly. Your finger moves
Example:
<Slider name="slider">
<StartPoint x="0" y="1600" w="1080" h="320" easeType="QuadEaseOut" easeTime="1000">
<NormalState>
<Image x="31" y="#screen_height-117" src="unlock_button.png" />
</NormalState>
<!-- Trigger Slider supports Trigger triggering when its state switches. -->
<PressedState>
<Text x="640" y="240" w="640" color="#FE9D01" size="28" text="小米主题" />
</PressedState>
<ReachedState>
<DateTime/>
<Trigger>
<SoundCommand sound="reached.mp3" volume="1"/>
</Trigger>
</ReachedState>
</StartPoint>
<EndPoint x="359" y="#screen_height-117" w="90" h="90">
<Path x="0" y="#screen_height-117">
<Position x="31" y="0" />
<Position x="359" y="0" />
</Path>
<NormalState>
</NormalState>
<PressedState>
</PressedState>
<ReachedState>
</ReachedState>
</EndPoint>
</Slider>
#Music player
<MusicControl name="music_control" y="800" w="1080" h="226" autoShow="true" defAlbumCover="music/default_bg.jpg" enableLyric="true" updateLyricInterval="100" visibility="false" >
<!-- Music window background -->
<Image x="40" y="0" w="1000" h="226" src="music/music_bg.9.png" />
<!-- Album art -->
<Group x="40" w="226" h="226" layered="true">
<Image name="music_album_cover" w="226" h="226" />
<Image w="226" h="226" src="music/music_bg.9.png" xfermode="dst_in" />
</Group>
<!-- Song name, artist name, play time -->
<Text x="296" y="61" w="674" alignV="center" textExp="ifelse(strIsEmpty(@music_control.title),'暂无音乐',@music_control.title)" color="#000000" size="39" marqueeSpeed="30" bold="true"/>
<Text x="296" y="117" w="350" alignV="center" textExp="ifelse(strIsEmpty(@music_control.artist),'--',@music_control.artist)" color="#000000" size="36" marqueeSpeed="30" alpha="128" />
<Text x="296" y="169" w="350" alignV="center" textExp="ifelse(strIsEmpty(@music_control.title),'--:--/--:--',formatDate('mm:ss / ',#music_control.music_position)+formatDate('mm:ss',#music_control.music_duration))" color="#000000" size="33" marqueeSpeed="30" alpha="128" />
<!-- Previous song, play, pause, next song -->
<Button name="music_prev" x="#screen_width - 308" y="76" w="120" h="150" align="right" alignChildren="true">
<Normal>
<Image x="60" y="75" align="center" alignV="center" src="music/prev.png" />
</Normal>
<Pressed>
<Image x="60" y="75" align="center" alignV="center" src="music/prev.png" alpha="128"/>
</Pressed>
</Button>
<Button name="music_play" x="#screen_width - 188" y="76" w="120" h="150" align="right" alignChildren="true">
<Normal>
<Image x="60" y="75" align="center" alignV="center" src="music/play.png" />
</Normal>
<Pressed>
<Image x="60" y="75" align="center" alignV="center" src="music/play.png" alpha="128"/>
</Pressed>
</Button>
<Button name="music_pause" x="#screen_width - 188" y="76" w="120" h="150" align="right" alignChildren="true">
<Normal>
<Image x="60" y="75" align="center" alignV="center" src="music/pause.png" />
</Normal>
<Pressed>
<Image x="60" y="75" align="center" alignV="center" src="music/pause.png" alpha="128"/>
</Pressed>
</Button>
<Button name="music_next" x="#screen_width - 68" y="76" w="120" h="150" align="right" alignChildren="true">
<Normal>
<Image x="60" y="75" align="center" alignV="center" src="music/next.png" />
</Normal>
<Pressed>
<Image x="60" y="75" align="center" alignV="center" src="music/next.png" alpha="128"/>
</Pressed>
</Button>
</MusicControl>
Example: Music player example lock915.mtz (opens new window)
Attribute | Description |
---|---|
autoShow | Whether to automatically display and close the music component according to the system playing music |
defAlbumCover | Specify the default album image and fill in the path file name |
enableLyric | true/false, whether to enable lyrics support. There will be a certain performance loss after turning on lyrics. Don't turn it on when you don't need lyrics |
updateLyricInterval | Update interval of music information. The smaller the interval, the more timely the update will be, but the greater the performance loss will be. Set the interval appropriately. |
music_album_cover | Album image |
clip | The elements within the Group only display the area specified by w h, and the excess parts are cut off |
musicName.title | Song name; get name="musicName" title text |
musicName.artist | Singer name; get name="musicName" album text |
musicName.album | Album name; ps: The album name may have no data |
musicName.lyric_before | Played lyrics |
musicName.lyric_after | Unplayed lyrics |
musicName.lyric_last | Previous lyrics |
musicName.lyric_current | Now playing lyrics |
musicName.lyric_next | Next lyrics |
musicName.lyric_current_line_progress | In-line playback progress of the current line of lyrics, floating point number 0 ~ 1.0 |
musicName.music_duration | Song length, unit ms |
musicName.music_position | Current playing position of the song, unit ms |
musicName.package | Currently playing music application package name |
musicName.class | Currently playing music application class name |
要点:
The song information cannot be obtained before the music is played. You can write a null judgment for the text such as song, singer, album, time, etc. to display the information in the no-data state. Example:
ifelse(strIsEmpty(@musicName.title),'No music yet',@musicName.title)
, whenstrIsEmpty(@musicName.title)==1
, 'No music yet' is displayed, otherwise @music.title is displayedMusic playback duration and total duration can be written using DateTime tags, example:
<DateTime x="" y="" color="" size="42" format="mm:ss" value="#musicName.music_position"/>
- The album picture is a picture with a ratio of 1:1. It can be made into any shape using Image overlay. For example: rectangle, rounded corners, disk; you can also add rotation to the album picture, so that the album picture has a certain effect when playing music.
#Calendar
<!-- Layer has a great optimization effect on elements that are not refreshed internally, but there is one thing to note: it will automatically move to the top of the current interface layer -->
<Layer x="200" y="300" width="560" height="560" frameRate="0" >
<!-- The first day of each month is in the first row of the calendar -->
<Var name="dw0_1" expression="(7-(#date-#day_of_week)%7)%7+1" const="true" />
<!-- Leap year? -->
<Var name="leap_year" expression="eq((#year%4),0)*ne((#year%100),0)+eq((#year%400),0)" const="true" />
<!-- How many days are there in this month? -->
<Var name="max_date" expression="28+ne(#leap_year+ne(#month+1,2),0)+ne((#month+1),2)+ne(#month,1)*ne(#month,3)*ne(#month,5)*ne(#month,8)*ne(#month,10)" const="true" />
<Array name="fde" count="42" indexName="__i" >
<!-- 日一二三四五六 are: d123456 -->
<Text textExp="substr('日一二三四五六',#__i,1)" size="36" x="20+#__i%7*80" y="0" color="#ffffff" visibility="lt(#__i,7)" />
<Rectangle x="#__i%7*80" y="int(#__i/7)*80+70" w="79" h="79" fillColor="argb(55,255,255,255)" visibility="int(#__i-#dw0_1+2)!=#date" />
<!-- Background of the day -->
<Rectangle x="#__i%7*80" y="int(#__i/7)*80+70" w="79" h="79" fillColor="argb(255,50,177,105)" visibility="int(#__i-#dw0_1+2)==#date" />
<DateTime format="d" value="#time_sys+(#__i+2-#date-#dw0_1)*86400000" size="36" x="40+#__i%7*80" y="int(#__i/7)*80+70" align="center" color="#ffffff" alpha="int(120+135*eq(#__i-#dw0_1+2,#date))" visibility="int(#__i-#dw0_1+2)*le(int(#__i-#dw0_1+2),#max_date)" />
<!-- Lunar calendar -->
<DateTime formatExp="ifelse(eqs(formatDate('e',#time_sys+(#__i+2-#date-#dw0_1)*86400000),'初一'),'N月e','e')" value="#time_sys+(#__i+2-#date-#dw0_1)*86400000" size="18" x="40+#__i%7*80" y="50+int(#__i/7)*80+70" align="center" color="#ffffff" alpha="int(120+135*eq(#__i-#dw0_1+2,#date))" visibility="int(#__i-#dw0_1+2)*le(int(#__i-#dw0_1+2),#max_date)" />
</Array>
</Layer>
Return gesture
<!-- Return gesture -->
<Group name="Return" >
<Image x="#about*1080" y="#touch_begin_y" w="#ReturnMove*0.5" h="858" alignV="center" rotation="#about*180" pivotY="429" src="bg/gesture_back_background.png" />
<Image x="#ReturnMove/4+#about*(1080-#ReturnMove/2)" y="#touch_begin_y" align="center" alignV="center" src="bg/gesture_back_arrow.png" visibility="#ReturnMove }= 100" />
<Var name="ReturnMove" expression="ifelse(#ReturnDown==1,min(abs(#touch_begin_x-#touch_x),168),min(abs(#touch_begin_x-#touch_x),168)*(1-#ReturnBack)*int(#ReturnMove}0))"/>
<Var name="ReturnBack">
<VariableAnimation initPause="true" loop="false">
<Item value="0" time="0" easeType="QuadEaseOut" />
<Item value="1" time="200" />
</VariableAnimation>
</Var>
<Button w="1080" h="#screen_height" >
<Triggers>
<Trigger action="down" condition="#touch_x{50 || #touch_x}1030" >
<VariableCommand name="ReturnDown" expression="1"/>
<VariableCommand name="about" expression="#touch_x}540"/>
</Trigger>
<Trigger action="up,cancel" >
<VariableCommand name="ReturnDown" expression="0" />
<AnimationCommand target="ReturnBack" command="play" />
<!-- When the moving distance is greater than 100px, perform animation -->
<AnimationCommand target="" command="play" condition="#ReturnMove}100" />
</Trigger>
</Triggers>
</Button>
</Group>
Return to gesture attachment (opens new window)
#External data
ContentProvider
The content provider provides a general interface for querying application informations, defines new xml code to query the content provider, and binds the queried information to variables to display third-party application information. As long as the third-party application provides the corresponding content provider. For example: it can display weather information, exercise step counting, to-do items, notes, etc.
<VariableBinders>
<ContentProviderBinder name="name1" uri="content://sample/test" uriFormat="" uriParas="" columns="col1,col2" where="" args="" order="" countName="count_name">
<Variable name="variable_name1" type="int" column="col1" row="0"/>
<Variable name="variable_name2" type="string" column="col2" row="0"/>
</ContentProviderBinder>
<ContentProviderBinder name="name2" dependency="name1" />
</VariableBinders>
VariableBinders defines the sources to which various variables are bound. Support ContentProviderBinder, WebServiceBinder, SensorBinder
ContentProviderBinder defines a content provider source and variables bound to it
Attribute | Description |
---|---|
uri | Specify which content provider to use |
uriFormat | If uri needs to add variables, you can use formatting, which needs to be used together with uriParas |
uriParas | Same format as Text element |
columns | Column names to be queried, separated by commas |
where | Query conditions, same as SQL |
args | "where" parameter. |
order | Sort condition, same as SQL |
countName | Bind the query structure number to the variable name |
- Variable define a bind variable
Attribute | Description |
---|---|
name | Variable name |
type | Data type in content provider: string/double/float/int/long |
column | The name of the column to which the variable is bound. |
row | The number of lines the variable is bound to, the default is 0. |
Support where formatting where="" whereFormat="" whereParas=""
Dependency supports dependency relationships, that is, the variables obtained after a certain ContentProviderBinder query are used as parameters of the next ContentProviderBinder query ()
<ContentProviderBinder name="name1" />
<ContentProviderBinder name="name2" dependency="name1" />
- After the query of name1 is completed, the query of name2 will be triggered
- The query of name2 can use the variable of name1
- And if the data of name1 changes and is requeried, a requery of name2 will be triggered
#Notifications
<!-- Query notification -->
<VariableBinders>
<ContentProviderBinder name="data" uri="content://keyguard.notification/notifications" columns="icon,title,content,time,info,subtext,key" countName="hasnotifications">
<List name="notice_list" />
</ContentProviderBinder>
</VariableBinders>
<!-- Notification list ‐ maxHeight is the maximum display height of the list. The excess part will not be displayed. The list can be scrolled up and down. -->
<List name="notice_list" x="0" y="0" w="858" maxHeight="370" data="icon:bitmap,title:string,content:string,time:string,info:string,subtext:string,key:int" visibility="#hasnotifications">
<Item x="0" y="0" w="858" h="185">
<!-- Display size and click area of a single message -->
<Button x="0" y="0" w="858" h="185" alignChildren="true">
<Normal>
<Image x="0" y="0" src="notice_bg.png" />
</Normal>
<Pressed>
<Image x="0" y="0" src="notice_bg.png" alpha="200" />
</Pressed>
<Image x="50" y="21" w="114" h="114" name="notice_icon" />
<Text x="190" y="42" size="32" color="#ee000000" bold="true" w="500" h="36" marqueeSpeed="30" name="notice_title" />
<Text x="190" y="80" size="28" color="#b4000000" w="500" h="42" marqueeSpeed="30" name="notice_content" />
<Text x="800" y="64" size="28" align="right" color="#b4000000" name="notice_time" />
<Triggers>
<Trigger action="up">
<IntentCommand action="com.miui.app.ExtraStatusBarManager.action_remove_keyguard_notification" broadcast="true">
<Extra name="com.miui.app.ExtraStatusBarManager.extra_notification_key" type="int" expression="#notice_list.key" />
<Extra name="com.miui.app.ExtraStatusBarManager.extra_notification_click" type="int" expression="1" />
</IntentCommand>
</Trigger>
</Triggers>
</Button>
</Item>
<AttrDataBinders>
<AttrDataBinder target="notice_icon" attr="bitmap" data="icon" />
<AttrDataBinder target="notice_title" attr="text" data="title" />
<AttrDataBinder target="notice_content" attr="text" data="content" />
<AttrDataBinder target="notice_time" attr="text" data="time" />
</AttrDataBinders>
</List>
#Message notification with slide to delete
<VariableBinders>
<!-- Query notification -->
<ContentProviderBinder name="data" uri="content://keyguard.notification/notifications" columns="icon,title,content,time,key" countName="hasnotifications">
<Variable name="notice_icon0" type="blob.bitmap" column="icon" row="0"/>
<Variable name="notice_icon1" type="blob.bitmap" column="icon" row="1"/>
<Variable name="notice_icon2" type="blob.bitmap" column="icon" row="2"/>
<Variable name="notice_icon3" type="blob.bitmap" column="icon" row="3"/>
<Variable name="notice_title" type="string[]" column="title"/>
<Variable name="notice_content" type="string[]" column="content"/>
<Variable name="notice_time" type="string[]" column="time"/>
<Variable name="notice_key" type="string[]" column="key"/>
<Trigger>
<AnimationCommand target="noticeUp" command="play(0,0)" />
<AnimationCommand target="_noticeAni" command="play(0,0)" />
</Trigger>
</ContentProviderBinder>
</VariableBinders>
<!-- notify -->
<Group y="688">
<Var name="_noticeMove" expression="int(#touch_x-#touch_begin_x)*#noticeMoveDown+#_noticeAni" />
<Var name="_noticeAni">
<VariableAnimation loop="false" initPause="true">
<Item value="0" time="0" />
<Item value="int(#touch_x-#touch_begin_x)" time="100" />
<Item value="ifelse(int(#touch_x-#touch_begin_x)}=300,1080,0)" time="300"/>
<Triggers>
<Trigger action="end" condition="#_noticeAni == 1080">
<AnimationCommand target="noticeUp" command="play" />
</Trigger>
</Triggers>
</VariableAnimation>
</Var>
<Var name="noticeUp">
<VariableAnimation name="noticeUpAni" loop="false" initPause="true">
<Item value="0" time="0" />
<Item value="1" time="300"/>
<Triggers>
<Trigger action="end" condition="#noticeUpAni.current_frame == -1" >
<MultiCommand>
<IntentCommand action="com.miui.app.ExtraStatusBarManager.action_remove_keyguard_notification" broadcast="true" >
<Extra name="com.miui.app.ExtraStatusBarManager.extra_notification_key" type="int" expression="@notice_key[#noticeDown]" />
<Extra name="com.miui.app.ExtraStatusBarManager.extra_notification_click" type="int" expression="0" />
</IntentCommand>
</MultiCommand>
</Trigger>
</Triggers>
</VariableAnimation>
</Var>
<!-- count="4" displays up to 4 messages, size="4" message time, how many messages are displayed will determine the time -->
<Array count="4" indexName="_notice" visibility="#hasnotifications}0" >
<Button x="ifelse(#noticeDown==#_notice,#_noticeMove,0)" y="#_notice*193 - ifelse(#_notice}#noticeDown,#noticeUp*193,0)" w="1080" h="183" alignChildren="true" alpha="ifelse(#noticeDown==#_notice,255-int(#_noticeMove)/3,255)" visibility="#_notice { #hasnotifications" >
<Normal>
<Rectangle x="28" w="1024" h="183" fillColor="#ffffff" cornerRadius="28" />
</Normal>
<Pressed>
<Rectangle x="28" w="1024" h="183" fillColor="#eeeeee" cornerRadius="28" />
</Pressed>
<Text name="noticeTime" x="#screen_width-66" y="62" align="right" alignV="center" color="#BFB7BE" size="32" spacingAdd="1" textExp="@notice_time[#_notice]"/>
<Text x="194" y="62" w="#screen_width-194-66- ifelse(#time_format,100,150)" alignV="center" color="#000000" size="38" marqueeSpeed="30" textExp="@notice_title[#_notice]" bold="true" />
<Text x="194" y="121" w="#screen_width-194-66" alignV="center" color="#606160" size="36" marqueeSpeed="30" textExp="@notice_content[#_notice]" fontFamily="miui-regular" />
<Triggers>
<Trigger action="down">
<VariableCommand name="noticeDown" expression="#_notice"/>
<VariableCommand name="noticeMoveDown" expression="1"/>
</Trigger>
<Trigger action="up,cancel">
<VariableCommand name="noticeMoveDown" expression="0"/>
<AnimationCommand target="_noticeAni" command="play(100,300)" />
<IntentCommand action="com.miui.app.ExtraStatusBarManager.action_remove_keyguard_notification" broadcast="true" condition="abs(#touch_x-#touch_begin_x){10" >
<Extra name="com.miui.app.ExtraStatusBarManager.extra_notification_key" type="int" expression="@notice_key[#noticeDown]" />
<Extra name="com.miui.app.ExtraStatusBarManager.extra_notification_click" type="int" expression="1" />
</IntentCommand>
</Trigger>
</Triggers>
</Button>
</Array>
<!-- Avatar coordinates and dimensions: x=50, y=30 width=60, height=60 -->
<Var name="noticeIconXYWH" type="number[]" values="50,30,60,60" const="true" />
<!-- Notification icon; name icon name, from 0-3, indicating a total of four; visibility, use hasnotifications to control whether the avatar needs to be displayed -->
<Image name="notice_icon0" x="#noticeIconXYWH[0]+ifelse(#noticeDown==0,#_noticeMove,0)" y="#noticeIconXYWH[1]+0*193 - ifelse(0}#noticeDown,#noticeUp*193,0)" w="#noticeIconXYWH[2]" h="#noticeIconXYWH[3]" alpha="ifelse(#noticeDown==0,255-int(#_noticeMove)/3,255)" visibility="#hasnotifications}=1" />
<Image name="notice_icon1" x="#noticeIconXYWH[0]+ifelse(#noticeDown==1,#_noticeMove,0)" y="#noticeIconXYWH[1]+1*193 - ifelse(1}#noticeDown,#noticeUp*193,0)" w="#noticeIconXYWH[2]" h="#noticeIconXYWH[3]" alpha="ifelse(#noticeDown==1,255-int(#_noticeMove)/3,255)" visibility="#hasnotifications}=2" />
<Image name="notice_icon2" x="#noticeIconXYWH[0]+ifelse(#noticeDown==2,#_noticeMove,0)" y="#noticeIconXYWH[1]+2*193 - ifelse(2}#noticeDown,#noticeUp*193,0)" w="#noticeIconXYWH[2]" h="#noticeIconXYWH[3]" alpha="ifelse(#noticeDown==2,255-int(#_noticeMove)/3,255)" visibility="#hasnotifications}=3" />
<Image name="notice_icon3" x="#noticeIconXYWH[0]+ifelse(#noticeDown==3,#_noticeMove,0)" y="#noticeIconXYWH[1]+3*193 - ifelse(3}#noticeDown,#noticeUp*193,0)" w="#noticeIconXYWH[2]" h="#noticeIconXYWH[3]" alpha="ifelse(#noticeDown==3,255-int(#_noticeMove)/3,255)" visibility="#hasnotifications}=4" />
</Group>
#Weather query
#Interface return fields
uri="content://weather/actualWeatherData/1"
ColIndex | Field name | Description | Type (type) | Field value examples | Remarks |
---|---|---|---|---|---|
0 | publish_time | Real-time weather information release time (time mills) | string | 1508143200000 | Can be used to determine the timeliness of real-time information |
1 | city_id | City unique identifier | string | 39.959_116.298 | Longitude and latitude (north latitude_east longitude) |
2 | city_name | City/street name | string | Anningzhuang South Road | If the street can be located, the street name will be returned first, otherwise the city name will be returned |
3 | description | Weather phenomena (real-time) | string | Cloudy | |
4 | temperature | Temperature (real-time) | string | 18℃ | |
5 | temperature_range | Temperature (forecast) | string | 8℃~18℃ | Supports arrays, type="string[]" |
6 | aqilevel | AQI level | int | 90 | See: AQI Level |
7 | locale | Language | string | zh_CN | |
8 | weather_type | Weather type (real-time) | int string |
1 | Arrays are not supported when type="int", arrays are supported when type="string[]" For reference on the corresponding relationship with weather phenomena, see: Weather phenomena code comparison table |
9 | humidity | Humidity | int | 68% | Unit:% |
10 | sunrise | Sunrise time | int | 80760000 | The number of milliseconds since 0 o'clock (only Hour and Minute are used when converting to time, for example, 80760000 is 06:26 after conversion) |
11 | sunset | Sunset time | int | 34440000 | The number of milliseconds since 0 o'clock (when converted into time, only Hour and Minute are taken, 34440000 is 17:34 after conversion) |
12 | wind | Wind direction, wind force | string | Southeast wind, level 2 | |
13 | day | Date offset | int | 1 | 0 represents yesterday, 1 represents today, and 2 represents tomorrow |
14 | pressure | Air pressure | int | 1016hPa | Unit: hPa |
15 | timestamp | Forecast weather information release time (time mills) | string | 1508055000000 | |
16 | tmphighs | Highest temperature (forecast) | string | 18 | Supports arrays, type="string[]" |
17 | tmplows | Minimum temperature (forecast) | string | 8 | Supports arrays, type="string[]" |
18 | forecast_type | Weather type (forecast) | int string |
1 | Arrays are not supported when type="int", arrays are supported when type="string[]" For reference on the corresponding relationship with weather phenomena, see: Weather phenomena code comparison table |
19 | weathernamesfrom | Weather phenomena (forecast) | string | Cloudy | Supports arrays, type="string[]" |
20 | weathernamesto | Same as above | |||
21 | temperature_unit | Temperature unit | int | 1 | 1 represents Celsius, 0 represents Fahrenheit |
22 | water | Temporarily useless (precipitation probability) | - | 50% | Temporarily unavailable |
#AQI Level (Mainland China)
Level | Range |
---|---|
Excellent | 0 <= aqi <= 50 |
Good | 50 < aqi <= 100 |
Mild pollution | 100 < aqi <= 150 |
Moderate pollution | 150 < aqi <= 200 |
Severe pollution | 200 < aqi <= 300 |
Severe pollution | aqi > 300 |
- Weather details for the next 5 days can be obtained at most (including today)
<ExternalCommands>
<Trigger action="init,resume">
<!-- The ContentProvider interface must be actively updated when switching screens. -->
<BinderCommand name="WeatherProvider" command="refresh" />
</Trigger>
</ExternalCommands>
<VariableBinders>
<!-- MIUI weather code; uri old interface: content://weather/weather, please change to the new interface when the old theme is updated: content://weather/actualWeatherData/1 -->
<ContentProviderBinder name="WeatherProvider" uri="content://weather/actualWeatherData/1" columns="city_id,city_name,weather_type,aqilevel,description,temperature,temperature_range,forecast_type,tmphighs,tmplows,wind,humidity,sunrise,sunset,pressure,weathernamesfrom,publish_time,temperature_unit,somatosensory,kinect" countName="hasweather">
<!-- City north latitude east longitude -->
<Variable name="city_id" type="string" column="city_id"/>
<!-- City/street name -->
<Variable name="weather_location" type="string" column="city_name"/>
<!-- Weather type (real-time) -->
<Variable name="weather_id" type="int" column="weather_type"/>
<Variable name="weather_type" type="string[]" column="weather_type"/>
<!-- Today's temperature -->
<Variable name="weather_temperature" type="string" column="temperature"/>
<!-- Today’s temperature range (20°C~30°C) -->
<Variable name="weather_temperature_range" type="string" column="temperature_range"/>
<!-- Weather phenomenon (real-time): sunny -->
<Variable name="weather_description" type="string" column="description"/>
<!-- Sunrise time -->
<Variable name="weather_sunrise" type="int" column="sunrise"/>
<!-- Sunset time -->
<Variable name="weather_sunset" type="int" column="sunset"/>
<!-- wind force -->
<Variable name="weather_wind" type="string" column="wind"/>
<!-- air pressure -->
<Variable name="weather_pressure" type="int" column="pressure"/>
<!-- humidity -->
<Variable name="weather_humidity" type="int" column="humidity"/>
<!-- Weather type (forecast) -->
<Variable name="weather_forecast_type" type="string[]" column="forecast_type"/>
<!-- Weather phenomena (forecast) -->
<Variable name="weather_weathernamesfrom" type="string[]" column="weathernamesfrom"/>
<!-- maximum temperature -->
<Variable name="weather_temphigh" type="string[]" column="tmphighs"/>
<!-- lowest temperature -->
<Variable name="weather_templow" type="string[]" column="tmplows"/>
<!-- Real-time weather information release time -->
<Variable name="weather_publish_time" type="string" column="publish_time"/>
<!-- Temperature unit, 1 represents Celsius, 0 represents Fahrenheit -->
<Variable name="weather_temperature_unit" type="int" column="temperature_unit" />
<!-- AQI level -->
<Variable name="weather_aqi" type="int" column="aqilevel"/>
<Trigger>
<!-- Air quality -->
<VariableCommand name="air_quality" expression="ifelse(#weather_aqi}=0**#weather_aqi{=50,'空气优',#weather_aqi}50**#weather_aqi{=100,'空气良好',#weather_aqi}100**#weather_aqi{=150,'轻度污染',#weather_aqi}150**#weather_aqi{=200,'中度污染',#weather_aqi}200**#weather_aqi{=300,'重度污染',#weather_aqi}300,'严重污染','获取信息异常')" type="string"/>
<!-- A simplified version of weather phenomena; can be used to display weather icons. For example: srcid="#weatherId" -->
<VariableCommand name="weatherId" expression="ifelse(#weather_id}25||#weather_id{0,0, (#weather_id}=4**#weather_id{=6||#weather_id}=8**#weather_id{=11||#weather_id==25),4,#weather_id}=13**#weather_id{=17,13 ,#weather_id}=18**#weather_id{=21||#weather_id==23,18,#weather_id)"/>
</Trigger>
</ContentProviderBinder>
<ContentProviderBinder name="WeatherAqi" dependency="WeatherProvider" uriFormat="content://weatherinfo/aqi/%s" uriParas="@city_id" columns="aqi,pm25,pm10,so2,no2" countName="hasweatherinfo">
<Variable name="aqi" type="int" column="aqi"/>
<Variable name="pm25" type="int" column="pm25"/>
<Variable name="no2" type="int" column="no2"/>
<Variable name="pm10" type="int" column="pm10"/>
<Variable name="so2" type="int" column="so2"/>
</ContentProviderBinder>
</VariableBinders>
<!-- Display data as text -->
<Text x="100" y="200" size="24" color="#ffffff" textExp="@weather_description+' '+@weather_location+' '+#pm25+' '+@air_quality"/>
<Array count="5" indexName="__w" >
<Text x="100" y="300+60*#__w" color="#FFFFFF" size="40" textExp="@weather_forecast_type[#__w]"/>
</Array>
It is to display the queried data in the form of text or graphics. The specific function can be to translate the value of column and then display it through the customized variable name of Variable.
#Weather phenomena codes:
#weather_id=0 it means sunny, =1 means cloudy...
Weather phenomenon coding / weather phenomenon | Weather phenomenon coding / weather phenomenon | Weather phenomenon coding / weather phenomenon |
---|---|---|
0: Clear | 9: Heavy rain | 18: Strong sandstorm |
1: Cloudy | 10: Moderate rain | 19: Sandstorm |
2: Yin | 11: Xiaoyu | 20: Sand |
3: Fog | 12: Sleet | 21: Blowing Sand |
4: Heavy rainstorm | 13: Blizzard | 22: Hail |
5: Heavy rain | 14: Snow shower | 23: Floating Dust |
6: Heavy rain | 15: Heavy snow | 24: Haze |
7:Thunderstorm | 16: Medium Snow | 25: Freezing Rain |
8: Showers | 17: Xiaoxue | 99: None |
#Simplified version of weather icon usage
In daily production, it would be a lot of work to draw 26 weather icons, so we can just group icons of one type into one icon.
Example below:
<ExternalCommands>
<Trigger action="init,resume">
<!-- The ContentProvider interface must be actively updated when switching screens. -->
<BinderCommand name="WeatherProvider" command="refresh" />
</Trigger>
</ExternalCommands>
<VariableBinders>
<ContentProviderBinder name="WeatherProvider" uri="content://weather/actualWeatherData/1" columns="city_name,weather_type,aqilevel,description,temperature,temperature_range" countName="hasweather">
<Variable name="weather_location" type="string" column="city_name"/>
<Variable name="weather_id" type="int" column="weather_type"/>
<Variable name="weather_temperature" type="string" column="temperature"/>
<Variable name="weather_description" type="string" column="description"/>
<Variable name="weather_aqi" type="int" column="aqilevel"/>
<Trigger>
<!-- Air quality -->
<VariableCommand name="air_quality" expression="ifelse(#weather_aqi}=0**#weather_aqi{=50,'Excellent air',#weather_aqi}50**#weather_aqi{=100,'Good air',#weather_aqi}100**#weather_aqi{=150,'Light pollution',#weather_aqi}150**#weather_aqi{=200,'Moderately polluted',#weather_aqi}200**#weather_aqi{=300,'Serious pollution',#weather_aqi}300,'Heavy pollution','Exception in obtaining information')" type="string"/>
<!-- Simplified version of weather type; can be used for weather icon display. For example: srcid="#weatherId" -->
<VariableCommand name="weatherId" expression="ifelse(#weather_id}25||#weather_id{0,0, (#weather_id}=4**#weather_id{=6||#weather_id}=8**#weather_id{=11||#weather_id==25),4,#weather_id}=13**#weather_id{=17,13 ,#weather_id}=18**#weather_id{=21||#weather_id==23,18,#weather_id)"/>
</Trigger>
</ContentProviderBinder>
</VariableBinders>
<!-- Show weather phenomena with pictures -->
<Image x="100" y="100" src="weather.png" srcid="#weatherId"/>
Simplified version of icons corresponding to weather phenomena:
11 weather icons download: 11 weather icons.zip (opens new window)
#Pedometer
New version of pedometer (data source: Xiaomi Health; only supports miui12)
Acquisition of pedometer data in the old version is more complicated and cannot obtain target steps, kilometers, calories, etc., so a new method of obtaining data through "Xiaomi Health" is added (only supported for miui12).
<ExternalCommands>
<Trigger action="resume">
<!-- Open the screen to refresh exercise record data -->
<BinderCommand name="MiSteps" command="refresh" />
</Trigger>
</ExternalCommands>
<VariableBinders>
<!-- Note: Do not uninstall Xiaomi Health App -->
<ContentProviderBinder name="MiSteps" uri="content://com.mi.health.provider.main/activity/steps/brief" column="steps,goal,distance,energy,strength_duration,summary" countName="hasSteps" >
<!-- Step count -->
<Variable name="MiSteps_steps" type="string" column="steps"/>
<!-- Target number of steps -->
<Variable name="MiSteps_goal" type="string" column="goal"/>
<!-- Distance -->
<Variable name="MiSteps_distance" type="string" column="distance"/>
<!-- burn calories -->
<Variable name="MiSteps_energy" type="string" column="energy"/>
<!-- Moderate and high-intensity exercise duration -->
<Variable name="MiSteps_strength_duration" type="string" column="strength_duration"/>
<!-- Is the exercise up to standard? -->
<Variable name="MiSteps_summary" type="string" column="summary"/>
</ContentProviderBinder>
</VariableBinders>
<Textx="100" y="300" alignV="center" color="#ffffffff" size="42" textExp="'Today’s steps:' +@MiSteps_steps+ 'steps'"/>
Attribute | Definition | Unit | Type |
---|---|---|---|
steps | Number of steps | steps | string |
goal | Target number of steps | steps | string |
distance | Distance | Kilometers km | string |
energy | Calories burned | Kcal kcal | string |
strength_duration | Moderate and high-intensity exercise duration | minute minute | string |
summary | Whether exercise is up to standard; 0 None 1 Not yet up to standard 2 Insufficient exercise 3 Exercise up to standard |
string |
Exception handling when obtaining data, refer to the following sample code:
- When the "Xiaomi Health" app is not installed, click to jump to the app store download page
- It has been installed but has never been opened. It prompts the user to obtain data and jumps to the health and exercise page after clicking.
<!-- Determine whether there is Xiaomi Health App -->
<Image name="app_health" x="0" y="0" w="168" h="168" srcType="ApplicationIcon" srcExp="'com.mi.health,com.mi.health.home.HomeActivity'" alpha="0"/>
<Button x="540" y="1000" w="300" h="140" align="center" alignV="center" alignChildren="true">
<Normal>
<Rectangle w="300" h="140" fillColor="#33ffffff" cornerRadius="70"/>
</Normal>
<Pressed>
<Rectangle w="300" h="140" fillColor="#66ffffff" cornerRadius="70"/>
</Pressed>
<Text x="150" y="70" align="center" alignV="center" color="#ffffffff" size="42" textExp="ifelse(#app_health.bmp_width,'打开','安装')"/>
<!-- hint -->
<Text x="150" y="-60" align="center" alignV="center" color="#ffffffff" size="39" textExp="ifelse(!#app_health.bmp_width,'无法获取数据,请安装小米健康',strIsEmpty(@MiSteps_goal),'点击获取数据','查看运动详情')"/>
<Triggers>
<Trigger action="up">
<ExternCommand command="unlock"/>
<!-- Not installed, go to store -->
<IntentCommand action="android.intent.action.VIEW" uri="market://details?id=com.mi.health&ref=mithemelocksreen" flags="268435456" condition="!#app_health.bmp_width" />
<!-- Installed open sports page -->
<IntentCommand action="android.intent.action.VIEW" uri="com.mi.health://localhost/d?action=steps&origin=mithemelocksreen" condition="#app_health.bmp_width"/>
</Trigger>
</Triggers>
</Button>
Note: The new version of step counting above is only supported by MIUI 12.
When adapting to MIUI 11, pay attention to design and production compatibility. You can use the following old version of the code on the V11 theme version.
Old version of pedometer (data source: Xiaomi pedometer)
<ExternalCommands>
<Trigger action="init,resume">
<!-- The ContentProvider interface must be actively updated when switching screens. -->
<BinderCommand name="MiStep" command="refresh" />
</Trigger>
</ExternalCommands>
<VariableBinders>
<ContentProviderBinder name="MiStep" uri="content://com.miui.providers.steps/item" columns="_id,_begin_time,_end_time,_mode,_steps" countName="hassteps" whereFormat="_begin_time>='%d'" whereParas="int((#time_sys-#hour24*3600000-#minute*60000-#second*1000)/1000)*1000">
<Variable name="Mi_step" type="string[]" column="_steps"/>
<Variable name="Mi_begin_time" type="string[]" column="_begin_time"/>
<Variable name="Mi_end_time" type="string[]" column="_end_time"/>
<Trigger>
<!-- Initialize today’s total steps -->
<VariableCommand name="step_today" expression="0" />
<!-- Calculate total steps taken today -->
<LoopCommand count="#hassteps" indexName="__s">
<VariableCommand name="step_today" expression="#step_today+int(@Mi_step[#__s])" />
</LoopCommand>
</Trigger>
</ContentProviderBinder>
</VariableBinders>
<Text x="100" y="100" color="#ffffff" size="40" textExp="#step_today"/>
Parameter name | Meaning | Type |
---|---|---|
_id | The id recorded in sqlite, starting from 1 | string[] |
_begin_time | Pedometer start time (each step count has a start time and end time, and is stored in the phone every once in a while) | string[] |
_end_time | Pedometer end time (each step count has a start time and end time, and is stored in the phone every once in a while) | string[] |
_steps | Number of steps for each dot | string[] |
_mode | Pedometer mode: 0: Not supported, 1: Still, 2: Walking, 3: Running, 11: Cycling, 12: Transportation | string[] |
#Voice assistant
Used to get what the user said? Voice can be used to enhance the ability to lock the screen, which also gives more possibilities. For example: lock screen voice input method, lock screen notepad, etc. Use your imagination, nothing is impossible. Don’t quite understand how to write it? You can first read about External data
<VariableBinders>
<!-- Start recording -->
<ContentProviderBinder name="MiAi" uriFormat="content://com.miui.voiceassist.speech.api/%s" uriParas="@t1" queryAtStart="false" />
<!-- Recognition results -->
<ContentProviderBinder name="MiAiResult" uri="content://com.miui.voiceassist.speech.api/status" columns="AsrStatus,AsrResult" countName="MiAiResult" queryAtStart="true">
<Variable name="Asr_Status" type="string" column="AsrStatus"/>
<Variable name="Asr_Result" type="string" column="AsrResult"/>
</ContentProviderBinder>
</VariableBinders>
<Var expression="#second" threshold="5" >
<Trigger>
<ExternCommand command="pokewakelock"/>
</Trigger>
</Var>
<Rectangle w="1080" h="#screen_height" fillColor="#f2f2f2" />
<Rectangle y="#screen_height-160" w="1080" h="2" fillColor="#000000" alpha="255*0.05" />
<Text x="540" y="1000" align="center" alignV="center" color="#000000" size="45" textExp="@AsrResult"/>
<!-- Recognition results refresh -->
<Var expression="#time" threshold="10" visibility="#MiAiResultRefresh" >
<Trigger>
<BinderCommand name="MiAiResult" command="refresh" />
<VariableCommand name="AsrResult" expression="@Asr_Result" type="string"/>
</Trigger>
</Var>
<Button x="540" y="#screen_height - 80" w="#screen_width-140" h="100" align="center" alignV="center" alignChildren="true" interceptTouch="true">
<Normal>
<Rectangle x="0" y="0" w="#screen_width-140" h="100" fillColor="#FFFFFF" cornerRadius="20"/>
</Normal>
<Pressed>
<Rectangle x="0" y="0" w="#screen_width-140" h="100" fillColor="#E8E8E8" cornerRadius="20"/>
</Pressed>
<Text x="(#screen_width-140)/2" y="50" align="center" alignV="center" color="#000000" size="42" textExp="ifelse(#MiAiResultRefresh,'松开结束','按下说话')"/>
<Triggers>
<Trigger action="down">
<!-- Start refreshing recognition results -->
<VariableCommand name="MiAiResultRefresh" expression="1" />
<AnimationCommand target="moveAni" command="play(0,200)"/>
<!-- exe Whether voice content execution is required -->
<VariableCommand name="t1" type="string" expression="'start?session=20190401&vad=false&exe=false"/>
<BinderCommand name="MiAi" command="refresh" />
<VariableCommand name="Asr_Result" expression="''" type="string" />
</Trigger>
<Trigger action="up,cancel">
<VariableCommand name="MiAiResultRefresh" expression="0" />
<AnimationCommand target="moveAni" command="play(1000,1200)"/>
<VariableCommand name="t1" type="string" expression="'cancel'"/>
</Trigger>
</Triggers>
</Button>
Use the ContentProviderBinder refresh interface to start Xiaomi recording or stop recording.
<!-- Start recording -->
content://com.miui.voiceassist.speech.api/start?session=20190401&vad=false&exe=false
<!-- Cancel -->
content://com.miui.voiceassist.speech.api/cancel
<!-- Stopping recording means you have finished speaking, which is different from canceling. -->
content://com.miui.voiceassist.speech.api/stop
Attribute | Definition |
---|---|
vad | Whether to turn on automatic stop judgment? If it is turned on, there is no need to let go to stop (false, true) |
exe | Whether it is necessary to execute the voice content, that is, what the user said (false, true) |
Here comes the point
Do some interactions based on the value returned by ContentProviderBinder, such as displaying text, animation during recording, etc.
content://com.miui.voiceassist.speech.api/status
Attribute | Definition |
---|---|
AsrStatus | Status; 0 means recognition is over and is idle, 1 means recording is in progress, 2 means recording is over and recognition is in progress |
AsrResult | Recognition results |
AsrVoiceVolume | Real-time recording volume, maximum 100 |
#Digital Wellbeing Bedtime
since MIUI13
<!-- Bedtime -->
<ExternalCommands>
<Trigger action="init,resume">
<!-- The ContentProvider interface must be actively updated when switching screens. -->
<BinderCommand name="clockProvider" command="refresh" />
</Trigger>
</ExternalCommands>
<VariableBinders>
<ContentProviderBinder name="clockProvider" uri="content://com.android.deskclock.bedtimeProvider/bedtime" countName="hasdeskclock">
<!-- Schedule management is not set, and the obtained sleep data is invalid. -->
<Variable name="clock_bedtime_state" type="int" column="bedtime_state"/>
<!-- Hour of bedtime, 24-hour clock -->
<Variable name="clock_sleep_hour" type="int" column="sleep_hour"/>
<!-- minutes of bedtime -->
<Variable name="clock_sleep_minute" type="int" column="sleep_minute"/>
<!-- Hour of wake-up time, 24-hour clock -->
<Variable name="clock_wake_hour" type="int" column="wake_hour"/>
<!-- Minutes of wake-up time -->
<Variable name="clock_wake_minute" type="int" column="wake_minute"/>
<!-- Repeat cycle -->
<Variable name="clock_repeat_type" type="int" column="repeat_type"/>
</ContentProviderBinder>
</VariableBinders>
content://com.android.deskclock.bedtimeProvider/bedtime
Field name | Description | Data type | Remarks |
---|---|---|---|
bedtime_state | Schedule management setting status, 0 off; 1 on | int | |
sleep_hour | Hour of bedtime | int | 24-hour clock |
sleep_minute | Bedtime minutes | int | |
wake_hour | Hour of wake-up time | int | 24-hour clock |
wake_minute | Minutes of wake-up time | int | |
repeat_type | Repeat cycle | int | Every day: 127 Legal working days: -1 Monday to Friday: 31 Custom: other values |
#Alarm Clock
since MIUI13
<ExternalCommands>
<Trigger action="init,resume">
<!-- The ContentProvider interface must be actively updated when switching screens -->
<BinderCommand name="DeskClockProvider" command="refresh" />
</Trigger>
</ExternalCommands>
<VariableBinders>
<ContentProviderBinder name="DeskClockProvider" uri="content://com.android.deskclock/alarm" countName="hasdeskclock">
<!-- Remarks -->
<Variable name="clock_message" type="string[]" column="message"/>
<!-- Switch -->
<Variable name="clock_enabled" type="string[]" column="enabled"/>
<!-- Time -->
<Variable name="clock_hour" type="string[]" column="hour"/>
<!-- Minutes -->
<Variable name="clock_minute" type="string[]" column="minutes"/>
<!-- Ring time -->
<Variable name="clock_alarmtime" type="string[]" column="alarmtime"/>
<!-- Repeat method -->
<!-- 0: One-time -->
<!-- 1: Monday -->
<!-- 2: Tuesday -->
<!-- 4: Wednesday -->
<!-- 8: Thursday -->
<!-- 16: Friday -->
<!-- 32: Saturday -->
<!-- 64: Sunday -->
<!-- 128: Legal working days -->
<!-- 256: Legal holidays -->
<Variable name="clock_daysofweek" type="string[]" column="daysofweek"/>
</ContentProviderBinder>
</VariableBinders>
content://com.android.deskclock/alarm
Field name | Description | Data type | Remarks |
---|---|---|---|
message | Remarks | string[] | |
enabled | Switch | string[] | |
hour | When | string[] | |
minutes | Points | string[] | |
alarmtime | Ring time | string[] | |
daysofweek | Repeat style | string[] |
0: One-time 1: Monday 2: Tuesday 4: Wednesday 8: Thursday 16: Friday 32: Saturday 64: Sunday 128: Legal working day 256: Legal holiday |
#To-do list
<ExternalCommands>
<Trigger action="init,resume">
<!-- The ContentProvider interface must be actively updated when switching screens. -->
<BinderCommand name="calendarEvents" command="refresh" />
</Trigger>
</ExternalCommands>
<VariableBinders>
<ContentProviderBinder name="calendarEvents" uri="content://com.android.calendar/events" order="dtstart ASC" whereFormat="hasExtendedProperties=='%d' AND dtstart>='%d' AND dtstart<='%d'" whereParas="0,int((#time_sys-#hour24*3600000-#minute*60000-#second*1000)/1000)*1000,int((#time_sys-#hour24*3600000-#minute*60000-#second*1000+86400000)/1000)*1000" countName="events">
<!-- Title -->
<Variable name="calendar_title" type="string[]" column="title"/>
<!-- Location, null if not available -->
<Variable name="calendar_eventLocation" type="string[]" column="eventLocation"/>
<!-- Reminder start time-->
<Variable name="calendar_dtstart" type="string[]" column="dtstart"/>
<!-- Reminder end time -->
<Variable name="calendar_dtend" type="string[]" column="dtend"/>
<!-- All day events -->
<Variable name="calendar_allDay" type="string[]" column="allDay"/>
<!-- The last time of the event, empty means infinite -->
<Variable name="calendar_lastDate" type="string[]" column="lastDate"/>
<!-- Type of schedule -->
<Variable name="calendar_hasExtendedProperties" type="string[]" column="hasExtendedProperties"/>
</ContentProviderBinder>
</VariableBinders>
content://com.android.calendar/events
Field name | Description | Data type | Field name indication | Remarks |
---|---|---|---|---|
title | Title | string[] | ||
eventLocation | Location, null if not | string[] | ||
dtstart | Reminder start time | string[] | ||
dtend | Reminder end time | string[] | ||
allDay | All-day events | string[] | 0 is not; 1 is | |
lastDate | The last time of the event, empty means infinite | string[] | 10800000 | Time stamp representation from 0 o'clock |
hasExtendedProperties | Type | string[] | Type of schedule (countdown day; anniversary; birthday; schedule) |
#Sensor call
SensorBinder supports gravity sensing, direction sensing, acceleration sensing, air pressure sensing (altitude)
#Gravity sensor
<VariableBinders>
<SensorBinder type="gravity" rate="2">
<Variable name="gravity_x" index="0"/>
<Variable name="gravity_y" index="1"/>
<Variable name="gravity_z" index="2" />
</SensorBinder>
</VariableBinders>
Attribute | Definition |
---|---|
index="0" | Gravity acceleration in x direction |
index="1" | Gravity acceleration in y direction |
index="2" | Gravity acceleration in z direction |
rate | refresh rate, has 4 special values 0, 1, 2, 3; default is 3 (if not written) |
Note: The smaller the value, the higher the refresh rate/the smoother the movement, and it will also consume more power.
- 0 means 0 microseconds
- 1 is 20000 microseconds
- 2 is 66667 microseconds
- 3 is 200000 microseconds
#Orientation sensor
<VariableBinders>
<SensorBinder type="orientation">
<Variable name="orientation0" index="0"/>
<Variable name="orientation1" index="1"/>
<Variable name="orientation2" index="2" />
</SensorBinder>
</VariableBinders>
Attribute | Definition |
---|---|
index="0" | Azimuth angle, 0~359, 0=north, 90=east, 180=south, 270=west |
index="1" | Pitch angle, -180 ~ 180, the z-axis turns to the positive direction of the y-axis |
index="2" | Roll angle,-90 ~ 90, the x-axis turns to the positive direction of the z-axis |
#Accelerometer
<VariableBinders>
<SensorBinder type="accelerometer">
<Variable name="accelerometer_x" index="0"/>
<Variable name="accelerometer_y" index="1"/>
<Variable name="accelerometer_z" index="2"/>
</SensorBinder>
</VariableBinders>
Attribute | Definition |
---|---|
index="0" | Acceleration in x direction |
index="1" | Acceleration in y direction |
index="2" | Acceleration in z direction |
#Linear acceleration sensor
<VariableBinders>
<SensorBinder type="linear_acceleration">
<Variable name="line_x" index="0"/>
<Variable name="line_y" index="1"/>
<Variable name="line_1" index="2"/>
</SensorBinder>
</VariableBinders>
Linear acceleration removes the influence of gravity acceleration: acceleration = linear acceleration + gravity acceleration
#Air pressure sensor
<VariableBinders>
<SensorBinder type="pressure">
<Variable name="pressure" index="0"/>
</SensorBinder>
</VariableBinders>
Attribute | Definition |
---|---|
index="0" | Barometric pressure value, unit hPa. The average air pressure at sea level is 1013.25hPa. The altitude can be estimated based on the air pressure value |
#Broadcast
Broadcasting is divided into two parts: sending and receiving
- The sending part is usually placed in Trigger
<ExternalCommands>
<Trigger action="pause">
<IntentCommand action="initialization" broadcast="true">
<Extra name="bg_number" type="number" expression="#rand_num" />
</IntentCommand>
</Trigger>
</ExternalCommands>
- The receiving part is in VariableBinders
<VariableBinders>
<BroadcastBinder action="initialization">
<Variable name="wallpaper_num" type="number" extra="bg_number" />
</BroadcastBinder>
</VariableBinders>
The name in the action attribute can be defined by yourself, as long as it is consistent.
#Dark mode
Instructions for use
Designers can customize the display effect after turning on [Dark Mode] according to the global variable #__darkmode_wallpaper
#__darkmode
.
First, you need to add the customizedDarkModeWallpaper="true" attribute to the root node of xml, such as:
- Themed wallpaper
<!-- customizedDarkModeWallpaper="true" Custom dark mode enabled -->
<MiWallpaper version="2" frameRate="5" screenWidth="1080" customizedDarkModeWallpaper="true">
<Var name="bgScale" expression="ifelse(#screen_height}2160,#screen_height/2160,1)" const="true" />
<!-- #__darkmode_wallpaper Turn on the dark mode and enable the darkening effect. When the value is 1; srcid="1" displays the image "bg_1.jpg" -->
<Image pivotX="540" pivotY="0" scale="#bgScale" src="bg.jpg" srcid="#__darkmode_wallpaper" />
</MiWallpaper>
- Themed lock screen
<!-- customizedDarkModeWallpaper="true" Custom dark mode enabled -->
<Lockscreen version="2" frameRate="60" screenWidth="1080" customizedDarkModeWallpaper="true">
</Lockscreen>
customizedDarkModeWallpaper defaults to false. When [Dark Mode and Wallpaper Dimming] is turned on, the default is a unified darkening effect; if true, a custom darkening effect is supported.
Development version after MIUI12 20.6.1 supported
Global variables | Definition | Description |
---|---|---|
__darkmode_wallpaper | Whether to enable dark mode and support darkening wallpaper | 0 means not enabled 1 means enabled |
__darkmode | Whether to enable dark mode | 0 means not enabled 1 means enabled |
#Lock screen settings
Lock screen personalization
The theme can bring a configuration description file to describe the items that can be personalized and
configured (config.xml and manifest.xml are placed in the same directory). Configurable items include:
- Switch
- Text input
- Text selection
- Number input
- Number selection
- Program shortcuts
- Custom image
Example:
<!-- Config root node -->
<Config>
<!-- Setting group text Setting group name, text displayed in the setting interface -->
<Group text="date time">
<!-- Switch (summary: detailed description of the setting item; id: variable name corresponding to the setting item; default: default value) -->
<CheckBox text="" summary="" id="show_date" default="0"/>
<!-- Text input -->
<StringInput text="Date format" summary="" id="format" default="MM/dd"/>
<!-- Digital input -->
<NumberInput text="text size" summary="text size" id="size_date" default="28"/>
</Group>
<Group text="please choose">
<!-- Text selection -->
<StringChoice text="" summary="" customizable="true" id="time_format">
<!-- Text selection item value variable value text interface displays text -->
<Item value="hh:mm" text="12 hours"/>
<Item value="kk:mm" text="24 hours"/>
</StringChoice>
<!-- Number selection -->
<NumberChoice text="" summary="" id="">
<!-- Number selection item value value text interface displays text -->
<Item value="0" text="Mode 1"/>
<Item value="1" text="Mode 2"/>
</NumberChoice>
</Group>
<Group text="Custom picture" summary="Please crop the image to the appropriate size and location first to ensure the display effect.">
<ImagePicker text="Picture 1" summary="Select picture 1" id="img1"/>
<ImagePicker text="Picture 2" summary="Select picture 2" id="img2"/>
</Group>
<Group text="A shortcut">
<AppPicker text="Left shortcut" id="left_task"/>
<AppPicker text="Right shortcut" id="right_task"/>
</Group>
</Config>
Clear the image (just leave the corresponding variable blank). See custom image usage here: Custom image
<Image x="0" y="0" src="@img1" srcType="Uri"/>
<Image x="0" y="500" src="@img2" srcType="Uri"/>
<Button x="540" y="#screen_height/2" w="280" h="280" alignChildren="true" visibility="#img1.has_bitmap">
<Rectangle w="280" h="280" fillColor="#ffffff" alpha="128" />
<Triggers>
<Trigger action="up">
<VariableCommand name="img1" expression="" type="string" persist="true" />
</Trigger>
</Triggers>
</Button>
A shortcut to quickly enter the lock screen personalization interface (Button)
<Button x="540" y="#screen_height/2+300" w="280" h="280" alignChildren="true">
<Rectangle w="280" h="280" fillColor="#ffffff" alpha="128" />
<Triggers>
<Trigger action="up">
<IntentCommand action="android.intent.action.MAIN" package="com.android.thememanager" class="miui.maml.MamlConfigSettings">
<Extra name="maml_code" type="string" expression="'lockstyle'"/>
</IntentCommand>
<ExternCommand command="unlock"/>
</Trigger>
</Triggers>
</Button>
#Internazionalization
MAML supports multiple languages. The following takes the lock screen as an example (the lock screen files are all in the lockscreen/advance folder, which we use as the root directory here) There are two types of multi-language adaptation: image resource adaptation and string adaptation:
- Image resource adaptation: the default location is the root directory, but for non-default ones, you need to create a new folder for the corresponding language (such as: images_en, images_cn_TW). For example, default image: a.png, the localized image path for traditional chinese is: images_zh_TW/a.png, for english is images_en/a.png
- Adaptation of string resources: the default is: strings/strings.xml, the non-default ones have the language suffix, for example strings/strings_zh_CN.xml for simplified cinese and strings/strings_zh_TW.xml for traditional chinese.
Look at the example below:
<!-- strings.xml contents -->
<strings>
<string name="musicName" value="Music player"/>
</strings>
<!-- strings_zh_CN.xml contents -->
<strings>
<string name="musicName" value="打开音乐播放器"/>
</strings>
<!-- strings_zh_TW.xml contents -->
<strings>
<string name="musicName" value="打開音樂播放器"/>
</strings>
manifest.xml Variables can be used directly in @musicName
- Adaptation of custom configuration files (just add the suffix directly to the config file)
Attribute | Description |
---|---|
English | config.xml |
Simplified Chinese | config_zh_CN.xml |
Traditional Chinese | config_zh_TW.xml |
When adapting to multiple languages, it is recommended that config.xml be in English, and then adapt to other languages.
#Advanced Tutorial
Dynamic frame rate
- Common usage
<?xml version="1.0" encoding="utf-8"?>
<Lockscreen version="1" frameRate="30" screenWidth="1080">
</Lockscreen>
- Common usage two (when charging has animation effects, you can add charging/full battery/low battery to the label to specify the frame rate, so that the frame rate of the specified battery status is 60-120, thereby making the animation smoother)
<?xml version="1.0" encoding="utf-8"?>
<Lockscreen version="1" frameRate="30" frameRateCharging="60" frameRateBatteryLow="20" frameRateBatteryFull="30" screenWidth="1080">
</Lockscreen>
Attribute | Code |
---|---|
frameRate | Specify the frame rate, the default is 30. Note: Currently, the screen refresh rate of Xiaomi mobile phones is up to 120hz. |
frameRateCharging | Lock screen frame rate during charging |
frameRateBatteryLow | Lock screen frame rate when battery is low |
frameRateBatteryFull | Lock screen frame rate when the battery is full (100%) |
- Advanced usage
Dynamically adjust the frame rate, for example, when playing animation or sliding the screen, increase the frame rate to 60fps or 120fps to ensure that animation and sliding are not stuck; at other times, it can be reduced to 30fps, while ensuring smoothness. Can take into account power saving.
Note: frameRate="120" is currently supported by mobile phones with high refresh rates. For mobile phones that do not support 120, the highest refresh rate supported by the mobile phone hardware shall prevail. (For example, when writing 120, the highest resolution on a 60hz model will only reach 60hz)
<FramerateController name="framerateControllerAni" initPause="true" loop="false" >
<ControlPoint frameRate="120" time="0"/>
<ControlPoint frameRate="120" time="2000"/>
<ControlPoint frameRate="30" time="2001"/>
</FramerateController>
<!-- Play when the frame rate needs to be increased; for example, when there is a screen-opening animation, play it when the screen is opened. -->
<AnimationCommand target="framerateControllerAni" command="play"/>
<!-- Increase frame rate when tapping/swiping the screen -->
<Button x="0" y="0" w="#screen_width" h="#screen_height">
<Triggers>
<Trigger action="down,up,cancel">
<AnimationCommand target="framerateControllerAni" command="play"/>
</Trigger>
<Trigger action="move">
<AnimationCommand target="framerateControllerAni" command="play" condition="#frame_rate{55" />
</Trigger>
</Triggers>
</Button>
Array of elements
It means that interface elements (graphics, text...) are displayed in a specific regular form to avoid duplication of similar codes.
For example: I want to display 100 80x80 rectangles in the form of 10 rows and 10 columns (the spacing is 20 pixels). If it were normal,
we would have to write at least 100 lines of code, but now it only takes a few lines of code to use an element array.
Example:
<Array x="50" y="500" count="100" indexName="__i" >
<!-- For usage of % and int, please see Expression -->
<Rectangle x="#__i%10*100" y="int(#__i/10)*100" w="80" h="80" fillColor="#99ffffff" />
<Text textExp="#__i" size="36" align="center" x="#__i%10*100+40" y="int(#__i/10)*100+20" color="#000000" />
</Array>
Attribute | Definition |
---|---|
Array | The label of the element array (which is to arrange the elements) |
indexName | The index name can be understood as the variable name used to number each element in the element array |
count | Specifies the number of elements of the same type in this element array, which is the range of your customized variable names in indexName. Count in Array does not support expression |
Loop processing
Mainly used in conjunction with arrays, it can save a lot of code and improve efficiency.
Example:
<Var name="find" type="number[]" size="100" const="true" />
<Array x="100" y="300" count="100" indexName="__i" >
<Text textExp="#find[#__i]" size="30" align="center" x="#__i%10*90" y="int(#__i/10)*90" color="#ffffffff" />
</Array>
<Button x="0" y="0" w="1080" h="1920">
<Triggers>
<Trigger action="up">
<LoopCommand count="100" indexName="col">
<VariableCommand name="find" type="number[]" index="#col" expression="#col" />
</LoopCommand>
</Trigger>
</Triggers>
</Button>
Attribute | Description |
---|---|
LoopCommand | Loop tag |
indexName | Exponent name, variable name used to mark the number of loop calculations (when #col is equal to 0, it means the first calculation) |
count | Specifies the number of loops |
begin | Start calculation when the variable specified by indexName reaches a certain value |
end | Terminate calculation when the variable specified by indexName reaches a certain value |
loopCondition | Loop conditions, which can be used to interrupt the loop |
The following paragraph is equivalent to the previous <LoopCommand/>
code
<LoopCommand begin="0" end="99" indexName="col">
<VariableCommand name="find" type="number[]" index="#col" expression="#col" />
</LoopCommand>
Bezier Curve
Important: CanvasDrawer is independent and must first calculate the scaling ratio at other resolutions.
<?xml version="1.0" encoding="UTF-8"?>
<Lockscreen version="2" frameRate="60" screenWidth="1080">
<Var name="orix" type="number" expression="400" const="true"/>
<Var name="oriy" type="number" expression="400" const="true"/>
<Var name="startx" type="number" expression="0" const="true"/>
<Var name="starty" type="number" expression="0" const="true"/>
<Var name="endx" type="number" expression="800" const="true"/>
<Var name="endy" type="number" expression="0" const="true"/>
<!-- Initialize first -->
<ExternalCommands>
<Trigger action="init">
<!-- scale_mum: CanvasDrawer is independent, first calculate the scaling ratio at other resolutions -->
<VariableCommand name="scale_mum" expression="int(#raw_screen_width/1.08)/1000" />
<MethodCommand targetType="ctor" class="android.graphics.Path" return="path" returnType="object"/>
<MethodCommand target="path" targetType="var" method="moveTo" paramTypes="float,float" params="#startx,#starty"/>
<MethodCommand target="path" targetType="var" method="quadTo" paramTypes="float,float,float,float" params="#orix,#oriy,#endx,#endy"/>
<MethodCommand targetType="ctor" class="android.graphics.Paint" return="paint" returnType="object"/>
<MethodCommand targetType="var" class="miui.maml.util.ReflectionHelper" method="getEnumConstant" paramTypes="String,String" params="'android.graphics.Paint$Style','STROKE'" return="style" returnType="object"/>
<MethodCommand target="paint" targetType="var" method="setStyle" paramTypes="android.graphics.Paint$Style" params="'style'"/>
</Trigger>
</ExternalCommands>
<Rectangle x="0" y="0" w="1080" h="1920" fillColor="argb(255,255,255,255)" touchable="true">
<Triggers>
<Trigger action="down" >
<!-- initial point -->
<VariableCommand name="x0" type="number" expression="#touch_x"/>
<VariableCommand name="y0" type="number" expression="#touch_y"/>
</Trigger>
<!-- The sliding process will refresh the data -->
<Trigger action="move" >
<VariableCommand name="dx" type="number" expression="#touch_x-#x0"/>
<VariableCommand name="dy" type="number" expression="#touch_y-#y0"/>
<MethodCommand target="path" targetType="var" method="reset"/>
<MethodCommand target="path" targetType="var" method="moveTo" paramTypes="float,float" params="#startx,#starty"/>
<MethodCommand target="path" targetType="var" method="quadTo" paramTypes="float,float,float,float" params="#orix+#dx,#oriy+#dy,#endx,#endy"/>
</Trigger>
<Trigger action="up" >
<VariableCommand name="orix" type="number" expression="#orix+#dx"/>
<VariableCommand name="oriy" type="number" expression="#oriy+#dy"/>
</Trigger>
</Triggers>
</Rectangle>
<Group x="140" y="240" scale="#scale_mum">
<!-- CanvasDrawer; Canvas! I’m so impressed by this feature! Good use everyone! Thank you, the father of MAML -->
<CanvasDrawer x="0" y="0">
<Triggers>
<Trigger action="draw" >
<MethodCommand target="paint" targetType="var" method="setStrokeWidth" paramTypes="float" params="8"/>
<MethodCommand target="paint" targetType="var" method="setColor" paramTypes="int" params="0xff00ff00"/>
<MethodCommand target="__objCanvas" targetType="var" method="drawPath" paramTypes="android.graphics.Path,android.graphics.Paint" params="'path','paint'"/>
<MethodCommand target="paint" targetType="var" method="setStrokeWidth" paramTypes="float" params="0"/>
<MethodCommand target="paint" targetType="var" method="setColor" paramTypes="int" params="0xff000000"/>
<MethodCommand target="paint" targetType="var" method="setTextSize" paramTypes="float" params="40"/>
<MethodCommand target="__objCanvas" targetType="var" method="drawTextOnPath" paramTypes="String,android.graphics.Path,float,float,android.graphics.Paint" params="'Hello World! This is a new awesome feature. Hello, this is a great new skill!','path',0,-25,'paint'"/>
</Trigger>
</Triggers>
</CanvasDrawer>
</Group>
</Lockscreen>
#Other related
Other related
<!-- To prevent a black screen when testing the lock screen in the initial stage, you can add this code to facilitate testing the lock screen effect. -->
<!-- Note: This code cannot be displayed directly when the theme is finally online, otherwise the lock screen will always be on and will not turn off; you can use conditions to control when it can be executed -->
<Var expression="#second" threshold="5" >
<Trigger>
<ExternCommand command="pokewakelock"/>
</Trigger>
</Var>
Equivalent to triggering once every 5 seconds (second). pokewakelock wakes up the screen
Wallpaper adaptive algorithm for all models
<Image name="i_bg" x="#screen_width/2" y="#screen_height/2" pivotX="#screen_width/2" pivotY="#screen_height/2" scale="max(#screen_width/#i_bg.bmp_width,#screen_height/#i_bg.bmp_height)" align="center" alignV="center" src="bg.jpg"/>
Applicable objects: pictures of any size. (The custom Image is not
, because wallpaper has its own adaptive scaling algorithm)
Principle: The width is not enough to make it wider, and the height is not enough to make it taller. That is: when the wallpaper is aligned and tiled according to the center of the screen, if the area is not filled, the entire wallpaper is scaled according to the ratio of the first two to fill the unfilled area.
#Full-screen model lock Screen element adaptation
Currently, mobile phone resolutions vary, so when we write code when making a lock screen, we need to pay attention to the fact that the elements at the bottom of the lock screen need to be written in relative coordinates, not absolute coordinates.
Solution:
- There is a screen_height in the global variable, Indicates the corresponding screen height under the resolution set by your current code
- The coordinates of the button at the bottom of the screen are based on the screen height (screen_height), which in layman's terms means how many pixels go up from the bottom
Example (here, take the small icons on both sides of the bottom of the system default lock screen as an example):
<!-- Small icons on the bottom left and right -->
<Group y="#screen_height-110">
<Image x="115" y="0" align="center" alignV="center" src="icon_left.png"/>
<Image x="#screen_width-115" y="0" align="center" alignV="center" src="icon_right.png"/>
</Group>
Interpretation: in y="#screen_height-110" the coordinate represents the screen height minus 110, which is the final display position of the bottom icon. Regardless of the model, the resolution height of the model minus 110 is used. This ensures that the bottom buttons of different models will be at the bottom.
Selecting pictures in personalization settings, file management and photo album does not take effect, use this line of code to solve it
<Var name="diy_img_var" type="string" expression="ifelse(strContains(@diy_img,'com.android.fileexplorer'),strReplaceAll(@diy_img,'content://com.android.fileexplorer.myprovider/external_files/','file:///sdcard/'),strContains(@diy_img,'com.miui.gallery'),strReplaceAll(@diy_img,'content://com.miui.gallery.open/raw/%2Fstorage%2Femulated%2F0%2F','file:///sdcard/'),@diy_img)"/>
<Image x="540" y="#screen_height/2" align="center" alignV="center" w="200" h="200" srcType="Uri" srcExp="@diy_img_var" />
Remember to put the following code into config.xml
<?xml version="1.0" encoding="utf-8"?>
<Config>
<Group text="custom image">
<ImagePicker text="custom image" summary="Best image size: 200x200" id="diy_img"/>
</Group>
</Config>
Add a large background to the clock plug-in in Themed Wallpaper
Put it in the clock plug-in:
<ExternalCommands>
<Trigger action="resume">
<!-- Send a broadcast with a different value each time (used to trigger calculations after Themed Wallpaper receives the broadcast) -->
<IntentCommand action="initialization" broadcast="true">
<Extra name="bg_number" type="number" expression="#time%1000000" />
</IntentCommand>
</Trigger>
</ExternalCommands>
Put it in the ever-changing wallpaper:
<VariableBinders>
<!-- After receiving the broadcast from the clock plug-in, start calculating the value of wallpaper_offset_x when there is a clock, and record it with ui_num -->
<BroadcastBinder action="initialization">
<Variable name="bg_num" type="number" extra="bg_number" />
<Trigger condition="#bg_num">
<VariableCommand name="ui_num" expression="int(#wallpaper_offset_x*10000000)" />
</Trigger>
</BroadcastBinder>
</VariableBinders>
<!-- background -->
<Rectangle x="0" y="0" w="1080" h="2160" fillColor="#fffcfed8" >
<!-- wallpaper_offset_step: When wallpaper_offset_x is equal to ui_num, the transparency is 255; single-screen offset, when the absolute value of the difference between the current value of wallpaper_offset_x and ui_num is equal to wallpaper_offset_step, the transparency is 0 -->
<Rectangle x="0" y="0" w="1080" h="1080" fillColor="#fffcfed8" alpha="255-int(255*abs(int(#wallpaper_offset_x*10000000)-#ui_num)/10000000/#wallpaper_offset_step)" >
<FillShaders>
<LinearGradient x="0" y="0" x1="0" y1="1080" tile="clamp">
<GradientStop color="#ff46e1c6" position="0"/>
<GradientStop color="#fffcfed8" position="1"/>
</LinearGradient>
</FillShaders>
</Rectangle>
Countdown
The calculation logic of the countdown is actually very simple. Before looking at the following, first understand the rules of the two-point calendar (one leap year every four years, no leap year in one hundred years, and one extra day in February when there is a leap year in four hundred years). The countdown below starts with 0000 The year is the reference year, and the calculation method is: how many days have passed since the target year to 0000, how many days have passed since the current time, and the difference between the two values is calculated.
VERIFY New Year\'s Eve ESCAPING SINGLE QUOTE!
<?xml version="1.0" encoding="UTF-8"?>
<Lockscreen frameRate="30" screenWidth="1080" version="1">
<ExternalCommands>
<Trigger action="resume">
<VariableCommand name="djs_text" expression="ifelse(strIsEmpty(@djs_text),'New Year\'s Eve',@djs_text)" type="string" persist="true" />
<VariableCommand name="lya" expression="eq((#year%4),0)*ne((#year%100),0)+eq((#year%400),0)" />
<VariableCommand name="da" expression="(ge(#month,1)*31+(28+#lya)*ge(#month,2)+ge(#month,3)*31+ge(#month,4)*30+ge(#month,5)*31+ge(#month,6)*30+ge(#month,7)*31+ge(#month,8)*31+ge(#month,9)*30+ge(#month,10)*31+ge(#month,11)*30+ge(#month,12)*31)+(365*#year+int(#year/4)+int(#year/400)-int(#year/100))+#date-1" />
<VariableCommand name="fa" expression="#hour24*60+#minute" />
<!-- Target date (2021/2/12 23:59) and calculation -->
<VariableCommand name="y1" expression="ifelse(isnull(#y1),2021,#y1)" persist="true" />
<VariableCommand name="m1" expression="ifelse(isnull(#m1),2,#m1)" persist="true" />
<VariableCommand name="d1" expression="ifelse(isnull(#d1),12,#d1)" persist="true" />
<VariableCommand name="h1" expression="ifelse(isnull(#h1),23,#h1)" persist="true" />
<VariableCommand name="f1" expression="ifelse(isnull(#f1),59,#f1)" persist="true" />
<VariableCommand name="lyz1" expression="eq((#y1%4),0)*ne((#y1%100),0)+eq((#y1%400),0)" />
<VariableCommand name="dz1" expression="(gt(#m1,1)*31+(28+#lyz1)*gt(#m1,2)+gt(#m1,3)*31+gt(#m1,4)*30+gt(#m1,5)*31+gt(#m1,6)*30+gt(#m1,7)*31+gt(#m1,8)*31+gt(#m1,9)*30+gt(#m1,10)*31+gt(#m1,11)*30+gt(#m1,12)*31)+(365*#y1+int(#y1/4)+int(#y1/400)-int(#y1/100))+#d1-1" />
<VariableCommand name="fz1" expression="#h1*60+#f1" />
</Trigger>
</ExternalCommands>
<Var name="vvv1" expression="ge(#dz1*1440+#fz1,#da*1440+#fa)" />
<Var name="date1" expression="ifelse(#vvv1,int(abs(#dz1-#da)-gt(#fa,#fz1)),int(abs(#dz1-#da)-gt(#fz1,#fa)))" />
<Var name="hour1" expression="ifelse(#vvv1,int((gt(#fa,#fz1)*1440+#fz1-#fa)/60),int((gt(#fz1,#fa)*1440+#fa-#fz1)/60))" />
<Var name="minute1" expression="ifelse(#vvv1,int((gt(#fa,#fz1)*1440+#fz1-#fa)%60),int((gt(#fz1,#fa)*1440+#fa-#fz1)%60))" />
<!--
lya Is this year a leap year?
da Calculate how many days there are between now and 0000
dz1 calculates the number of days from the target date to 0000
fa How many minutes have passed today?
vvv1 determines whether the target date is before or after now
-->
<Wallpaper x="0" y="0" />
<Text x="540" y="500" align="center" color="#ffffff" size="35" textExp="@djs_text+ifelse(#to,'('+#y1+'/'+#m1+'/'+#d1+'/'+#h1+':'+#f1+')','')+ifelse(#vvv1,'before','after')+' date '+#date1+' hour '+#hour1+' minute '+#minute1" />
</Lockscreen>
Put the following code into the config.xml file on the lock screen to customize the countdown target time:
<Config>
<Group text="Lock screen countdown settings">
<StringInput text="Countdown custom text" id="djs_text" default="" />
<NumberInput text="target year" id="y1" default="2021" />
<NumberInput text="target month" id="m1" default="2" />
<NumberInput text="target day" id="d1" default="12" />
<NumberInput text="target time" id="h1" default="23" />
<NumberInput text="target minute" id="f1" default="59" />
</Group>
</Config>
Shake to bring up the NFC interface
<VariableBinders>
<SensorBinder type="linear_acceleration" rate="2">
<Variable name="va_x" index="0" />
<Variable name="va_y" index="1" />
<Variable name="va_z" index="2" />
<Trigger condition="abs(#va_x*10)}120">
<!-- Linear acceleration data correction (taking the maximum value) solves problems caused by different sensitivities of different hardware-->
<VariableCommand name="va_num" expression="abs(#va_x*10)" persist="true" condition="(abs(#va_x*10))}#va_num" />
<!-- If the real-time value of va_x is greater than va_num*0.8, open the NFC interface -->
<IntentCommand action="com.miui.intent.action.DOUBLE_CLICK" package="com.miui.tsmclient" condition="abs(#va_x*10)}(0.8*#va_num)">
<Extra name="event_source" type="string" expression="'key_volume_down'"/>
</IntentCommand>
</Trigger>
</SensorBinder>
</VariableBinders>
#Performance optimization
Some points to note when optimizing:
A good lock screen not only requires good visual effects, but also requires a smooth experience (the performance and gaming level of the user's mobile phone vary, so when making a theme, you must consider how to maximize users).
- Try not to use top-end phones when testing, and write frameRate as 60. Actual test results: Ordinary mobile phones should not be lower than 30 frames/s, and better mobile phones must be higher than 40 frames/s (here refers to the highest frame that can be achieved) rate, not the frame rate you defined at the beginning or the dynamic frame rate you wrote in the code).
- Reduce image file size, reduce cache reading time, and save running memory: Stored images must be exported to web format, or compressed later; jpg can be used, never png, webp is also a good choice!
- Try to use pictures of appropriate sizes. Use small sizes and never large ones, which effectively reduces the amount of calculation!
- The code should not conflict logically and must be streamlined.
- Make full use of visibility to control the visibility of each module. Use
to judge whether the variable #num is greater than 0 to only display the part that currently needs to be displayed.
Detailed explanation of Layer optimization tool
Layer, this component is a Group, but the frame rate can be set independently to achieve independent rendering and refresh control of the internal elements of the Layer independent of other parts. This component corresponds to a single View in Android at the bottom of the MAML rendering engine, so this element can set the layer type like View to implement GPU hardware buffering and improve drawing performance.
<Layer name="layer" x="25" y="300" hardware="true" width="800" height="600" pivotX="400" pivotY="200" touchable="true" interceptTouch="true" frameRate="0">
</Layer>
Attribute | Description |
---|---|
hardware | Whether to use hardware drawing buffer, true is faster but takes up more memory, false is the opposite |
frameRate | Specify Layer internal elements |
updatePosition | Whether the Layer position (x y) needs to be updated, true/false |
updateSize | Layer size (w h) needs to be updated, true/false |
updateTranslation | Do the pivot/rotation/scale/alpha attributes of Layer need to be updated |
The above three updatePosition/updateSizeupdateTranslation properties are all true by default. If some property values are fixed and do not need to be updated (for example, x is not an expression, or there is no displacement animation), setting it to false will improve performance.
Optimization steps:
- When a certain part of the interface (such as a calendar panel or weather panel, etc.) has a different refresh rate from other interface parts, you can consider placing this interface element in a Layer. First try hardware="false" and specify a suitable one for the Layer. Refresh the frame rate frameRate to see if there are any performance problems.
- If there are still problems, you can set hardware to true and test again. This will consume additional memory. It is best not to use it if it is not necessary.
- If you really need hardware="true", but the memory usage exceeds the budget, Layer provides a function interface setHardwareLayer(boolean) called through MethodCommand, set to true before the animation starts, and set to false after the animation ends.
- Array and loop components that can reduce repetitive code writing on the interface: component array, variable array, LoopCommand, etc.
Applicable scenarios:
- The overall update frequency is high, but some areas (such as common calendars) do not need to be updated frequently. You can put this part in a Layer to specify a lower frame rate
- The overall update frequency is low, but there are some areas with animations that need to be updated frequently. You can put this part into a Layer and specify the animation frame rate separately
- For more complex animations, slide in and out of the panel to improve the smoothness of the animation. Use Layer to set appropriate hardware attributes
Note that due to the zorder problem, the internal elements of Layer are in a separate layer, similar to the following:
<Lockscreen>
<Wallpaper />
<Image src="img1.jpg"/>
<Layer name="layer1"/>
<Image src="img2.jpg"/>
<!-- Above layer1 is the top layer, img1 and img2 are on the original default bottom layer, so img2 is below layer1. -->
<!-- If img2 must be above Layer, you need to create another Layer as below: -->
<Image src="img1.jpg"/>
<Layer name="layer1"/>
<Layer name="layer2">
<Image src="img2.jpg"/>
</Layer>
</Lockscreen>
#Xiaomi Always-on Display
Theme package structure
Development instructions:
- The always-on display is based on maml, and existing global variables of maml can be used; the effect of the screen is only for display (time, date, notification, etc.), and the use of interactive functions such as sensor/button jump is prohibited.
- When the always-on display is displayed for the first time, animation can be played. The total duration of the animation cannot exceed 4000ms. Animations that time out will be forcibly stopped. The maximum frame rate is recommended to be 30 or 60, and there is no need to use a dynamic frame rate.
- When the screen is idle, the system stipulates: 1min interval refresh time, 2min interval refresh position. (Cannot be modified through maml code)
- Limit the size; the AOD module must be within 10M, the smaller the better.
- For effects that include animation, the code writing method needs to be standardized during initialization and animation end, so that the system can obtain the animation status.
- Supports the configuration of general switches for the screen (power, notifications, lunar calendar), allowing users to turn them off or on.
- The notification only displays the icon, which is consistent with the desktop icon and cannot be adapted by itself.
- Try to avoid using a large number of sequence frame images
AOD settings page related configurations (aod_description.xml) Supports configuration through code, can be customized to support user-controlled switch item display, and supports configuring the default state of switches.
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<MIUI_Theme_Values>
<!-- theme type, always maml_style -->
<theme_type>maml_style</theme_type>
<!-- Whether to support the display of lunar calendar, power, notification switches: 1 supports, 0 does not support, the default is 0 -->
<support_lunar_calendar>0</support_lunar_calendar>
<support_battery>1</support_battery>
<support_notification>1</support_notification>
<!-- Initial state of switch: lunar calendar, power, notification, 1 is on, 0 is off, default is 0 -->
<lunar_calendar_enable>0</lunar_calendar_enable>
<battery_enable>1</battery_enable>
<notification_enable>1</notification_enable>
</MIUI_Theme_Values>
Configuration items | Parameters | Description |
---|---|---|
theme_type | maml_style | Type: Variety Framework (required) |
support_lunar_calendar | 1 supported, 0 not supported (default) | Whether to support user configuration of the lunar calendar switch |
support_battery | 1 supported, 0 not supported (default) | Whether to support user configuration power switch |
support_notification | 1 supported, 0 not supported (default) | Whether user configuration notification switch is supported |
Global variables | Status | Description |
lunar_calendar_enable | 1 to display, 0 not to display (default) | When the lunar calendar switch is supported, the switch status |
battery_enable | 1 to display, 0 not to display (default) | When the power switch is supported, the switch status |
notification_enable | 1 to display, 0 not to display (default) | When notification switch is supported, switch status |
preview_mode | 1 Preview mode, 0 Screen mode | When the notification switch is supported. This variable needs to be used to ensure that the preview effect is supported in the settings page |
For effects that include animation, the code writing method needs to be standardized during initialization and animation end, so that the system can obtain the animation status.
<ExternalCommands>
<Trigger action="init">
<!-- After initialization, is required to give the system a state -->
<ExternCommand command="animationState" strPara="'init'" />
</Trigger>
<!-- When first displayed, all animations played; action="play" is similar to action="resume", but use play here -->
<Trigger action="play">
<AnimationCommand target="endAni" command="play"/>
</Trigger>
</ExternalCommands>
<Var name="endAni">
<VariableAnimation name="endAnimation" loop="false" initPause="true">
<Item value="0" time="0" easeType="CubicEaseOut" />
<Item value="1" time="1200"/>
<Triggers>
<!-- #endAnimation.current_frame==-1 Animation ends -->
<Trigger action="end" condition="#endAnimation.current_frame==-1">
<!-- After the longest animation ends, send a command to the system; note: this command can only exist once, do not use it repeatedly. -->
<ExternCommand command="animationState" strPara="'finish'" />
</Trigger>
</Triggers>
</VariableAnimation>
</Var>
AOD notification message AOD notifications are different from lock screen notifications. AOD notifications only display icons and do not display specific content information. aod will filter duplicate notifications sent by the app. For example, the same app will only display one icon and will not be repeated. For details, please refer to the code below:
<VariableBinders>
<!-- AOD notifications content provider: content://aod.notification/notifications -->
<ContentProviderBinder name="data" uri="content://aod.notification/notifications" columns="pkg" countName="hasnotifications">
<Variable name="noticePkg" type="string[]" column="pkg"/>
<Trigger>
<VariableCommand name="noticeCount" type="number" expression="min(#hasnotifications-1,3)"/>
</Trigger>
</ContentProviderBinder>
</VariableBinders>
<Var name="noticeApp" type="string[]" const="true" expression="" values="'com.android.contacts,com.android.contacts.activities.TwelveKeyDialer','com.android.mms','com.miui.securitycenter','com.android.thememanager'" />
<Array y="945+int((#timeAni-1)*140)" count="4" indexName="_i" alpha="#timeAni*255" visibility="#notification_enable">
<!-- Preview notification icon -->
<Image x="540+#_i*132-132/2*3" w="72" h="72" align="center" srcType="ApplicationIcon" srcExp="@noticeApp[#_i]" visibility="#preview_mode" />
<!-- Real notification icon -->
<Image x="540+#_i*132-132/2*#noticeCount" w="72" h="72" align="center" srcType="ApplicationIcon" srcExp="@noticePkg[#_i]" visibility="#_i{=#noticeCount ** !#preview_mode" />
</Array>
Click to download attachments
Android app to enable AOD preview.zip (opens new window) [install apk on android to test the aod theme from Aod Editor below, enable also USB Debug] (note: if the old version doesn't work use the new version)
AOD Editor screenshots translations
Note: second window is to create an empty new theme. ** Using delete cache will show this confirm message:
Caching can reduce theme time application, are you sure to delete it?Left button is for cancel the operation and right button is to confirm deleting cache
AOD english sample theme
Sample theme with english comments, that shows only notification: Notification Only Theme (MTZ file), Notification Only Theme (ZIP exploded).#Commonly used package names, class names and how to obtain them
Get method
1. Download and install the editor (opens new window)use password: 123a
2. Open the software you want to jump to on your phone
3. Open the editor and connect your phone to the computer with a data cable
4. Click the "Current Interface Jump Button" in the editor (the package name and class name will be automatically copied after clicking)
5. Just paste it in your code and use it
The following IntentCommands need to be used with the unlock command in the lock screen.
If you encounter a problem that cannot jump normally, it may be because the app has changed the relevant package name and class name. Just follow the acquisition method to obtain it again.
Dial ▽
<IntentCommand package="com.android.contacts" class="com.android.contacts.activities.TwelveKeyDialer"/>
Contact ▽
<IntentCommand package="com.android.contacts" class="com.android.contacts.activities.PeopleActivity"/>
SMS ▽
<IntentCommand package="com.android.mms" class="com.android.mms.ui.MmsTabActivity"/>
Camera ▽
<IntentCommand package="com.android.camera" class="com.android.camera.Camera"/>
Theme Manager ▽
<IntentCommand package="com.android.thememanager" class="com.android.thememanager.ThemeResourceTabActivity"/>
Calendar ▽
<IntentCommand package="com.android.calendar" class="com.android.calendar.AllInOneActivity"/>
Weather ▽
<IntentCommand package="com.miui.weather2" class="com.miui.weather2.ActivityWeatherMain"/>
Clock ▽
<IntentCommand package="com.android.deskclock" class="com.android.deskclock.DeskClockTabActivity"/>
Notes ▽
<IntentCommand package="com.miui.notes" class="com.miui.notes.ui.NotesListActivity"/>
Gallery ▽
<IntentCommand package="com.miui.gallery" class="com.miui.gallery.app.Gallery"|/>
Album ▽
<IntentCommand package="com.miui.gallery" class="com.miui.gallery.activity.HomePageActivity"/>
Browser ▽
<IntentCommand package="com.android.browser" class="com.android.browser.BrowserActivity"/>
Calculator ▽
<IntentCommand package="com.miui.calculator" class="com.miui.calculator.cal.CalculatorActivity"/>
Compass ▽
<IntentCommand package="com.miui.compass" class="com.miui.compass.CompassActivity"/>
Settings ▽
<IntentCommand package="com.android.settings" class="com.android.settings.MiuiSettings"/>
Mijia ▽
<IntentCommand package="com.xiaomi.smarthome" class="com.xiaomi.smarthome.SmartHomeMainActivity"/>
File manager ▽
<IntentCommand package="com.android.fileexplorer" class="com.android.fileexplorer.FileExplorerTabActivity"/>
Xiaomi Video ▽
<IntentCommand package="com.miui.video" class="com.miui.video.HomeActivity"/>
App Store ▽
<IntentCommand package="com.xiaomi.market" class="com.xiaomi.market.ui.MarketTabActivity"/>
Xiaoai classmates, voice assistant ▽
<IntentCommand category="android.intent.category.LAUNCHER" package="com.miui.voiceassist" class="com.xiaomi.voiceassistant.CTAAlertActivity"/>
Security Center ▽
<IntentCommand package="com.android.settings" class="com.miui.securitycenter.Main"/>
Email ▽
<IntentCommand package="com.android.email" class="com.android.email.activity.Welcome"/>
Radio ▽
<IntentCommand package="com.miui.fmradio" class="com.miui.fmradio.FmRadioActivity"/>
Recorder ▽
<IntentCommand package="com.android.soundrecorder" class="com.android.soundrecorder.SoundRecorder"/>
Download manager ▽
<IntentCommand package="com.android.providers.downloads.ui" class="com.android.providers.downloads.ui.DownloadList"/>
UC Browser ▽
<IntentCommand package="com.UCMobile" class="com.UCMobile.main.UCMobile"/>
Xiaomi Mall ▽
<IntentCommand package="com.xiaomi.shop" class="com.xiaomi.shop.activity.MainTabActivity"/>
Xiaomi Music ▽
<IntentCommand package="com.miui.player" class="com.miui.player.ui.MusicBrowserActivity"/>
QQ Music ▽
<IntentCommand package="com.tencent.qqmusic" class="com.tencent.qqmusic.activity.AppStarterActivity"/>
NetEase Cloud Music ▽
<IntentCommand package="com.netease.cloudmusic" class="com.netease.cloudmusic.activity.LoadingActivity"/>
Kugou Music ▽
<IntentCommand package="com.kugou.android" class="com.kugou.android.app.splash.SplashActivity"/>
Xiami Music ▽
<IntentCommand package="fm.xiami.main" class="fm.xiami.main.SplashActivity"/>
Baidu Map ▽
<IntentCommand package="com.baidu.BaiduMap" class="com.baidu.baidumaps.WelcomeScreen"/>
Today's headlines ▽
<IntentCommand package="com.ss.android.article.news" class="com.ss.android.article.news.activity.SplashActivity"/>
Taobao ▽
<IntentCommand package="com.taobao.taobao" class="com.taobao.tao.welcome.Welcome"/>
Sina Weibo ▽
<IntentCommand package="com.sina.weibo" class="com.sina.weibo.SplashActivity"/>
Mobile QQ ▽
<IntentCommand package="com.tencent.mobileqq" class="com.tencent.mobileqq.activity.SplashActivity"/>
WeChat ▽
<IntentCommand package="com.tencent.mm" class="com.tencent.mm.ui.LauncherUI"/>
WeChat Scan ▽
<IntentCommand package="com.tencent.mm" class="com.tencent.mm.plugin.scanner.ui.BaseScanUI"/>
WeChat payment code ▽
<IntentCommand package="com.tencent.mm" class="com.tencent.mm.plugin.offline.ui.WalletOfflineEntranceUI"/>
WeChat payment code ▽
<IntentCommand package="com.tencent.mm" class="com.tencent.mm.plugin.collect.ui.CollectAdapterUI"/>
WeChat Moments ▽
<IntentCommand package="com.tencent.mm" class="com.tencent.mm.plugin.sns.ui.SnsTimeLineUI"/>
WeChat My Business Card ▽
<IntentCommand package="com.tencent.mm" class="com.tencent.mm.plugin.setting.ui.setting.SelfQRCodeUI"/>
Alipay ▽
<IntentCommand package="com.eg.android.AlipayGphone" class="com.eg.android.AlipayGphone.AlipayLogin"/>
Alipay Scan ▽
<IntentCommand package="com.eg.android.AlipayGphone" class="com.alipay.mobile.scan.as.main.MainCaptureActivity"/>
Alipay payment code ▽
<IntentCommand package="com.eg.android.AlipayGphone" class="com.eg.android.AlipayGphone.FastStartActivity"/>
Bus Card (Xiaomi Wallet) ▽
<IntentCommand package="com.miui.tsmclient" class="com.miui.tsmclient.ui.quick.DoubleClickActivity"/>
Use without unlocking
<!-- Recorder -->
<IntentCommand action="android.intent.action.MAIN" package="com.android.soundrecorder" class="com.android.soundrecorder.SoundRecorder">
<Extra name="StartActivityWhenLocked" type="boolean" expression="1"/>
<Extra name="navigation_tab" expression="2" type="int" />
</IntentCommand>
<!-- Calculator -->
<IntentCommand action="android.intent.action.MAIN" package="com.miui.calculator" class="com.miui.calculator.cal.CalculatorActivity">
<Extra name="StartActivityWhenLocked" type="boolean" expression="1"/>
<Extra name="navigation_tab" expression="2" type="int"/>
</IntentCommand>
<!-- Camera -->
<IntentCommand action="android.intent.action.MAIN" package="com.android.camera" class="com.android.camera.Camera">
<Extra name="ShowCameraWhenLocked" type="boolean" expression="1"/>
<Extra name="StartActivityWhenLocked" type="boolean" expression="1"/>
</IntentCommand>
<!-- Flashlight -->
<VariableCommand name="lightSwitch" expression="!(#lightSwitch)"/>
<IntentCommand action="miui.intent.action.TOGGLE_TORCH" broadcast="true">
<Extra name="miui.intent.extra.IS_ENABLE" type="boolean" expression="ifelse(int(@__miui_version_code)}=8,#lightSwitch,1)"/>
</IntentCommand>