1c5c2eed7Sopenharmony_ci/*
2c5c2eed7Sopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
3c5c2eed7Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4c5c2eed7Sopenharmony_ci * you may not use this file except in compliance with the License.
5c5c2eed7Sopenharmony_ci * You may obtain a copy of the License at
6c5c2eed7Sopenharmony_ci *
7c5c2eed7Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
8c5c2eed7Sopenharmony_ci *
9c5c2eed7Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10c5c2eed7Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11c5c2eed7Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12c5c2eed7Sopenharmony_ci * See the License for the specific language governing permissions and
13c5c2eed7Sopenharmony_ci * limitations under the License.
14c5c2eed7Sopenharmony_ci */
15c5c2eed7Sopenharmony_ciimport UIExtensionAbility from '@ohos.app.ability.UIExtensionAbility';
16c5c2eed7Sopenharmony_ciimport window from '@ohos.window';
17c5c2eed7Sopenharmony_ciimport { Log, PermissionDialogException, setAvoidArea } from '../common/utils/utils';
18c5c2eed7Sopenharmony_ciimport UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession';
19c5c2eed7Sopenharmony_ciimport Want from '@ohos.app.ability.Want';
20c5c2eed7Sopenharmony_ciimport uiExtensionHost from '@ohos.uiExtensionHost';
21c5c2eed7Sopenharmony_ciimport display from '@ohos.display';
22c5c2eed7Sopenharmony_ciimport { BusinessError } from '@ohos.base';
23c5c2eed7Sopenharmony_ciimport Constants from '../common/utils/constant';
24c5c2eed7Sopenharmony_ciimport AbilityConstant from '@ohos.app.ability.AbilityConstant';
25c5c2eed7Sopenharmony_ci
26c5c2eed7Sopenharmony_cilet terminating: boolean = false;
27c5c2eed7Sopenharmony_cilet state: boolean = false;
28c5c2eed7Sopenharmony_ci
29c5c2eed7Sopenharmony_ciexport default class PermissionStateSheetAbility extends UIExtensionAbility {
30c5c2eed7Sopenharmony_ci  private subWindowForHost: window.Window | null = null;
31c5c2eed7Sopenharmony_ci  private shouldChangeState: boolean = true;
32c5c2eed7Sopenharmony_ci
33c5c2eed7Sopenharmony_ci  onCreate(launchParam: AbilityConstant.LaunchParam): void {
34c5c2eed7Sopenharmony_ci    Log.info('PermissionStateSheetAbility on create');
35c5c2eed7Sopenharmony_ci  }
36c5c2eed7Sopenharmony_ci
37c5c2eed7Sopenharmony_ci  onSessionCreate(want: Want, session: UIExtensionContentSession): void {
38c5c2eed7Sopenharmony_ci    Log.info('current state:' + state);
39c5c2eed7Sopenharmony_ci    if (state == true) {
40c5c2eed7Sopenharmony_ci      this.shouldChangeState = false;
41c5c2eed7Sopenharmony_ci      Log.error('window state is already set');
42c5c2eed7Sopenharmony_ci      PermissionDialogException(Constants.ERR_MODAL_ALREADY_EXIST, session);
43c5c2eed7Sopenharmony_ci      this.context.terminateSelf();
44c5c2eed7Sopenharmony_ci      return;
45c5c2eed7Sopenharmony_ci    } else {
46c5c2eed7Sopenharmony_ci      Log.info('set window state to true');
47c5c2eed7Sopenharmony_ci      state = true;
48c5c2eed7Sopenharmony_ci    }
49c5c2eed7Sopenharmony_ci
50c5c2eed7Sopenharmony_ci    let param: Record<string, Object> = {
51c5c2eed7Sopenharmony_ci      'session': session, 'want': want
52c5c2eed7Sopenharmony_ci    };
53c5c2eed7Sopenharmony_ci
54c5c2eed7Sopenharmony_ci    let storage: LocalStorage = new LocalStorage(param);
55c5c2eed7Sopenharmony_ci    this.createSubWindow(session, storage).then(() => {
56c5c2eed7Sopenharmony_ci      // if met error, do not load window,
57c5c2eed7Sopenharmony_ci      // in case of nested error codes passed to applications
58c5c2eed7Sopenharmony_ci      if (terminating) {
59c5c2eed7Sopenharmony_ci        this.context.terminateSelf();
60c5c2eed7Sopenharmony_ci        return;
61c5c2eed7Sopenharmony_ci      }
62c5c2eed7Sopenharmony_ci      this.loadContentAndSetColor(storage).then((result: boolean) => {
63c5c2eed7Sopenharmony_ci        if (!result) {
64c5c2eed7Sopenharmony_ci          return;
65c5c2eed7Sopenharmony_ci        }
66c5c2eed7Sopenharmony_ci        this.showWindow();
67c5c2eed7Sopenharmony_ci      })
68c5c2eed7Sopenharmony_ci    })
69c5c2eed7Sopenharmony_ci  }
70c5c2eed7Sopenharmony_ci
71c5c2eed7Sopenharmony_ci  onSessionDestroy(session: UIExtensionContentSession): void {
72c5c2eed7Sopenharmony_ci    Log.info('OnSessionDestroy start');
73c5c2eed7Sopenharmony_ci    try {
74c5c2eed7Sopenharmony_ci      Log.info('try to reshow non-secure windows.');
75c5c2eed7Sopenharmony_ci      this.subWindowForHost?.hideNonSystemFloatingWindows(false);
76c5c2eed7Sopenharmony_ci    } catch (error) {
77c5c2eed7Sopenharmony_ci      Log.error('failed to reshowing the non-secure windows. Cause: ' + JSON.stringify(error));
78c5c2eed7Sopenharmony_ci    }
79c5c2eed7Sopenharmony_ci    try {
80c5c2eed7Sopenharmony_ci      this.subWindowForHost?.off('windowEvent');
81c5c2eed7Sopenharmony_ci    } catch (exception) {
82c5c2eed7Sopenharmony_ci      Log.error('fail to call off window event: ' + JSON.stringify(exception));
83c5c2eed7Sopenharmony_ci    }
84c5c2eed7Sopenharmony_ci    this.subWindowForHost?.destroyWindow((err, data) => {
85c5c2eed7Sopenharmony_ci      if (err && err.code !== 0) {
86c5c2eed7Sopenharmony_ci        Log.info('destroy window:' + err.message + ' ' + JSON.stringify(data));
87c5c2eed7Sopenharmony_ci      }
88c5c2eed7Sopenharmony_ci    });
89c5c2eed7Sopenharmony_ci    Log.info('destroy window end');
90c5c2eed7Sopenharmony_ci  }
91c5c2eed7Sopenharmony_ci
92c5c2eed7Sopenharmony_ci  onForeground(): void {
93c5c2eed7Sopenharmony_ci    Log.info('onForegorund');
94c5c2eed7Sopenharmony_ci    this.subWindowForHost?.showWindow();
95c5c2eed7Sopenharmony_ci  }
96c5c2eed7Sopenharmony_ci
97c5c2eed7Sopenharmony_ci  onBackground(): void {
98c5c2eed7Sopenharmony_ci    Log.info('extension onBackground');
99c5c2eed7Sopenharmony_ci    this.subWindowForHost?.hide();
100c5c2eed7Sopenharmony_ci  }
101c5c2eed7Sopenharmony_ci
102c5c2eed7Sopenharmony_ci  onDestroy(): void | Promise<void> {
103c5c2eed7Sopenharmony_ci    Log.info('onDestroy');
104c5c2eed7Sopenharmony_ci    if (this.shouldChangeState == true) {
105c5c2eed7Sopenharmony_ci      state = false;
106c5c2eed7Sopenharmony_ci      Log.info('set window state to false');
107c5c2eed7Sopenharmony_ci    }
108c5c2eed7Sopenharmony_ci    this.subWindowForHost = null;
109c5c2eed7Sopenharmony_ci  }
110c5c2eed7Sopenharmony_ci
111c5c2eed7Sopenharmony_ci  async loadContentAndSetColor(storage: LocalStorage): Promise<boolean> {
112c5c2eed7Sopenharmony_ci    let promise: Promise<void> | undefined =
113c5c2eed7Sopenharmony_ci      this.subWindowForHost?.loadContent('PermissionSheet/PermissionStateSheetDialog', storage);
114c5c2eed7Sopenharmony_ci    if (promise) {
115c5c2eed7Sopenharmony_ci      await promise.then(() => {
116c5c2eed7Sopenharmony_ci        Log.info('Succeeded in loading the content');
117c5c2eed7Sopenharmony_ci        try {
118c5c2eed7Sopenharmony_ci          this.subWindowForHost?.setWindowBackgroundColor('#00000000');
119c5c2eed7Sopenharmony_ci        } catch (error) {
120c5c2eed7Sopenharmony_ci          Log.error(`setWindowBackgroundColor failed, error code ${JSON.stringify(error)}`);
121c5c2eed7Sopenharmony_ci        }
122c5c2eed7Sopenharmony_ci      }).catch((err: BusinessError) => {
123c5c2eed7Sopenharmony_ci        Log.error('Failed to load the content. Cause:' + JSON.stringify(err));
124c5c2eed7Sopenharmony_ci        return false;
125c5c2eed7Sopenharmony_ci      })
126c5c2eed7Sopenharmony_ci    }
127c5c2eed7Sopenharmony_ci    return true;
128c5c2eed7Sopenharmony_ci  }
129c5c2eed7Sopenharmony_ci
130c5c2eed7Sopenharmony_ci  async createSubWindow(session: UIExtensionContentSession, storage: LocalStorage): Promise<void> {
131c5c2eed7Sopenharmony_ci    let extensionWinProxy: uiExtensionHost.UIExtensionHostWindowProxy = session?.getUIExtensionHostWindowProxy();
132c5c2eed7Sopenharmony_ci    setAvoidArea(extensionWinProxy);
133c5c2eed7Sopenharmony_ci    let subWindowOpts: window.SubWindowOptions = {
134c5c2eed7Sopenharmony_ci      title: '', decorEnabled: false, isModal: true,
135c5c2eed7Sopenharmony_ci    };
136c5c2eed7Sopenharmony_ci    if (!extensionWinProxy) {
137c5c2eed7Sopenharmony_ci      Log.error('extensionWinProxy is nullptr!');
138c5c2eed7Sopenharmony_ci      return;
139c5c2eed7Sopenharmony_ci    }
140c5c2eed7Sopenharmony_ci    // try to create subwindow first
141c5c2eed7Sopenharmony_ci    await extensionWinProxy.createSubWindowWithOptions('permissionSubWindowForHost' + Date(), subWindowOpts)
142c5c2eed7Sopenharmony_ci      .then((subWindow: window.Window)=>{
143c5c2eed7Sopenharmony_ci        this.subWindowForHost = subWindow;
144c5c2eed7Sopenharmony_ci      }).catch((err: BusinessError) => {
145c5c2eed7Sopenharmony_ci        Log.error('create sub window error:' + err.message + ' ' + err.code);
146c5c2eed7Sopenharmony_ci      });
147c5c2eed7Sopenharmony_ci    // if create subwindow fails, try create floating window
148c5c2eed7Sopenharmony_ci    if (!this.subWindowForHost) {
149c5c2eed7Sopenharmony_ci      Log.error('subwindow is null, failed to create subwindow, tray create float window');
150c5c2eed7Sopenharmony_ci      await this.createFloatingWindow(session);
151c5c2eed7Sopenharmony_ci      if (!this.subWindowForHost) {
152c5c2eed7Sopenharmony_ci        terminating = true;
153c5c2eed7Sopenharmony_ci        PermissionDialogException(Constants.ERR_MODAL_ALREADY_EXIST, session);
154c5c2eed7Sopenharmony_ci        this.context.terminateSelf();
155c5c2eed7Sopenharmony_ci        Log.error('floating is null, failed to create float window, terminate self');
156c5c2eed7Sopenharmony_ci        return;
157c5c2eed7Sopenharmony_ci      }
158c5c2eed7Sopenharmony_ci    }
159c5c2eed7Sopenharmony_ci    let dis = display.getDefaultDisplaySync();
160c5c2eed7Sopenharmony_ci    await this.subWindowForHost?.resize(dis.width, dis.height).catch((err: BusinessError) => {
161c5c2eed7Sopenharmony_ci      Log.error('resize window error: ' + err.code + ' ' + err.message);
162c5c2eed7Sopenharmony_ci    });
163c5c2eed7Sopenharmony_ci    this.subWindowForHost?.on('windowEvent', (eventType: window.WindowEventType) => {
164c5c2eed7Sopenharmony_ci      Log.info('window event type is: ' + eventType);
165c5c2eed7Sopenharmony_ci      storage.setOrCreate('permissionWindow', eventType);
166c5c2eed7Sopenharmony_ci    })
167c5c2eed7Sopenharmony_ci    Log.info('create and resize window success');
168c5c2eed7Sopenharmony_ci    return;
169c5c2eed7Sopenharmony_ci  }
170c5c2eed7Sopenharmony_ci
171c5c2eed7Sopenharmony_ci  async createFloatingWindow(session: UIExtensionContentSession): Promise<void> {
172c5c2eed7Sopenharmony_ci    let config: window.Configuration = {
173c5c2eed7Sopenharmony_ci      name: 'PermissionFloatWindow' + Date(),
174c5c2eed7Sopenharmony_ci      windowType: window.WindowType.TYPE_FLOAT,
175c5c2eed7Sopenharmony_ci      ctx: this.context
176c5c2eed7Sopenharmony_ci    }
177c5c2eed7Sopenharmony_ci    let promise: Promise<window.Window> | undefined = window.createWindow(config);
178c5c2eed7Sopenharmony_ci    await promise.then((floatWindow: window.Window) => {
179c5c2eed7Sopenharmony_ci      this.subWindowForHost = floatWindow;
180c5c2eed7Sopenharmony_ci    }).catch((err: BusinessError) => {
181c5c2eed7Sopenharmony_ci      Log.error('create floating window error:' + err.message + ' ' + err.code);
182c5c2eed7Sopenharmony_ci      // if modal already exist , return
183c5c2eed7Sopenharmony_ci      if (err.code === Constants.CREATE_WINDOW_REPEATED) {
184c5c2eed7Sopenharmony_ci        Log.error('try to create subwindow repeatedly');
185c5c2eed7Sopenharmony_ci        terminating = true;
186c5c2eed7Sopenharmony_ci        PermissionDialogException(Constants.ERR_MODAL_ALREADY_EXIST, session);
187c5c2eed7Sopenharmony_ci        this.context.terminateSelf();
188c5c2eed7Sopenharmony_ci      }
189c5c2eed7Sopenharmony_ci    });
190c5c2eed7Sopenharmony_ci    if (!this.subWindowForHost) {
191c5c2eed7Sopenharmony_ci      Log.error('create floating fail');
192c5c2eed7Sopenharmony_ci      return;
193c5c2eed7Sopenharmony_ci    }
194c5c2eed7Sopenharmony_ci    Log.info('create floating window success');
195c5c2eed7Sopenharmony_ci  }
196c5c2eed7Sopenharmony_ci
197c5c2eed7Sopenharmony_ci  showWindow(): void {
198c5c2eed7Sopenharmony_ci    this.subWindowForHost?.showWindow((err) => {
199c5c2eed7Sopenharmony_ci      if (err && err.code !== 0) {
200c5c2eed7Sopenharmony_ci        Log.error(`failed to showWindow for subWindow, ${JSON.stringify(err)}`);
201c5c2eed7Sopenharmony_ci        return;
202c5c2eed7Sopenharmony_ci      }
203c5c2eed7Sopenharmony_ci      this.subWindowForHost?.hideNonSystemFloatingWindows(true);
204c5c2eed7Sopenharmony_ci      Log.info('show window and set non floating success');
205c5c2eed7Sopenharmony_ci    })
206c5c2eed7Sopenharmony_ci  }
207c5c2eed7Sopenharmony_ci}