#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:

  1. The wallpaper is cut into double screen width (screen width refers to screenWidth)
  2. Wallpaper positioning: x="-#wallpaper_offset_x * screen width"
  3. 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:

  1. 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)
  2. 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
  3. 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.
  4. 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 (&#10; 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.

[NOTE: VERIFY EXAMPLES THAT OUTPUTS IN CHINESE]
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:

xfermodeNum xfermode Mixing result 0 clear 1 src 2 dst 3 src_over 4 dst_over 5 src_in xfermodeNum xfermode Mixing result 6 dst_in 7 src_out 8 dst_out 9 src_atop 10 dst_atop 11 xor

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


#Image distortion

Sample theme: Shake demo [picture deformation].mtz (opens new window)

As shown in the picture:

bg.jpg (400x400) is divided into m x n grids (m width, n height) If m and n are both 2 we divided the picture in 4 blocks and we have: (m+1)*(n+1) = 9 nodes Controlling the coordinate of each point we can deform the picture. To control picture nodes, we generally use arrays. <Image x="0" y="0" scale="#scale_wh" src="bg.jpg" mesh="2,2" meshVertsArr="mesharr" />

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)
  1. 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), when strIsEmpty(@musicName.title)==1, 'No music yet' is displayed, otherwise @music.title is displayed

  • Music 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:

Clear Partly cloudy Cloudy Fog Rain includes (light rain, moderate rain, heavy rain, showers, heavy rain, heavy rain, extremely heavy rain, freezing rain) Thunderstorm Sleet Snow includes (light snow, moderate snow, heavy snow, snow showers, blizzard) Sand and dust include (blowing sand, sand dust, sandstorm, strong sandstorm) Hail Haze

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&amp;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&amp;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&amp;vad=false&amp;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&amp;vad=false&amp;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&gt;='%d' AND dtstart&lt;='%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

<!-- 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 -->