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/js-apis-window.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 subwindow 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. Cause:' + JSON.stringify(err)); 76 return; 77 } 78 console.info('Succeeded in creating the volume window.') 79 windowClass = data; 80 // 2. Change the size and position of the volume bar window, or set its properties such as the background color and brightness. 81 windowClass.moveWindowTo(300, 300, (err: BusinessError) => { 82 let errCode: number = err.code; 83 if (errCode) { 84 console.error('Failed to move the window. Cause:' + JSON.stringify(err)); 85 return; 86 } 87 console.info('Succeeded in moving the window.'); 88 }); 89 windowClass.resize(500, 500, (err: BusinessError) => { 90 let errCode: number = err.code; 91 if (errCode) { 92 console.error('Failed to change the window size. Cause:' + JSON.stringify(err)); 93 return; 94 } 95 console.info('Succeeded in changing the window size.'); 96 }); 97 // 3.1 Load content to the volume bar window. 98 windowClass.setUIContent("pages/page_volume", (err: BusinessError) => { 99 let errCode: number = err.code; 100 if (errCode) { 101 console.error('Failed to load the content. Cause:' + JSON.stringify(err)); 102 return; 103 } 104 console.info('Succeeded in loading the content.'); 105 // 3.2 Show the volume bar window. 106 (windowClass as window.Window).showWindow((err: BusinessError) => { 107 let errCode: number = err.code; 108 if (errCode) { 109 console.error('Failed to show the window. Cause:' + JSON.stringify(err)); 110 return; 111 } 112 console.info('Succeeded in showing the window.'); 113 }); 114 }); 115 // 4. Hide or destroy the volume bar window. 116 // Hide the volume bar window when a touch event outside the window is detected. 117 windowClass.on('touchOutside', () => { 118 console.info('touch outside'); 119 (windowClass as window.Window).hide((err: BusinessError) => { 120 let errCode: number = err.code; 121 if (errCode) { 122 console.error('Failed to hide the window. Cause: ' + JSON.stringify(err)); 123 return; 124 } 125 console.info('Succeeded in hiding the window.'); 126 }); 127 }); 128 }); 129 } 130}; 131``` 132 133## Customizing an Animation to Be Played During the Display or Hiding of a System Window 134 135You can determine whether to play an animation when a system window is showing or hiding. 136 137### How to Develop 138 1391. Obtain the transition animation controller. 140 141 Call **getTransitionController** to obtain the controller, which completes subsequent animation operations. 142 1432. Configure the animation to be played. 144 145 Call [animateTo()](../reference/apis-arkui/arkui-ts/ts-explicit-animation.md) to configure the animation attributes. 146 1473. Complete the transition. 148 149 Use **completeTransition(true)** to set the completion status of the transition. If **false** is passed in, the transition is canceled. 150 1514. Show or hide the system window and play the animation during the process. 152 153 Call **showWithAnimation** to show the window and play the animation. Call **hideWithAnimation** to hide the window and play the animation. 154 155```ts 156// Import the showWithAnimation and hideWithAnimation methods to the .ts file. 157import { window } from '@kit.ArkUI'; 158 159export class AnimationConfig { 160 private animationForShownCallFunc_: Function = undefined; 161 private animationForHiddenCallFunc_: Function = undefined; 162 163 ShowWindowWithCustomAnimation(windowClass: window.Window, callback) { 164 if (!windowClass) { 165 console.error('LOCAL-TEST windowClass is undefined'); 166 return false; 167 } 168 this.animationForShownCallFunc_ = callback; 169 // Obtain the transition animation controller. 170 let controller: window.TransitionController = windowClass.getTransitionController(); 171 controller.animationForShown = (context: window.TransitionContext)=> { 172 this.animationForShownCallFunc_(context); 173 }; 174 175 windowClass.showWithAnimation(()=>{ 176 console.info('LOCAL-TEST show with animation success'); 177 }) 178 return true; 179 } 180 181 HideWindowWithCustomAnimation(windowClass: window.Window, callback) { 182 if (!windowClass) { 183 console.error('LOCAL-TEST window is undefined'); 184 return false; 185 } 186 this.animationForHiddenCallFunc_ = callback; 187 // Obtain the transition animation controller. 188 let controller: window.TransitionController = windowClass.getTransitionController(); 189 controller.animationForHidden = (context : window.TransitionContext) => { 190 this.animationForHiddenCallFunc_(context); 191 }; 192 193 windowClass.hideWithAnimation(()=>{ 194 console.info('Hide with animation success'); 195 }) 196 return true; 197 } 198} 199``` 200 201```ts 202// In the .ets file, implement the operation of creating the main window. 203import { window } from '@kit.ArkUI'; 204import { UIAbility, Want, AbilityConstant, common } from '@kit.AbilityKit'; 205import { hilog } from '@kit.PerformanceAnalysisKit' 206 207export default class EntryAbility extends UIAbility { 208 onCreate(want: Want,launchParam:AbilityConstant.LaunchParam) { 209 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); 210 } 211 212 onDestroy(): void { 213 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability Destroy'); 214 } 215 216 onWindowStageCreate(windowStage:window.WindowStage): void{ 217 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); 218 219 windowStage.loadContent('pages/transferControllerPage',(err, data)=>{ 220 if(err.code){ 221 hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause:%{public}s', JSON.stringify(err)??''); 222 return ; 223 } 224 hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data:%{public}s', JSON.stringify(data)??''); 225 226 }); 227 228 AppStorage.setOrCreate<common.UIAbilityContext>("currentContext",this.context); 229 } 230 231 onWindowStageDestroy(): void{ 232 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); 233 } 234 235 onForeground(): void{ 236 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); 237 } 238 239 onBackground(): void{ 240 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); 241 } 242}; 243``` 244 245```ts 246// In the xxx.ets file, implement the attribute setting of the subwindow. 247import { window, router } from '@kit.ArkUI'; 248import { common } from '@kit.AbilityKit'; 249 250@Entry 251@Component 252struct transferCtrlSubWindow { 253 @State message: string = 'transferCtrlSubWindow' 254 255 build() { 256 Column() { 257 Text("close") 258 .fontSize(24) 259 .fontWeight(FontWeight.Normal) 260 .margin({ left: 10, top: 10 }) 261 Button() { 262 Text("close") 263 .fontSize(24) 264 .fontSize(FontWeight.Normal) 265 }.width(220).height(68) 266 .margin({ left: 10, top: 10 }) 267 .onClick(() => { 268 let subWin = AppStorage.get<window.Window>("TransferSubWindow"); 269 subWin?.destroyWindow(); 270 AppStorage.setOrCreate<window.Window>("TransferSubWindow", undefined); 271 }) 272 }.height('100%').width('100%').backgroundColor('#ff556243').shadow({radius: 30,color: '#ff555555',offsetX: 15,offsetY: 15}) 273 } 274} 275``` 276 277```ts 278// In the .ets file, implement the operations of creating a subwindow and displaying or hiding a window. 279import { window, router } from '@kit.ArkUI'; 280import { common, Want } from '@kit.AbilityKit'; 281import { BusinessError } from '@kit.BasicServicesKit'; 282import { AnimationConfig } from '../entryability/AnimationConfig'; 283 284@Entry 285@Component 286struct Index { 287 @State message: string = 'transferControllerWindow'; 288 289 private animationConfig_?:AnimationConfig = undefined; 290 private subWindow_?:window.Window = undefined; 291 292 aboutToAppear(){ 293 if(!this.animationConfig_){ 294 this.animationConfig_ = new AnimationConfig(); 295 } 296 } 297 298 private CreateTransferSubWindow(){ 299 if(this.subWindow_){ 300 this.subWindow_ = AppStorage.get<window.Window>("TransferSubWindow"); 301 if(!this.subWindow_){ 302 this.subWindow_ = undefined; 303 } 304 } 305 let context = AppStorage.get<common.UIAbilityContext>("currentContext"); 306 console.log('LOCAL-TEST try to CreateTransferSubWindow'); 307 let windowConfig:window.Configuration = { 308 name : "systemTypeWindow", 309 windowType : window.WindowType.TYPE_FLOAT, 310 ctx : context, 311 }; 312 let promise = window?.createWindow(windowConfig); 313 promise?.then(async(subWin) => { 314 this.subWindow_ = subWin; 315 AppStorage.setOrCreate<window.Window>("systemTypeWindow", subWin); 316 await subWin.setUIContent("pages/transferCtrlSubWindow",()=>{}); 317 await subWin.moveWindowTo(100,100); 318 await subWin.resize(200,200); 319 }).catch((err:Error)=>{ 320 console.log('LOCAL-TEST createSubWindow err:' + JSON.stringify(err)); 321 }) 322 } 323 private ShowSUBWindow(){ 324 if(!this.subWindow_){ 325 console.log('LOCAL-TEST this.subWindow_is null'); 326 return ; 327 } 328 let animationConfig = new AnimationConfig(); 329 let systemTypeWindow = window.findWindow("systemTypeWindow"); 330 console.log("LOCAL-TEST try to ShowWindowWithCustomAnimation"); 331 animationConfig.ShowWindowWithCustomAnimation(systemTypeWindow,(context:window.TransitionContext)=>{ 332 console.info('LOCAL-TEST start show window animation'); 333 let toWindow = context.toWindow; 334 animateTo({ 335 duration: 200, // Animation duration 336 tempo: 0.5, // Playback speed. 337 curve: Curve.EaseInOut, // Animation curve. 338 delay: 0, // Animation delay. 339 iterations: 1, // Number of playback times. 340 playMode: PlayMode.Normal // Animation playback mode. 341 onFinish: () => { 342 console.info('LOCAL-TEST onFinish in show animation'); 343 context.completeTransition(true); 344 } 345 },() => { 346 let obj: window.TranslateOptions = { 347 x: 100.0, 348 y: 0.0, 349 z: 0.0 350 }; 351 try { 352 toWindow.translate(obj); // Set the transition animation. 353 }catch(exception){ 354 console.error('Failed to translate. Cause: ' + JSON.stringify(exception)); 355 } 356 }); 357 console.info('LOCAL-TEST complete transition end'); 358 }); 359 } 360 361 private HideSUBWindow(){ 362 if(!this.subWindow_){ 363 console.log('LOCAL-TEST this.subWindow_is null'); 364 return ; 365 } 366 let animationConfig = new AnimationConfig(); 367 let systemTypeWindow = window.findWindow("systemTypeWindow"); 368 console.log("LOCAL-TEST try to HideWindowWithCustomAnimation"); 369 animationConfig.HideWindowWithCustomAnimation(systemTypeWindow,(context:window.TransitionContext)=>{ 370 console.info('LOCAL-TEST start hide window animation'); 371 let toWindow = context.toWindow; 372 animateTo({ 373 duration: 200, // Animation duration 374 tempo: 0.5, // Playback speed. 375 curve: Curve.EaseInOut, // Animation curve. 376 delay: 0, // Animation delay. 377 iterations: 1, // Number of playback times. 378 playMode: PlayMode.Normal // Animation playback mode. 379 onFinish: () => { 380 console.info('LOCAL-TEST onFinish in hide animation'); 381 context.completeTransition(true); 382 } 383 },() => { 384 let obj: window.TranslateOptions = { 385 x: 500.0, 386 y: 0.0, 387 z: 0.0 388 }; 389 try { 390 toWindow.translate(obj); // Set the transition animation. 391 }catch(exception){ 392 console.error('Failed to translate. Cause: ' + JSON.stringify(exception)); 393 } 394 }); 395 console.info('LOCAL-TEST complete transition end'); 396 }); 397 } 398 399 build() { 400 Column() { 401 Text(this.message) 402 .fontSize(24) 403 .fontWeight(FontWeight.Normal) 404 .margin({left: 10, top: 10}) 405 406 Button(){ 407 Text("CreateSub") 408 .fontSize(24) 409 .fontWeight(FontWeight.Normal) 410 }.width(220).height(68) 411 .margin({left: 10, top: 10}) 412 .onClick(() => { 413 this.CreateTransferSubWindow(); 414 }) 415 416 Button(){ 417 Text("Show") 418 .fontSize(24) 419 .fontWeight(FontWeight.Normal) 420 }.width(220).height(68) 421 .margin({left: 10, top: 10}) 422 .onClick(() => { 423 this.ShowSUBWindow(); 424 }) 425 426 Button(){ 427 Text("Hide") 428 .fontSize(24) 429 .fontWeight(FontWeight.Normal) 430 }.width(220).height(68) 431 .margin({left: 10, top: 10}) 432 .onClick(() => { 433 this.HideSUBWindow(); 434 }) 435 }.width('100%').height('100%') 436 } 437} 438``` 439