• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# System Window Development (Stage Model Only)
2
3## Overview
4
5In the stage model, system applications are allowed to create and manage system windows, including the volume bar, wallpaper, notification panel, status bar, and navigation bar. For details about the supported system window types, see [WindowType](../reference/apis-arkui/arkts-apis-window-e.md#windowtype7).
6
7When a window is displayed, hidden, or switched, an animation is usually used to smooth the interaction process.
8
9The animation is the default behavior for application windows. You do not need to set or modify the code.
10
11However, you can customize an animation to be played during the display or hiding of a system window.
12
13> **NOTE**
14>
15> This document involves the use of system APIs. You must use the full SDK for development. <!--Del-->For details, see [Guide to Switching to Full SDK](../faqs/full-sdk-switch-guide.md).<!--DelEnd-->
16
17
18## Available APIs
19
20For details about more APIs, see [Window](../reference/apis-arkui/js-apis-window-sys.md).
21
22| Instance           | API                                                      | Description                                                        |
23| ----------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
24| Window static method   | createWindow(config: Configuration, callback: AsyncCallback\<Window>): void | Creates a child window or system window.<br>**config**: parameters used for creating the window.    |
25| Window            | resize(width: number, height: number, callback: AsyncCallback&lt;void&gt;): void | Changes the window size.                                          |
26| Window            | moveWindowTo(x: number, y: number, callback: AsyncCallback&lt;void&gt;): void | Moves this window.                                          |
27| Window            | setUIContent(path: string, callback: AsyncCallback&lt;void&gt;): void | Loads the content of a page, with its path in the current project specified, to this window.<br>**path**: path of the page from which the content will be loaded. The path is configured in the **main_pages.json** file of the project in the stage model.                                    |
28| Window            | showWindow(callback: AsyncCallback\<void>): void             | Shows this window.                                              |
29| Window            | on(type: 'touchOutside', callback: Callback&lt;void&gt;): void | Subscribes to touch events outside this window.                          |
30| Window            | hide (callback: AsyncCallback\<void>): void                  | Hides this window. This is a system API.                            |
31| Window            | destroyWindow(callback: AsyncCallback&lt;void&gt;): void     | Destroys this window.                                              |
32| Window            | getTransitionController(): TransitionController              | Obtains the transition animation controller. This is a system API.                  |
33| TransitionContext | completeTransition(isCompleted: boolean): void               | Completes the transition. This API can be called only after [animateTo()](../reference/apis-arkui/arkui-ts/ts-explicit-animation.md) is executed. This is a system API.|
34| Window            | showWithAnimation(callback: AsyncCallback\<void>): void      | Shows this window and plays an animation during the process. This is a system API.            |
35| Window            | hideWithAnimation(callback: AsyncCallback\<void>): void      | Hides this window and plays an animation during the process. This is a system API.            |
36
37## Developing a System Window
38
39This section uses the volume bar as an example to describe how to develop a system window.
40
41### How to Develop
42
43
441. Create a system window.
45
46   In [ServiceExtensionContext](../reference/apis-ability-kit/js-apis-inner-application-serviceExtensionContext-sys.md), call **window.createWindow** to create a system window of the volume bar type.
47
482. Set the properties of the system window.
49
50   After the volume bar window is created, you can change its size and position, and set its properties such as the background color and brightness.
51
523. Load content to and show the system window.
53
54   You can call **setUIContent** to load content to the volume bar window and **showWindow** to show the window.
55
564. Hide or destroy the system window.
57
58   When the volume bar window is no longer needed, you can call **hide** or **destroyWindow** to hide or destroy it.
59
60```ts
61import { Want, ServiceExtensionAbility } from '@kit.AbilityKit';
62import { window } from '@kit.ArkUI';
63import { BusinessError } from '@kit.BasicServicesKit';
64
65export default class ServiceExtensionAbility1 extends ServiceExtensionAbility {
66  onCreate(want: Want) {
67    // 1. Create a volume bar window.
68    let windowClass: window.Window | null = null;
69    let config: window.Configuration = {
70      name: "volume", windowType: window.WindowType.TYPE_VOLUME_OVERLAY, ctx: this.context
71    };
72    window.createWindow(config, (err: BusinessError, data) => {
73      let errCode: number = err.code;
74      if (errCode) {
75        console.error(`Failed to create the volume window. Code:${err.code}, message:${err.message}`);
76        return;
77      }
78      console.info('Succeeded in creating the volume window.')
79      windowClass = data;
80      if (!windowClass) {
81        console.error('windowClass is null');
82        return;
83      }
84      // 2. Change the size and position of the volume bar window, or set its properties such as the background color and brightness.
85      windowClass.moveWindowTo(300, 300, (err: BusinessError) => {
86        let errCode: number = err.code;
87        if (errCode) {
88          console.error('Failed to move the window. Cause:' + JSON.stringify(err));
89          return;
90        }
91        console.info('Succeeded in moving the window.');
92      });
93      windowClass.resize(500, 500, (err: BusinessError) => {
94        let errCode: number = err.code;
95        if (errCode) {
96          console.error('Failed to change the window size. Cause:' + JSON.stringify(err));
97          return;
98        }
99        console.info('Succeeded in changing the window size.');
100      });
101      // 3.1 Load content to the volume bar window.
102      windowClass.setUIContent("pages/page_volume", (err: BusinessError) => {
103        let errCode: number = err.code;
104        if (errCode) {
105          console.error('Failed to load the content. Cause:' + JSON.stringify(err));
106          return;
107        }
108        console.info('Succeeded in loading the content.');
109        if (!windowClass) {
110          console.error('windowClass is null');
111          return;
112        }
113        // 3.2 Show the volume bar window.
114        windowClass.showWindow((err: BusinessError) => {
115          let errCode: number = err.code;
116          if (errCode) {
117            console.error('Failed to show the window. Cause:' + JSON.stringify(err));
118            return;
119          }
120          console.info('Succeeded in showing the window.');
121        });
122      });
123      // 4. Hide or destroy the volume bar window.
124      // Hide the volume bar window when a touch event outside the window is detected.
125      windowClass.on('touchOutside', () => {
126        console.info('touch outside');
127        if (!windowClass) {
128          console.error('windowClass is null');
129          return;
130        }
131        windowClass.hide((err: BusinessError) => {
132          let errCode: number = err.code;
133          if (errCode) {
134            console.error('Failed to hide the window. Cause: ' + JSON.stringify(err));
135            return;
136          }
137          console.info('Succeeded in hiding the window.');
138        });
139      });
140    });
141  }
142};
143```
144
145## Customizing an Animation to Be Played During the Display or Hiding of a System Window
146
147You can determine whether to play an animation when a system window is showing or hiding.
148
149### How to Develop
150
1511. Obtain the transition animation controller.
152
153   Call **getTransitionController** to obtain the controller, which completes subsequent animation operations.
154
1552. Configure the animation to be played.
156
157   Call [animateTo()](../reference/apis-arkui/arkui-ts/ts-explicit-animation.md) to configure the animation attributes.
158
1593. Complete the transition.
160
161   Use **completeTransition(true)** to set the completion status of the transition. If **false** is passed in, the transition is canceled.
162
1634. Show or hide the system window and play the animation during the process.
164
165   Call **showWithAnimation** to show the window and play the animation. Call **hideWithAnimation** to hide the window and play the animation.
166
167```ts
168// Import the showWithAnimation and hideWithAnimation methods to the .ts file.
169import { window } from '@kit.ArkUI';
170
171export class AnimationConfig {
172  private animationForShownCallFunc_: Function = undefined;
173  private animationForHiddenCallFunc_: Function = undefined;
174
175  ShowWindowWithCustomAnimation(windowClass: window.Window, callback) {
176    if (!windowClass) {
177      console.error('LOCAL-TEST windowClass is undefined');
178      return false;
179    }
180    this.animationForShownCallFunc_ = callback;
181    // Obtain the transition animation controller.
182    let controller: window.TransitionController = windowClass.getTransitionController();
183    controller.animationForShown = (context: window.TransitionContext)=> {
184      this.animationForShownCallFunc_(context);
185    };
186
187    windowClass.showWithAnimation(()=>{
188      console.info('LOCAL-TEST show with animation success');
189    })
190    return true;
191  }
192
193  HideWindowWithCustomAnimation(windowClass: window.Window, callback) {
194    if (!windowClass) {
195      console.error('LOCAL-TEST window is undefined');
196      return false;
197    }
198    this.animationForHiddenCallFunc_ = callback;
199    // Obtain the transition animation controller.
200    let controller: window.TransitionController = windowClass.getTransitionController();
201    controller.animationForHidden = (context : window.TransitionContext) => {
202      this.animationForHiddenCallFunc_(context);
203    };
204
205    windowClass.hideWithAnimation(()=>{
206      console.info('Hide with animation success');
207    })
208    return true;
209  }
210}
211```
212
213```ts
214// In the .ets file, implement the operation of creating the main window.
215import { window } from '@kit.ArkUI';
216import { UIAbility, Want, AbilityConstant, common } from '@kit.AbilityKit';
217import { hilog } from '@kit.PerformanceAnalysisKit'
218
219export default class EntryAbility extends UIAbility {
220  onCreate(want: Want,launchParam:AbilityConstant.LaunchParam) {
221    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
222  }
223
224  onDestroy(): void {
225    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability Destroy');
226  }
227
228  onWindowStageCreate(windowStage:window.WindowStage): void{
229    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
230
231    windowStage.loadContent('pages/transferControllerPage',(err, data)=>{
232      if(err.code){
233        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause:%{public}s', JSON.stringify(err)??'');
234        return ;
235      }
236      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data:%{public}s', JSON.stringify(data)??'');
237
238    });
239
240    AppStorage.setOrCreate<common.UIAbilityContext>("currentContext",this.context);
241  }
242
243  onWindowStageDestroy(): void{
244    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
245  }
246
247  onForeground(): void{
248    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
249  }
250
251  onBackground(): void{
252    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
253  }
254};
255```
256
257```ts
258// In the xxx.ets file, implement the attribute setting of the child window.
259import { window, router } from '@kit.ArkUI';
260import { common } from '@kit.AbilityKit';
261
262@Entry
263@Component
264struct transferCtrlSubWindow {
265  @State message: string = 'transferCtrlSubWindow'
266
267  build() {
268    Column() {
269      Text("close")
270        .fontSize(24)
271        .fontWeight(FontWeight.Normal)
272        .margin({ left: 10, top: 10 })
273      Button() {
274        Text("close")
275          .fontSize(24)
276          .fontWeight(FontWeight.Normal)
277      }.width(220).height(68)
278      .margin({ left: 10, top: 10 })
279      .onClick(() => {
280        let subWin = AppStorage.get<window.Window>("TransferSubWindow");
281        subWin?.destroyWindow();
282        AppStorage.setOrCreate<window.Window>("TransferSubWindow", undefined);
283      })
284    }.height('100%').width('100%').backgroundColor('#ff556243').shadow({radius: 30,color: '#ff555555',offsetX: 15,offsetY: 15})
285  }
286}
287```
288
289```ts
290// In the .ets file, implement the operations of creating a child window and displaying or hiding a window.
291import { window, router } from '@kit.ArkUI';
292import { common, Want } from '@kit.AbilityKit';
293import { BusinessError } from '@kit.BasicServicesKit';
294import { AnimationConfig } from '../entryability/AnimationConfig';
295
296@Entry
297@Component
298struct Index {
299  @State message: string = 'transferControllerWindow';
300
301  private animationConfig_?:AnimationConfig = undefined;
302  private subWindow_?:window.Window = undefined;
303
304  aboutToAppear(){
305    if(!this.animationConfig_){
306      this.animationConfig_ = new AnimationConfig();
307    }
308  }
309
310  private CreateTransferSubWindow(){
311    if(this.subWindow_){
312      this.subWindow_ = AppStorage.get<window.Window>("TransferSubWindow");
313      if(!this.subWindow_){
314        this.subWindow_ = undefined;
315      }
316    }
317    let context = AppStorage.get<common.UIAbilityContext>("currentContext");
318    console.log('LOCAL-TEST try to CreateTransferSubWindow');
319    let windowConfig:window.Configuration = {
320      name : "systemTypeWindow",
321      windowType : window.WindowType.TYPE_FLOAT,
322      ctx : context,
323    };
324    let promise = window?.createWindow(windowConfig);
325    promise?.then(async(subWin) => {
326      this.subWindow_ = subWin;
327      AppStorage.setOrCreate<window.Window>("systemTypeWindow", subWin);
328      await subWin.setUIContent("pages/transferCtrlSubWindow",()=>{});
329      await subWin.moveWindowTo(100,100);
330      await subWin.resize(200,200);
331    }).catch((err:Error)=>{
332      console.log('LOCAL-TEST createSubWindow err:' + JSON.stringify(err));
333    })
334  }
335  private ShowSUBWindow(){
336    if(!this.subWindow_){
337      console.log('LOCAL-TEST this.subWindow_is null');
338      return ;
339    }
340    let animationConfig = new AnimationConfig();
341    let systemTypeWindow = window.findWindow("systemTypeWindow");
342    console.log("LOCAL-TEST try to ShowWindowWithCustomAnimation");
343    animationConfig.ShowWindowWithCustomAnimation(systemTypeWindow,(context:window.TransitionContext)=>{
344      console.info('LOCAL-TEST start show window animation');
345      let toWindow = context.toWindow;
346      this.getUIContext()?.animateTo({
347        duration: 200, // Animation duration
348        tempo: 0.5, // Playback speed.
349        curve: Curve.EaseInOut, // Animation curve.
350        delay: 0, // Animation delay.
351        iterations: 1, // Number of playback times.
352        playMode: PlayMode.Normal // Animation playback mode.
353        onFinish: () => {
354          console.info('LOCAL-TEST onFinish in show animation');
355          context.completeTransition(true);
356        }
357      },() => {
358        let obj: window.TranslateOptions = {
359          x: 100.0,
360          y: 0.0,
361          z: 0.0
362        };
363        try {
364          toWindow.translate(obj); // Set the transition animation.
365        }catch(exception){
366          console.error('Failed to translate. Cause: ' + JSON.stringify(exception));
367        }
368      });
369      console.info('LOCAL-TEST complete transition end');
370    });
371  }
372
373  private HideSUBWindow(){
374    if(!this.subWindow_){
375      console.log('LOCAL-TEST this.subWindow_is null');
376      return ;
377    }
378    let animationConfig = new AnimationConfig();
379    let systemTypeWindow = window.findWindow("systemTypeWindow");
380    console.log("LOCAL-TEST try to HideWindowWithCustomAnimation");
381    animationConfig.HideWindowWithCustomAnimation(systemTypeWindow,(context:window.TransitionContext)=>{
382      console.info('LOCAL-TEST start hide window animation');
383      let toWindow = context.toWindow;
384      this.getUIContext()?.animateTo({
385        duration: 200, // Animation duration
386        tempo: 0.5, // Playback speed.
387        curve: Curve.EaseInOut, // Animation curve.
388        delay: 0, // Animation delay.
389        iterations: 1, // Number of playback times.
390        playMode: PlayMode.Normal // Animation playback mode.
391        onFinish: () => {
392          console.info('LOCAL-TEST onFinish in hide animation');
393          context.completeTransition(true);
394        }
395      },() => {
396        let obj: window.TranslateOptions = {
397          x: 500.0,
398          y: 0.0,
399          z: 0.0
400        };
401        try {
402          toWindow.translate(obj); // Set the transition animation.
403        }catch(exception){
404          console.error('Failed to translate. Cause: ' + JSON.stringify(exception));
405        }
406      });
407      console.info('LOCAL-TEST complete transition end');
408    });
409  }
410
411  build() {
412    Column() {
413      Text(this.message)
414        .fontSize(24)
415        .fontWeight(FontWeight.Normal)
416        .margin({left: 10, top: 10})
417
418      Button(){
419        Text("CreateSub")
420          .fontSize(24)
421          .fontWeight(FontWeight.Normal)
422      }.width(220).height(68)
423      .margin({left: 10, top: 10})
424      .onClick(() => {
425        this.CreateTransferSubWindow();
426      })
427
428      Button(){
429        Text("Show")
430          .fontSize(24)
431          .fontWeight(FontWeight.Normal)
432      }.width(220).height(68)
433      .margin({left: 10, top: 10})
434      .onClick(() => {
435        this.ShowSUBWindow();
436      })
437
438      Button(){
439        Text("Hide")
440          .fontSize(24)
441          .fontWeight(FontWeight.Normal)
442      }.width(220).height(68)
443      .margin({left: 10, top: 10})
444      .onClick(() => {
445        this.HideSUBWindow();
446      })
447    }.width('100%').height('100%')
448  }
449}
450```
451