1# Application Window Development (Stage Model)
2
3
4## Basic Concepts
5
6- Immersive window: a window display mode where the system windows (generally the status bar and navigation bar) are hidden to allow users to fully engage with the content.
7
8  The immersive window feature is applicable only to the main window of an application in full-screen mode. It does not apply to a main window in freeform window mode or a subwindow (for example, a dialog box or a floating window).
9
10- Floating window: a special application window that can still be displayed in the foreground when the main window and corresponding ability are running in the background.
11
12  The floating window can be used to continue playing a video after the application is switched to the background, or offer a quick entry (for example, bubbles) to the application. Before creating a floating window, an application must apply for the required permission.
13
14
15## When to Use
16
17In the stage model, you can perform the following operations during application window development:
18
19- Setting the properties and content of the main window of an application
20
21- Setting the properties and content of the subwindow of an application
22
23- Experiencing the immersive window feature
24
25- Setting a floating window
26
27- Listening for interactive and non-interactive window events
28
29## Available APIs
30
31The table below lists the common APIs used for application window development. For details about more APIs, see [Window](../reference/apis-arkui/js-apis-window.md).
32
33| Instance        | API                                                      | Description                                                        |
34| -------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
35| WindowStage    | getMainWindow(callback: AsyncCallback&lt;Window&gt;): void   | Obtains the main window of this window stage.<br>This API can be used only in the stage model.|
36| WindowStage    | loadContent(path: string, callback: AsyncCallback&lt;void&gt;): void | Loads content to the main window in this window stage.<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.<br>This API can be used only in the stage model.|
37| WindowStage    | createSubWindow(name: string, callback: AsyncCallback&lt;Window&gt;): void | Creates a subwindow.<br>This API can be used only in the stage model.            |
38| WindowStage    | on(type: 'windowStageEvent', callback: Callback&lt;WindowStageEventType&gt;): void | Subscribes to window stage lifecycle change events.<br>This API can be used only in the stage model.|
39| Window static method| createWindow(config: Configuration, callback: AsyncCallback\<Window>): void | Creates a subwindow or system window.<br>**config**: parameters used for creating the window.            |
40| 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.                                    |
41| Window         | setWindowBrightness(brightness: number, callback: AsyncCallback&lt;void&gt;): void | Sets the brightness for this window.                                            |
42| Window         | setWindowTouchable(isTouchable: boolean, callback: AsyncCallback&lt;void&gt;): void | Sets whether this window is touchable.                                    |
43| Window         | moveWindowTo(x: number, y: number, callback: AsyncCallback&lt;void&gt;): void | Moves this window.                                          |
44| Window         | resize(width: number, height: number, callback: AsyncCallback&lt;void&gt;): void | Changes the window size.                                          |
45| Window         | setWindowLayoutFullScreen(isLayoutFullScreen: boolean): Promise&lt;void&gt; | Sets whether to enable the full-screen mode for the window layout.                                 |
46| Window         | setWindowSystemBarEnable(names: Array&lt;'status'\|'navigation'&gt;): Promise&lt;void&gt; | Sets whether to display the status bar and navigation bar in this window.                                |
47| Window         | setWindowSystemBarProperties(systemBarProperties: SystemBarProperties): Promise&lt;void&gt; | Sets the properties of the status bar and navigation bar in this window.<br>**systemBarProperties**: properties of the status bar and navigation bar.|
48| Window         | showWindow(callback: AsyncCallback\<void>): void             | Shows this window.                                              |
49| Window         | on(type: 'touchOutside', callback: Callback&lt;void&gt;): void | Subscribes to touch events outside this window.                          |
50| Window         | destroyWindow(callback: AsyncCallback&lt;void&gt;): void     | Destroys this window.                                              |
51
52
53## Setting the Main Window of an Application
54
55In the stage model, the main window of an application is created and maintained by a **UIAbility** instance. In the **onWindowStageCreate** callback of the **UIAbility** instance, use **WindowStage** to obtain the main window of the application and set its properties. You can also set the properties (for example, **maxWindowWidth**) in the [abilities tag of the module.json5 file](../quick-start/module-configuration-file.md#abilities).
56
57### How to Develop
58
591. Obtain the main window.
60
61   Call **getMainWindow** to obtain the main window of the application.
62
632. Set the properties of the main window.
64
65   You can set multiple properties of the main window, such as the background color, brightness, and whether the main window is touchable. The code snippet below uses the **touchable** property as an example.
66
673. Load content to the main window.
68
69   Call **loadContent** to load content to the main window.
70
71```ts
72import { UIAbility } from '@kit.AbilityKit';
73import { window } from '@kit.ArkUI';
74import { BusinessError } from '@kit.BasicServicesKit';
75
76export default class EntryAbility extends UIAbility {
77  onWindowStageCreate(windowStage: window.WindowStage) {
78    // 1. Obtain the main window of the application.
79    let windowClass: window.Window | null = null;
80    windowStage.getMainWindow((err: BusinessError, data) => {
81      let errCode: number = err.code;
82      if (errCode) {
83        console.error('Failed to obtain the main window. Cause: ' + JSON.stringify(err));
84        return;
85      }
86      windowClass = data;
87      console.info('Succeeded in obtaining the main window. Data: ' + JSON.stringify(data));
88      // 2. Set the touchable property of the main window.
89      let isTouchable: boolean = true;
90      windowClass.setWindowTouchable(isTouchable, (err: BusinessError) => {
91        let errCode: number = err.code;
92        if (errCode) {
93          console.error('Failed to set the window to be touchable. Cause:' + JSON.stringify(err));
94          return;
95        }
96        console.info('Succeeded in setting the window to be touchable.');
97      })
98    })
99    // 3. Load content to the main window.
100    windowStage.loadContent("pages/page2", (err: BusinessError) => {
101      let errCode: number = err.code;
102      if (errCode) {
103        console.error('Failed to load the content. Cause:' + JSON.stringify(err));
104        return;
105      }
106      console.info('Succeeded in loading the content.');
107    });
108  }
109};
110```
111
112## Setting a Subwindow of an Application
113
114You can create an application subwindow, such as a dialog box, and set its properties.
115
116> **NOTE**
117>
118> Due to the following limitations, using subwindows is not recommended in mobile device scenarios. Instead, you are advised to use the [overlay](../reference/apis-arkui/arkui-ts/ts-universal-attributes-overlay.md) capability of components.
119> - Subwindows on mobile devices are constrained within the main window's boundaries, mirroring the limitations of components.
120> - In split-screen or freeform window mode, components, when compared with subwindows, offer better real-time adaptability to changes in the main window's position and size.
121> - On certain platforms, system configurations may restrict subwindows to default system animations and rounded shadows, offering no customization options for applications and thereby limiting their versatility.
122
123### How to Develop
124
1251. Create a subwindow.
126
127   Call **createSubWindow** to create a subwindow.
128
1292. Set the properties of the subwindow.
130
131   After the subwindow is created, you can set its properties, such as the size, position, background color, and brightness.
132
1333. Load content to and show the subwindow.
134
135   Call **setUIContent** to load content to the subwindow and **showWindow** to show the subwindow.
136
1374. Destroy the subwindow.
138
139   When the subwindow is no longer needed, you can call **destroyWindow** to destroy it.
140
141The code snippet for creating a subwindow in **onWindowStageCreate** is as follows:
142
143```ts
144import { UIAbility } from '@kit.AbilityKit';
145import { window } from '@kit.ArkUI';
146import { BusinessError } from '@kit.BasicServicesKit';
147
148let windowStage_: window.WindowStage | null = null;
149let sub_windowClass: window.Window | null = null;
150
151export default class EntryAbility extends UIAbility {
152  showSubWindow() {
153    // 1. Create a subwindow.
154    if (windowStage_ == null) {
155      console.error('Failed to create the subwindow. Cause: windowStage_ is null');
156    }
157    else {
158      windowStage_.createSubWindow("mySubWindow", (err: BusinessError, data) => {
159        let errCode: number = err.code;
160        if (errCode) {
161          console.error('Failed to create the subwindow. Cause: ' + JSON.stringify(err));
162          return;
163        }
164        sub_windowClass = data;
165        console.info('Succeeded in creating the subwindow. Data: ' + JSON.stringify(data));
166        // 2. Set the position, size, and other properties of the subwindow.
167        sub_windowClass.moveWindowTo(300, 300, (err: BusinessError) => {
168          let errCode: number = err.code;
169          if (errCode) {
170            console.error('Failed to move the window. Cause:' + JSON.stringify(err));
171            return;
172          }
173          console.info('Succeeded in moving the window.');
174        });
175        sub_windowClass.resize(500, 500, (err: BusinessError) => {
176          let errCode: number = err.code;
177          if (errCode) {
178            console.error('Failed to change the window size. Cause:' + JSON.stringify(err));
179            return;
180          }
181          console.info('Succeeded in changing the window size.');
182        });
183        // 3.1 Load content to the subwindow.
184        sub_windowClass.setUIContent("pages/page3", (err: BusinessError) => {
185          let errCode: number = err.code;
186          if (errCode) {
187            console.error('Failed to load the content. Cause:' + JSON.stringify(err));
188            return;
189          }
190          console.info('Succeeded in loading the content.');
191          // 3.2 Show the subwindow.
192          (sub_windowClass as window.Window).showWindow((err: BusinessError) => {
193            let errCode: number = err.code;
194            if (errCode) {
195              console.error('Failed to show the window. Cause: ' + JSON.stringify(err));
196              return;
197            }
198            console.info('Succeeded in showing the window.');
199          });
200        });
201      })
202    }
203  }
204
205  destroySubWindow() {
206    // 4. Destroy the subwindow when it is no longer needed (depending on the service logic).
207    (sub_windowClass as window.Window).destroyWindow((err: BusinessError) => {
208      let errCode: number = err.code;
209      if (errCode) {
210        console.error('Failed to destroy the window. Cause: ' + JSON.stringify(err));
211        return;
212      }
213      console.info('Succeeded in destroying the window.');
214    });
215  }
216
217  onWindowStageCreate(windowStage: window.WindowStage) {
218    windowStage_ = windowStage;
219    // Create a subwindow when it is needed, for example, when a touch event occurs in the main window. Calling onWindowStageCreate is not always necessary. The code here is for reference only.
220    this.showSubWindow();
221  }
222
223  onWindowStageDestroy() {
224    // Destroy the subwindow when it is no longer needed, for example, when the Close button in the subwindow is touched. Calling onWindowStageDestroy is not always necessary. The code here is for reference only.
225    this.destroySubWindow();
226  }
227};
228```
229
230You can also click a button on a page to create a subwindow. The code snippet is as follows:
231
232```ts
233// EntryAbility.ets
234onWindowStageCreate(windowStage: window.WindowStage) {
235  windowStage.loadContent('pages/Index', (err) => {
236    if (err.code) {
237      console.error('Failed to load the content. Cause:' + JSON.stringify(err));
238      return;
239    }
240    console.info('Succeeded in loading the content.');
241  })
242
243  // Transfer the window stage to the Index page.
244  AppStorage.setOrCreate('windowStage', windowStage);
245}
246```
247
248```ts
249// Index.ets
250import { window } from '@kit.ArkUI';
251import { BusinessError } from '@kit.BasicServicesKit';
252
253let windowStage_: window.WindowStage | undefined = undefined;
254let sub_windowClass: window.Window | undefined = undefined;
255@Entry
256@Component
257struct Index {
258  @State message: string = 'Hello World';
259  private CreateSubWindow(){
260    // Obtain the window stage.
261    windowStage_ = AppStorage.get('windowStage');
262    // 1. Create a subwindow.
263    if (windowStage_ == null) {
264      console.error('Failed to create the subwindow. Cause: windowStage_ is null');
265    }
266    else {
267      windowStage_.createSubWindow("mySubWindow", (err: BusinessError, data) => {
268        let errCode: number = err.code;
269        if (errCode) {
270          console.error('Failed to create the subwindow. Cause: ' + JSON.stringify(err));
271          return;
272        }
273        sub_windowClass = data;
274        console.info('Succeeded in creating the subwindow. Data: ' + JSON.stringify(data));
275        // 2. Set the position, size, and other properties of the subwindow.
276        sub_windowClass.moveWindowTo(300, 300, (err: BusinessError) => {
277          let errCode: number = err.code;
278          if (errCode) {
279            console.error('Failed to move the window. Cause:' + JSON.stringify(err));
280            return;
281          }
282          console.info('Succeeded in moving the window.');
283        });
284        sub_windowClass.resize(500, 500, (err: BusinessError) => {
285          let errCode: number = err.code;
286          if (errCode) {
287            console.error('Failed to change the window size. Cause:' + JSON.stringify(err));
288            return;
289          }
290          console.info('Succeeded in changing the window size.');
291        });
292        // 3. Load content to the subwindow.
293        sub_windowClass.setUIContent("pages/subWindow", (err: BusinessError) => {
294          let errCode: number = err.code;
295          if (errCode) {
296            console.error('Failed to load the content. Cause:' + JSON.stringify(err));
297            return;
298          }
299          console.info('Succeeded in loading the content.');
300          // 3. Show the subwindow.
301          (sub_windowClass as window.Window).showWindow((err: BusinessError) => {
302            let errCode: number = err.code;
303            if (errCode) {
304              console.error('Failed to show the window. Cause: ' + JSON.stringify(err));
305              return;
306            }
307            console.info('Succeeded in showing the window.');
308          });
309        });
310      })
311    }
312  }
313  private destroySubWindow(){
314    // 4. Destroy the subwindow when it is no longer needed (depending on the service logic).
315    (sub_windowClass as window.Window).destroyWindow((err: BusinessError) => {
316      let errCode: number = err.code;
317      if (errCode) {
318        console.error('Failed to destroy the window. Cause: ' + JSON.stringify(err));
319        return;
320      }
321      console.info('Succeeded in destroying the window.');
322    });
323  }
324  build() {
325    Row() {
326      Column() {
327        Text(this.message)
328          .fontSize(50)
329          .fontWeight(FontWeight.Bold)
330        Button(){
331          Text('CreateSubWindow')
332          .fontSize(24)
333          .fontWeight(FontWeight.Normal)
334        }.width(220).height(68)
335        .margin({left:10, top:60})
336        .onClick(() => {
337          this.CreateSubWindow()
338        })
339        Button(){
340          Text('destroySubWindow')
341          .fontSize(24)
342          .fontWeight(FontWeight.Normal)
343        }.width(220).height(68)
344        .margin({left:10, top:60})
345        .onClick(() => {
346          this.destroySubWindow()
347        })
348      }
349      .width('100%')
350    }
351    .height('100%')
352  }
353}
354```
355
356```ts
357// subWindow.ets
358@Entry
359@Component
360struct SubWindow {
361  @State message: string = 'Hello subWindow';
362  build() {
363    Row() {
364      Column() {
365        Text(this.message)
366          .fontSize(50)
367          .fontWeight(FontWeight.Bold)
368      }
369      .width('100%')
370    }
371    .height('100%')
372  }
373}
374```
375
376## Experiencing the Immersive Window Feature
377
378To create a better video watching and gaming experience, you can use the immersive window feature to hide the status bar and navigation bar. This feature is available only for the main window of an application. Since API version 10, the immersive window has the same size as the full screen by default; its layout is controlled by the component module; the background color of its status bar and navigation bar is transparent, and the text color is black. When an application window calls **setWindowLayoutFullScreen**, with **true** passed in, an immersive window layout is used. If **false** is passed in, a non-immersive window layout is used.
379
380> **NOTE**
381>
382> Currently, immersive UI development supports window-level configuration, but not page-level configuration. If page redirection is required, you can set the immersive mode at the beginning of the page lifecycle, for example, in the **onPageShow** callback, and then restore the default settings when the page exits, for example, in the **onPageHide** callback.
383
384### How to Develop
385
3861. Obtain the main window.
387
388   Call **getMainWindow** to obtain the main window of the application.
389
3902. Implement the immersive effect. You can use either of the following methods:
391
392   - Method 1: When the main window of the application is a full-screen window, call **setWindowSystemBarEnable** to hide the status bar and navigation bar.
393
394   - Method 2: Call **setWindowLayoutFullScreen** to enable the full-screen mode for the main window layout. Call **setWindowSystemBarProperties** to set the opacity, background color, text color, and highlighted icon of the status bar and navigation bar to create a display effect consistent with that of the main window.
395
3963. Load content to the immersive window.
397
398   Call **loadContent** to load content to the immersive window.
399
400```ts
401import { UIAbility } from '@kit.AbilityKit';
402import { window } from '@kit.ArkUI';
403import { BusinessError } from '@kit.BasicServicesKit';
404
405export default class EntryAbility extends UIAbility {
406  onWindowStageCreate(windowStage: window.WindowStage) {
407    // 1. Obtain the main window of the application.
408    let windowClass: window.Window | null = null;
409    windowStage.getMainWindow((err: BusinessError, data) => {
410      let errCode: number = err.code;
411      if (errCode) {
412        console.error('Failed to obtain the main window. Cause: ' + JSON.stringify(err));
413        return;
414      }
415      windowClass = data;
416      console.info('Succeeded in obtaining the main window. Data: ' + JSON.stringify(data));
417
418      // 2. Implement the immersive effect by hiding the status bar and navigation bar.
419      let names: Array<'status' | 'navigation'> = [];
420      windowClass.setWindowSystemBarEnable(names)
421        .then(() => {
422          console.info('Succeeded in setting the system bar to be visible.');
423        })
424        .catch((err: BusinessError) => {
425          console.error('Failed to set the system bar to be visible. Cause:' + JSON.stringify(err));
426        });
427      // 2. Alternatively, implement the immersive effect by setting the properties of the status bar and navigation bar.
428      let isLayoutFullScreen = true;
429      windowClass.setWindowLayoutFullScreen(isLayoutFullScreen)
430        .then(() => {
431          console.info('Succeeded in setting the window layout to full-screen mode.');
432        })
433        .catch((err: BusinessError) => {
434          console.error('Failed to set the window layout to full-screen mode. Cause:' + JSON.stringify(err));
435        });
436      let sysBarProps: window.SystemBarProperties = {
437        statusBarColor: '#ff00ff',
438        navigationBarColor: '#00ff00',
439        // The following properties are supported since API version 8.
440        statusBarContentColor: '#ffffff',
441        navigationBarContentColor: '#ffffff'
442      };
443      windowClass.setWindowSystemBarProperties(sysBarProps)
444        .then(() => {
445          console.info('Succeeded in setting the system bar properties.');
446        })
447        .catch((err: BusinessError) => {
448          console.error('Failed to set the system bar properties. Cause: ' + JSON.stringify(err));
449        });
450    })
451    // 3. Load content to the immersive window.
452    windowStage.loadContent("pages/page2", (err: BusinessError) => {
453      let errCode: number = err.code;
454      if (errCode) {
455        console.error('Failed to load the content. Cause:' + JSON.stringify(err));
456        return;
457      }
458      console.info('Succeeded in loading the content.');
459    });
460  }
461};
462```
463
464<!--RP2-->
465## Setting a Floating Window<!--RP2End-->
466
467A floating window is created based on an existing task. It is always displayed in the foreground, even if the task used for creating the floating window is switched to the background. Generally, the floating window is above all application windows. You can create a floating window and set its properties.
468
469
470### How to Develop
471
472<!--RP1-->
473**Prerequisites**: To create a floating window (a window of the type **WindowType.TYPE_FLOAT**), you must request the **ohos.permission.SYSTEM_FLOAT_WINDOW** permission. For details, see [Requesting Permissions for system_basic Applications](../security/AccessToken/determine-application-mode.md#requesting-permissions-for-system_basic-applications).
474<!--RP1End-->
475
4761. Create a floating window.
477
478   Call **window.createWindow** to create a floating window.
479
4802. Set properties of the floating window.
481
482   After the floating window is created, you can set its properties, such as the size, position, background color, and brightness.
483
4843. Load content to and show the floating window.
485
486   Call **setUIContent** to load content to the floating window and **showWindow** to show the window.
487
4884. Destroy the floating window.
489
490   When the floating window is no longer needed, you can call **destroyWindow** to destroy it.
491
492```ts
493import { UIAbility } from '@kit.AbilityKit';
494import { window } from '@kit.ArkUI';
495import { BusinessError } from '@kit.BasicServicesKit';
496
497export default class EntryAbility extends UIAbility {
498  onWindowStageCreate(windowStage: window.WindowStage) {
499    // 1. Create a floating window.
500    let windowClass: window.Window | null = null;
501    let config: window.Configuration = {
502      name: "floatWindow", windowType: window.WindowType.TYPE_FLOAT, ctx: this.context
503    };
504    window.createWindow(config, (err: BusinessError, data) => {
505      let errCode: number = err.code;
506      if (errCode) {
507        console.error('Failed to create the floatWindow. Cause: ' + JSON.stringify(err));
508        return;
509      }
510      console.info('Succeeded in creating the floatWindow. Data: ' + JSON.stringify(data));
511      windowClass = data;
512      // 2. Set the position, size, and other properties of the floating window.
513      windowClass.moveWindowTo(300, 300, (err: BusinessError) => {
514        let errCode: number = err.code;
515        if (errCode) {
516          console.error('Failed to move the window. Cause:' + JSON.stringify(err));
517          return;
518        }
519        console.info('Succeeded in moving the window.');
520      });
521      windowClass.resize(500, 500, (err: BusinessError) => {
522        let errCode: number = err.code;
523        if (errCode) {
524          console.error('Failed to change the window size. Cause:' + JSON.stringify(err));
525          return;
526        }
527        console.info('Succeeded in changing the window size.');
528      });
529      // 3.1 Load content to the floating window.
530      windowClass.setUIContent("pages/page4", (err: BusinessError) => {
531        let errCode: number = err.code;
532        if (errCode) {
533          console.error('Failed to load the content. Cause:' + JSON.stringify(err));
534          return;
535        }
536        console.info('Succeeded in loading the content.');
537        // 3.2 Show the floating window.
538        (windowClass as window.Window).showWindow((err: BusinessError) => {
539          let errCode: number = err.code;
540          if (errCode) {
541            console.error('Failed to show the window. Cause: ' + JSON.stringify(err));
542            return;
543          }
544          console.info('Succeeded in showing the window.');
545        });
546      });
547      // 4. Destroy the floating window when it is no longer needed (depending on the service logic).
548      windowClass.destroyWindow((err: BusinessError) => {
549        let errCode: number = err.code;
550        if (errCode) {
551          console.error('Failed to destroy the window. Cause: ' + JSON.stringify(err));
552          return;
553        }
554        console.info('Succeeded in destroying the window.');
555      });
556    });
557  }
558};
559```
560
561## Listening for Interactive and Non-Interactive Window Events
562
563When running in the foreground, an application may switch between interactive and non-interactive states and process services depending on the state. For example, when the user opens the **Recents** screen, an application becomes non-interactive and pauses the service interaction with the user, such as video playback or camera preview; when the user switched back to the foreground, the application becomes interactive again, and the paused service needs to be resumed.
564
565### How to Develop
566
567After a **WindowStage** object is created, the application can listen for the **'windowStageEvent'** event to obtain window stage lifecycle changes, for example, whether the window stage is interactive or non-interactive in the foreground. The application can process services based on the reported event status.
568
569```ts
570import { UIAbility } from '@kit.AbilityKit';
571import { window } from '@kit.ArkUI';
572
573export default class EntryAbility extends UIAbility {
574  onWindowStageCreate(windowStage: window.WindowStage) {
575    try {
576      windowStage.on('windowStageEvent', (data) => {
577        console.info('Succeeded in enabling the listener for window stage event changes. Data: ' +
578          JSON.stringify(data));
579
580        // Process services based on the event status.
581        if (data == window.WindowStageEventType.SHOWN) {
582          console.info('current window stage event is SHOWN');
583          // The application enters the foreground and is interactive by default.
584          // ...
585        } else if (data == window.WindowStageEventType.HIDDEN) {
586          console.info('current window stage event is HIDDEN');
587          // The application enters the background and is non-interactive by default.
588          // ...
589        } else if (data == window.WindowStageEventType.PAUSED) {
590          console.info('current window stage event is PAUSED');
591          // The user opens the Recents screen when the application is running in the foreground, and the application becomes non-interactive.
592          // ...
593        } else if (data == window.WindowStageEventType.RESUMED) {
594          console.info('current window stage event is RESUMED');
595          // The user switches back from the Recents screen to the application, and the application becomes interactive.
596          // ...
597        }
598
599        // ...
600      });
601    } catch (exception) {
602      console.error('Failed to enable the listener for window stage event changes. Cause:' +
603        JSON.stringify(exception));
604    }
605  }
606}
607```
608