1/*
2 * Copyright (c) 2021-2023 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 { backBar } from '../common/components/backBar';
17import router from '@ohos.router';
18import common from '@ohos.app.ability.common';
19import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
20import bundleManager from '@ohos.bundle.bundleManager';
21import { BusinessError } from '@ohos.base';
22import { groups, userGrantPermissions } from '../common/model/permissionGroup';
23import Constants from '../common/utils/constant';
24import { Log, verifyAccessToken, getGroupIdByPermission } from '../common/utils/utils';
25import { PermissionObj, AppInfo } from '../common/model/typedef';
26import { GlobalContext } from '../common/utils/globalContext';
27
28const FUZZY_LOCATION_PERMISSION = 'ohos.permission.APPROXIMATELY_LOCATION';
29const PRECISE_LOCATION_PERMISSION = 'ohos.permission.LOCATION';
30const BACKGROUND_LOCATION_PERMISSION = 'ohos.permission.LOCATION_IN_BACKGROUND';
31const DOWNLOAD_PERMISSION = 'ohos.permission.READ_WRITE_DOWNLOAD_DIRECTORY';
32const DESKTOP_PERMISSION = 'ohos.permission.READ_WRITE_DESKTOP_DIRECTORY';
33const DOCUMENTS_PERMISSION = 'ohos.permission.READ_WRITE_DOCUMENTS_DIRECTORY';
34
35@Entry
36@Component
37struct appNamePlusPage {
38  private context = getContext(this) as common.UIAbilityContext;
39  @State allowedListItem: PermissionObj[] = []; // Array of allowed permissions
40  @State bannedListItem: PermissionObj[] = []; // array of forbidden permissions
41  @State applicationInfo: AppInfo = GlobalContext.load('applicationInfo'); // Routing jump data
42  @State bundleName: string = GlobalContext.load('bundleName');
43  @State label: string = '';
44  @State isTouch: string = '';
45  @State isGranted: number = Constants.PERMISSION_ALLOW;
46  @State folderStatus: boolean[] = [false, false, false];
47  @State reqUserPermissions: Permissions[] = [];
48
49  @Builder ListItemLayout(item: PermissionObj, status: number) {
50    ListItem() {
51      Row() {
52        Column() {
53          Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
54            Row() {
55              Text(item.groupName)
56                .fontSize(Constants.TEXT_MIDDLE_FONT_SIZE)
57                .fontColor($r('sys.color.font_primary'))
58                .fontWeight(FontWeight.Medium)
59                .flexGrow(Constants.FLEX_GROW)
60              SymbolGlyph($r('sys.symbol.chevron_forward'))
61                .width(Constants.IMAGE_WIDTH)
62                .height(Constants.IMAGE_HEIGHT)
63                .fontSize(Constants.FONT_SIZE_18_vp)
64                .fontColor([$r('sys.color.icon_tertiary')])
65                .fontWeight(FontWeight.Medium)
66            }
67            .width(Constants.FULL_WIDTH)
68            .height(Constants.LISTITEM_ROW_HEIGHT)
69          }
70        }.onClick(() => {
71          GlobalContext.store('currentPermissionGroup', item.group);
72          router.pushUrl({
73            url: item.group == 'OTHER' ? 'pages/other-permissions' : 'pages/application-tertiary',
74            params: {
75              bundleName: this.applicationInfo.bundleName,
76              backTitle: item.groupName,
77              permission: item.permission,
78              status,
79              tokenId: this.applicationInfo.tokenId
80            }
81          });
82        })
83      }
84    }.padding({ left: $r('sys.float.ohos_id_card_margin_start'), right: $r('sys.float.ohos_id_card_margin_end') })
85    .borderRadius($r('sys.float.ohos_id_corner_radius_default_l'))
86    .linearGradient((this.isTouch === item.group) ? {
87        angle: 90,
88        direction: GradientDirection.Right,
89        colors: [['#DCEAF9', 0.0], ['#FAFAFA', 1.0]]
90      } : {
91        angle: 90,
92        direction: GradientDirection.Right,
93        colors: [[$r('sys.color.comp_background_list_card'), 1], [$r('sys.color.comp_background_list_card'), 1]]
94      })
95    .onTouch(event => {
96      if (event === undefined) {
97        return;
98      }
99      if (event.type === TouchType.Down) {
100        this.isTouch = item.group;
101      }
102      if (event.type === TouchType.Up) {
103        this.isTouch = '';
104      }
105    })
106  }
107
108  async getReqUserPermissions(info: AppInfo) {
109    let acManager = abilityAccessCtrl.createAtManager();
110    if (info.permissions.length > 0) {
111      for (let j = 0; j < info.permissions.length; j++) {
112        let permission = info.permissions[j];
113        if (userGrantPermissions.indexOf(permission) == -1) {
114          continue;
115        }
116        try {
117          let flag = await acManager.getPermissionFlags(info.tokenId, permission);
118          if (flag == Constants.PERMISSION_SYSTEM_FIXED) {
119            continue;
120          }
121        } catch (err) {
122          Log.error('getPermissionFlags error: ' + JSON.stringify(err));
123        }
124        this.reqUserPermissions.push(permission);
125      }
126    }
127  }
128
129  async initApplicationInfo(info: AppInfo) {
130    Log.info(`labelResource: ` + JSON.stringify(info.labelResource));
131    let resourceManager = this.context.createBundleContext(info.bundleName).resourceManager;
132
133    if (info.labelResource.id !== 0) {
134      info.label = await this.context.resourceManager.getStringValue(info.labelResource);
135    } else {
136      info.label = await resourceManager.getStringValue(info.labelId);
137    }
138
139    try {
140      if (info.iconResource.id !== 0) {
141        let iconDescriptor = this.context.resourceManager.getDrawableDescriptor(info.iconResource);
142        info.icon = iconDescriptor?.getPixelMap();
143      } else {
144        let iconDescriptor = resourceManager.getDrawableDescriptor(info.iconId);
145        info.icon = iconDescriptor?.getPixelMap();
146      }
147    } catch (error) {
148      Log.error(`getDrawableDescriptor failed, error code: ${error.code}, message: ${error.message}.`);
149    }
150
151    if (!info.icon) {
152      info.icon = $r('app.media.icon');
153    }
154
155    this.reqUserPermissions = [];
156    await this.getReqUserPermissions(info);
157    let groupIds: number[] = [];
158    for (let i = 0; i < this.reqUserPermissions.length; i++) {
159      let groupId = getGroupIdByPermission(this.reqUserPermissions[i])
160      if (groupIds.indexOf(groupId) == -1) {
161        groupIds.push(groupId);
162      }
163    }
164    info.permissions = this.reqUserPermissions;
165    info.groupId = groupIds;
166  }
167
168  /**
169   * Initialize permission status information and group permission information
170   */
171  async initialPermissions() {
172    if (this.bundleName && !this.applicationInfo.groupId.length) {
173      await this.initApplicationInfo(this.applicationInfo);
174    }
175    let reqPermissions = this.applicationInfo.permissions;
176    let reqGroupIds = this.applicationInfo.groupId;
177
178    this.allowedListItem = [];
179    this.bannedListItem = [];
180
181    for (let i = 0; i < reqGroupIds.length; i++) {
182      let id = reqGroupIds[i];
183      let groupName = groups[id].groupName;
184      let group = groups[id].name;
185      let groupReqPermissions: Permissions[] = [];
186      for (let j = 0; j < reqPermissions.length; j++) {
187        let permission = reqPermissions[j];
188        if (groups[id].permissions.indexOf(permission) != -1) {
189          groupReqPermissions.push(permission);
190        }
191      }
192      this.isGranted = group === 'LOCATION' ? Constants.PERMISSION_BAN : Constants.PERMISSION_ALLOW;
193      this.folderStatus = [false, false, false];
194      await this.getStatus(groupReqPermissions, group);
195
196      if (
197        this.isGranted === Constants.PERMISSION_ALLOW || this.isGranted === Constants.PERMISSION_ALLOWED_ONLY_DURING_USE
198      ) {
199        this.allowedListItem.push(new PermissionObj(groupName, groupReqPermissions, group));
200      } else {
201        if (group === 'FOLDER' && this.folderStatus.includes(true)) {
202          this.allowedListItem.push(new PermissionObj(groupName, groupReqPermissions, group));
203        } else {
204          this.bannedListItem.push(new PermissionObj(groupName, groupReqPermissions, group));
205        }
206      }
207
208      GlobalContext.store('folderStatus', this.folderStatus);
209    }
210  }
211
212  async getStatus(groupReqPermissions: Permissions[], group: string) {
213    if (group === 'LOCATION') {
214      try {
215        let acManager = abilityAccessCtrl.createAtManager();
216        let fuzzyState = acManager.verifyAccessTokenSync(this.applicationInfo.tokenId, FUZZY_LOCATION_PERMISSION);
217        fuzzyState === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED ?
218          this.isGranted = Constants.PERMISSION_ALLOWED_ONLY_DURING_USE : null;
219        let backgroundState =
220          acManager.verifyAccessTokenSync(this.applicationInfo.tokenId, BACKGROUND_LOCATION_PERMISSION);
221        backgroundState === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED ?
222          this.isGranted = Constants.PERMISSION_ALLOW : null;
223        await acManager.getPermissionFlags(this.applicationInfo.tokenId, FUZZY_LOCATION_PERMISSION ).then(flag => {
224          flag === Constants.PERMISSION_ALLOW_THIS_TIME ? this.isGranted = Constants.PERMISSION_ONLY_THIS_TIME : null;
225        })
226      } catch (err) {
227        Log.error('change location status error: ' + JSON.stringify(err));
228      }
229      GlobalContext.store('locationStatus', this.isGranted);
230      return true;
231    }
232    for (let i = 0; i < groupReqPermissions.length; i++) {
233      let permission = groupReqPermissions[i];
234      let res = await verifyAccessToken(this.applicationInfo.tokenId, permission);
235      if (res != abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
236        this.isGranted = Constants.PERMISSION_BAN;
237      }
238      if (group === 'FOLDER' && res === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
239        switch (permission) {
240          case DOWNLOAD_PERMISSION:
241            this.folderStatus[0] = true;
242            break;
243          case DESKTOP_PERMISSION:
244            this.folderStatus[1] = true;
245            break;
246          case DOCUMENTS_PERMISSION:
247            this.folderStatus[2] = true;
248            break;
249        }
250      }
251    }
252    return true;
253  }
254
255  /**
256   * Lifecycle function, triggered once when this page is displayed
257   */
258  onPageShow() {
259    this.initialPermissions();
260    bundleManager.getApplicationInfo(
261      this.applicationInfo.bundleName, bundleManager.ApplicationFlag.GET_APPLICATION_INFO_DEFAULT
262    ).then(appInfo => {
263      let bundleContext = this.context.createBundleContext(this.applicationInfo.bundleName)
264      bundleContext.resourceManager.getStringValue(appInfo.labelId, (error, value) => {
265        if (value) {
266          this.applicationInfo.label = value;
267          GlobalContext.store('applicationInfo', this.applicationInfo);
268          this.label = value
269        }
270      })
271    }).catch((error: BusinessError) => {
272      Log.error('getApplicationInfo error: ' + JSON.stringify(error));
273    })
274  }
275
276  build() {
277    Column() {
278      GridRow({ gutter: Constants.GUTTER, columns: {
279        xs: Constants.XS_COLUMNS, sm: Constants.SM_COLUMNS, md: Constants.MD_COLUMNS, lg: Constants.LG_COLUMNS } }) {
280        GridCol({
281          span: { xs: Constants.XS_SPAN, sm: Constants.SM_SPAN, md: Constants.MD_SPAN, lg: Constants.LG_SPAN },
282          offset: { xs: Constants.XS_OFFSET, sm: Constants.SM_OFFSET, md: Constants.MD_OFFSET, lg: Constants.LG_OFFSET }
283        }) {
284          Row() {
285            Column() {
286              Row() {
287                backBar({ title: JSON.stringify(this.label || this.applicationInfo.label), recordable: false })
288              }
289              Row() {
290                Column() {
291                  if (!this.allowedListItem.length && !this.bannedListItem.length) {
292                    Row() {
293                      List() {
294                        ListItem() {
295                          Row() {
296                            Column() {
297                              Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
298                                Row() {
299                                  Column() {
300                                    Row() {
301                                      Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
302                                        Text($r('app.string.no_permission'))
303                                          .fontSize(Constants.TEXT_MIDDLE_FONT_SIZE)
304                                          .fontColor($r('sys.color.font_primary'))
305                                      }.margin({ top: Constants.FLEX_MARGIN_TOP, bottom: Constants.FLEX_MARGIN_BOTTOM })
306                                    }.height(Constants.FULL_HEIGHT)
307                                  }.flexGrow(Constants.FLEX_GROW)
308                                   .constraintSize({minHeight: Constants.CONSTRAINTSIZE_MINHEIGHT })
309                                }
310                                .width(Constants.FULL_WIDTH)
311                                .height(Constants.LISTITEM_ROW_HEIGHT)
312                              }
313                            }
314                          }
315                        }.padding({ left: Constants.LISTITEM_PADDING_LEFT, right: Constants.LISTITEM_PADDING_RIGHT })
316                      }
317                      .backgroundColor($r('sys.color.comp_background_list_card'))
318                      .borderRadius($r('sys.float.ohos_id_corner_radius_card'))
319                      .padding({ top: Constants.LIST_PADDING_TOP, bottom: Constants.LIST_PADDING_BOTTOM })
320                    }.margin({ top: Constants.ROW_MARGIN_TOP })
321                     .padding({
322                       left: Constants.SECONDARY_LIST_PADDING_LEFT,
323                       right: Constants.SECONDARY_LIST_PADDING_RIGHT
324                     })
325                  } else {
326                    Scroll() {
327                      List() {
328                        if (this.allowedListItem.length) {
329                          ListItem() {
330                            Row() {
331                              Text($r('app.string.allowed'))
332                                .fontSize(Constants.TEXT_SMALL_FONT_SIZE)
333                                .fontColor($r('sys.color.font_secondary'))
334                                .fontWeight(FontWeight.Medium)
335                                .lineHeight(Constants.SUBTITLE_LINE_HEIGHT)
336                            }.constraintSize({ minHeight: Constants.SUBTITLE_MIN_HEIGHT })
337                            .width(Constants.FULL_WIDTH)
338                            .padding({ top: Constants.SUBTITLE_PADDING_TOP, bottom: Constants.SUBTITLE_PADDING_BOTTOM,
339                              left: Constants.SECONDARY_TEXT_MARGIN_LEFT})
340                          }
341
342                          ListItem() {
343                            Row() {
344                              List() {
345                                ForEach(this.allowedListItem, (item: PermissionObj) => {
346                                  this.ListItemLayout(item, Constants.PERMISSION_ALLOW)
347                                }, (item: PermissionObj) => JSON.stringify(item))
348                              }
349                              .backgroundColor($r('sys.color.comp_background_list_card'))
350                              .borderRadius($r('sys.float.ohos_id_corner_radius_card'))
351                              .padding(Constants.LIST_PADDING_TOP)
352                              .divider({
353                                strokeWidth: Constants.DIVIDER,
354                                color: $r('sys.color.comp_divider'),
355                                startMargin: Constants.DEFAULT_MARGIN_START,
356                                endMargin: Constants.DEFAULT_MARGIN_END
357                              })
358                            }.margin({ top: Constants.ROW_MARGIN_TOP })
359                            .padding({
360                              left: Constants.SECONDARY_LIST_PADDING_LEFT,
361                              right: Constants.SECONDARY_LIST_PADDING_RIGHT
362                            })
363                          }
364                        }
365                        if (this.bannedListItem.length) {
366                          ListItem() {
367                            Row() {
368                              Text($r('app.string.banned'))
369                                .fontSize(Constants.TEXT_SMALL_FONT_SIZE)
370                                .fontColor($r('sys.color.font_secondary'))
371                                .fontWeight(FontWeight.Medium)
372                                .lineHeight(Constants.SUBTITLE_LINE_HEIGHT)
373                            }.constraintSize({ minHeight: Constants.SUBTITLE_MIN_HEIGHT })
374                            .width(Constants.FULL_WIDTH)
375                            .padding({ top: Constants.SUBTITLE_PADDING_TOP, bottom: Constants.SUBTITLE_PADDING_BOTTOM,
376                              left: Constants.SECONDARY_TEXT_MARGIN_LEFT})
377                          }
378
379                          ListItem() {
380                            Row() {
381                              List() {
382                                ForEach(this.bannedListItem, (item: PermissionObj) => {
383                                  this.ListItemLayout(item, Constants.PERMISSION_BAN)
384                                }, (item: PermissionObj) => JSON.stringify(item))
385                              }
386                              .backgroundColor($r('sys.color.comp_background_list_card'))
387                              .borderRadius($r('sys.float.ohos_id_corner_radius_card'))
388                              .padding(Constants.LIST_PADDING_TOP)
389                              .divider({
390                                strokeWidth: Constants.DIVIDER,
391                                color: $r('sys.color.comp_divider'),
392                                startMargin: Constants.DEFAULT_MARGIN_START,
393                                endMargin: Constants.DEFAULT_MARGIN_END
394                              })
395                            }.margin({ top: Constants.ROW_MARGIN_TOP })
396                            .padding({
397                              left: Constants.SECONDARY_LIST_PADDING_LEFT,
398                              right: Constants.SECONDARY_LIST_PADDING_RIGHT
399                            })
400                          }
401                        }
402                      }
403                    }.scrollBar(BarState.Off)
404                  }
405                }
406                .width(Constants.FULL_WIDTH)
407                .height(Constants.FULL_HEIGHT)
408              }
409              .layoutWeight(Constants.LAYOUT_WEIGHT)
410            }
411          }
412          .height(Constants.FULL_HEIGHT)
413          .width(Constants.FULL_WIDTH)
414          .backgroundColor($r('sys.color.background_secondary'))
415        }
416      }.backgroundColor($r('sys.color.background_secondary'))
417    }
418  }
419}
420