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<void>): void | Changes the window size. | 26| Window | moveWindowTo(x: number, y: number, callback: AsyncCallback<void>): void | Moves this window. | 27| Window | setUIContent(path: string, callback: AsyncCallback<void>): 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<void>): 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<void>): 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