1/*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import AccountManager from "@ohos.account.osAccount";
17import Log from "./Log";
18import getSingleInstance from "./SingleInstanceHelper";
19
20const TAG = "SwitchUserManager";
21const SUBSCRIBE_KEY = "SystemUiAccount";
22const USER_CHANGE_EVENT = "activate";
23const DELAY_TIME = 3 * 1000;
24export const INVALID_USER_ID = -1;
25
26type AccountInfo = {
27  localId: number;
28  localName: string;
29  photo: string;
30};
31export type UserChangeListener = {
32  userChange: (data: UserInfo) => void;
33};
34
35export class UserInfo {
36  userId: number = INVALID_USER_ID;
37  userName: string = "";
38  userIcon: string | Resource = "";
39  [key: string]: any;
40}
41
42async function getCurrentAccountInfo(): Promise<AccountInfo> {
43  let accountInfos = await AccountManager.getAccountManager().queryAllCreatedOsAccounts();
44  for (let accountInfo of accountInfos) {
45    Log.showDebug(TAG, `accountInfo: ${accountInfo.localId}, isActive: ${accountInfo.isActived}`);
46    if (accountInfo.isActived) {
47      return accountInfo;
48    }
49  }
50  return { localId: INVALID_USER_ID, localName: "", photo: "" };
51}
52
53function parseAccountInfo(accountInfo: AccountInfo): UserInfo {
54  return {
55    userId: accountInfo.localId,
56    userName: accountInfo.localName,
57    userIcon: accountInfo.photo,
58  };
59}
60
61export default class SwitchUserManager {
62  mUserInfo: UserInfo = new UserInfo();
63  mListeners = new Set<UserChangeListener>();
64  mHasWait: boolean = false;
65
66  static getInstance(): SwitchUserManager {
67    return getSingleInstance(SwitchUserManager, TAG);
68  }
69
70  constructor() {
71    Log.showDebug(TAG, `SwitchUserManager constructor`);
72    try {
73      AccountManager.getAccountManager().on(USER_CHANGE_EVENT, SUBSCRIBE_KEY, this.handleUserChange.bind(this));
74    } catch (err) {
75      Log.showError(TAG, `AccountManager.getAccountManager().on error: ${JSON.stringify(err)}`);
76    }
77  }
78
79  public async getCurrentUserInfo(): Promise<UserInfo> {
80    if (this.mUserInfo.userId == INVALID_USER_ID) {
81      this.mUserInfo = parseAccountInfo(await getCurrentAccountInfo());
82      while (!this.mHasWait && this.mUserInfo.userId == INVALID_USER_ID) {
83        await new Promise((resolve) => setTimeout(resolve, DELAY_TIME));
84        this.mUserInfo = parseAccountInfo(await getCurrentAccountInfo());
85      }
86      this.mHasWait = true;
87      this.mUserInfo = parseAccountInfo(await getCurrentAccountInfo());
88    }
89    Log.showDebug(TAG, `getCurrentUserInfo userId: ${this.mUserInfo.userId}`);
90    return this.mUserInfo;
91  }
92
93  public registerListener(listener: UserChangeListener) {
94    this.mListeners.add(listener);
95  }
96
97  public unregisterListener(listener: UserChangeListener) {
98    this.mListeners.delete(listener);
99  }
100
101  handleUserChange(accountId: number): void {
102    AccountManager.getAccountManager()
103      .queryOsAccountById(accountId)
104      .then((accountInfo) => {
105        Log.showInfo(TAG, `userChange, localId: ${accountInfo?.localId}`);
106        this.mUserInfo = parseAccountInfo(accountInfo);
107        this.notifyUserChange();
108      })
109      .catch((err) => Log.showError(TAG, `Can't query account by ${accountId}, err: ${err}`));
110  }
111
112  notifyUserChange() {
113    this.mListeners.forEach((listener) => listener.userChange(this.mUserInfo));
114  }
115}
116