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 bundleManager from '@ohos.bundle.bundleManager'; 20import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl'; 21import { BusinessError } from '@ohos.base'; 22import { showSubpermissionsGrop } from "../common/model/permissionGroup"; 23import { verifyAccessToken, getPermissionLabel } from "../common/utils/utils"; 24import Constants from '../common/utils/constant'; 25import { MediaDocObj, routerParams_3, appInfo } from '../common/utils/typedef'; 26import { GlobalContext } from '../common/utils/globalContext'; 27 28const TAG = 'PermissionManager_MainAbility:'; 29const PRECISE_LOCATION_PERMISSION = 'ohos.permission.LOCATION'; 30let api: number = 0; 31let nowGrantResult = Constants.PERMISSION_NUM; // Authorization results now 32let nowRevokeResult = Constants.PERMISSION_NUM; // Now deauthorize results 33let GrantResultFlag: number[] = []; // Authorization result Flag 34let RevokeResultFlag: number[] = []; // Cancel authorization result Flag 35let accessTokenId: number = 0; 36let reqPermissionInfo: bundleManager.ReqPermissionDetail; 37 38@Entry 39@Component 40struct mediaDocumentPage { 41 private backTitle: ResourceStr = (router.getParams() as routerParams_3).backTitle; 42 private permissions: Permissions[] = (router.getParams() as routerParams_3).permission; 43 private tokenId: number = (router.getParams() as routerParams_3).tokenId; 44 @State refresh: boolean = false; 45 @State isCheckList: boolean[] = []; // Permission status array 46 @State isRefreshReason: number = 0 47 48 build() { 49 Column() { 50 GridRow({ gutter: Constants.GUTTER, columns: { 51 xs: Constants.XS_COLUMNS, sm: Constants.SM_COLUMNS, md: Constants.MD_COLUMNS, lg: Constants.LG_COLUMNS } }) { 52 GridCol({ span: { xs: Constants.XS_SPAN, sm: Constants.SM_SPAN, md: Constants.MD_SPAN, lg: Constants.LG_SPAN }, 53 offset: { xs: Constants.XS_OFFSET, sm: Constants.SM_OFFSET, md: Constants.MD_OFFSET, lg: Constants.LG_OFFSET } }) { 54 Row() { 55 Column() { 56 Row() { 57 backBar({ title: JSON.stringify(this.backTitle), recordable: false }) 58 } 59 Row() { 60 Column() { 61 mediaDocumentItem({ isCheckList: $isCheckList, isRefreshReason: $isRefreshReason }) 62 }.width(Constants.FULL_WIDTH) 63 } 64 .margin({ top: Constants.TITLE_MARGIN_BOTTOM }) 65 .layoutWeight(Constants.LAYOUT_WEIGHT) 66 } 67 } 68 .height(Constants.FULL_HEIGHT) 69 .width(Constants.FULL_WIDTH) 70 .backgroundColor($r("sys.color.ohos_id_color_sub_background")) 71 } 72 }.backgroundColor($r("sys.color.ohos_id_color_sub_background")) 73 } 74 } 75 76 onPageShow() { 77 console.log(TAG + 'onPageShow'); 78 if (this.refresh) this.refreshStatus(); 79 this.refresh = true; 80 } 81 82 async refreshStatus() { 83 this.isRefreshReason ++; 84 console.log(TAG + 'Refresh permission status'); 85 let isGranted = true; 86 for (let i = 0; i < this.permissions.length; i++) { 87 let permission = this.permissions[i]; 88 if (api >= Constants.API_VERSION_SUPPORT_STAGE && permission == PRECISE_LOCATION_PERMISSION) { 89 continue; 90 } 91 let res = await verifyAccessToken(this.tokenId, permission); 92 if (res != abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { 93 isGranted = false; 94 } 95 } 96 console.log(TAG + 'isGranted: ' + JSON.stringify(isGranted)); 97 98 if (isGranted) { 99 this.isCheckList = [true, false]; 100 } else { 101 this.isCheckList = [false, true]; 102 } 103 } 104} 105 106@Component 107struct mediaDocumentItem { 108 private context = getContext(this) as common.UIAbilityContext; 109 private backTitle: ResourceStr = (router.getParams() as routerParams_3).backTitle; 110 private bundleName: string = (router.getParams() as routerParams_3).bundleName; 111 private permissions: Permissions[] = (router.getParams() as routerParams_3).permission; 112 private status: number = (router.getParams() as routerParams_3).status; 113 @State currentGroup: string = GlobalContext.load('currentPermissionGroup'); 114 @State applicationInfo: appInfo = GlobalContext.load('applicationInfo'); 115 @State mediaDocListItem: MediaDocObj[] = []; // Permission information array 116 @Link isCheckList: boolean[]; // Permission status array 117 @State accurateIsOn: boolean = true; 118 @State api: number = 0; 119 @State isRisk: boolean = false; // Whether it is a risky application 120 @State isTouch: number = -1; 121 @State reason: string = ''; 122 @State label: string = ''; 123 @State version: string = ''; 124 @State permissionLabels: Array<ResourceStr> = []; 125 @Link @Watch("updateReason") isRefreshReason: number; 126 127 /** 128 * Grant permissions to the app 129 * @param {Number} accessTokenId 130 * @param {String} permission permission name 131 */ 132 grantUserGrantedPermission(accessTokenId: number, permission: Permissions) { 133 abilityAccessCtrl.createAtManager().grantUserGrantedPermission(accessTokenId, permission, Constants.PERMISSION_FLAG) 134 .then(() => { 135 nowGrantResult = Constants.PERMISSION_INDEX; 136 }) 137 .catch((error: BusinessError) => { 138 console.error(TAG + 'grantUserGrantedPermission failed. Cause: ' + JSON.stringify(error)); 139 }) 140 } 141 142 /** 143 * Deauthorize the app 144 * @param {Number} accessTokenId 145 * @param {String} permission permission name 146 */ 147 revokeUserGrantedPermission(accessTokenId: number, permission: Permissions) { 148 abilityAccessCtrl.createAtManager().revokeUserGrantedPermission(accessTokenId, permission, Constants.PERMISSION_FLAG) 149 .then(() => { 150 nowRevokeResult = Constants.PERMISSION_INDEX; 151 }) 152 .catch((error: BusinessError) => { 153 console.error(TAG + 'revokeUserGrantedPermission failed. Cause: ' + JSON.stringify(error)); 154 }) 155 } 156 157 /** 158 * Update reason 159 */ 160 updateReason() { 161 bundleManager.getApplicationInfo(this.applicationInfo.bundleName, bundleManager.ApplicationFlag.GET_APPLICATION_INFO_DEFAULT).then(appInfo => { 162 let bundleContext = this.context.createBundleContext(this.bundleName) 163 bundleContext.resourceManager.getStringValue(appInfo.labelId, (error, value) => { 164 if (value) { 165 this.applicationInfo.label = value; 166 GlobalContext.store('applicationInfo', this.applicationInfo); 167 this.label = value 168 } 169 }) 170 }).catch((error: BusinessError) => { 171 console.error(TAG + 'getApplicationInfo error: ' + JSON.stringify(error)); 172 }) 173 let context = this.context.createModuleContext(this.bundleName, reqPermissionInfo.moduleName); 174 context.resourceManager.getStringValue(reqPermissionInfo.reasonId).then(value => { 175 if (value !== undefined) { 176 this.reason = value.slice(Constants.START_SUBSCRIPT, Constants.END_SUBSCRIPT); 177 } 178 }) 179 } 180 181 /** 182 * Lifecycle function, executed when the page is initialized 183 */ 184 aboutToAppear() { 185 this.label = this.applicationInfo.label; 186 if (showSubpermissionsGrop.indexOf(this.currentGroup) != -1) { 187 this.permissions.forEach((permission, idx) => { 188 if (idx > 0) { 189 this.permissionLabels.push($r("app.string.and")) 190 } 191 let label = getPermissionLabel(permission) 192 this.permissionLabels.push(label); 193 }) 194 } 195 let hasReason = false; 196 bundleManager.getBundleInfo(this.bundleName, bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION).then(info => { 197 this.permissions.forEach(permission => { 198 info.reqPermissionDetails.forEach(reqPermissionDetail => { 199 if (reqPermissionDetail.name == permission) { 200 console.info("reqPermissionDetail: " + JSON.stringify(reqPermissionDetail)); 201 let context = this.context.createModuleContext(this.bundleName, reqPermissionDetail.moduleName); 202 context.resourceManager.getStringValue(reqPermissionDetail.reasonId).then(value => { 203 if (value !== undefined && !hasReason) { 204 this.reason = value.slice(Constants.START_SUBSCRIPT, Constants.END_SUBSCRIPT); 205 reqPermissionInfo = reqPermissionDetail; 206 hasReason = true; 207 } 208 }) 209 } 210 }) 211 }) 212 }) 213 bundleManager.getBundleInfo(this.bundleName, bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION).then(res => { 214 this.api = res.targetVersion; 215 this.version = res.versionName; 216 api = res.targetVersion; 217 accessTokenId = res.appInfo.accessTokenId; 218 let acManager = abilityAccessCtrl.createAtManager(); 219 let accurateStatus = acManager.verifyAccessTokenSync(res.appInfo.accessTokenId, PRECISE_LOCATION_PERMISSION); 220 this.accurateIsOn = (accurateStatus == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) ? true : false; 221 try { 222 acManager.getPermissionFlags(res.appInfo.accessTokenId, this.permissions[0]).then((data) => { 223 console.log(TAG + `getPermissionFlags success, data->${JSON.stringify(data)}`); 224 this.isRisk = (data == Constants.PERMISSION_POLICY_FIXED) ? true : false; 225 }) 226 } catch(err) { 227 console.log(TAG + 'acManager.getPermissionFlags failed. Cause: ' + JSON.stringify(err)); 228 } 229 this.mediaDocListItem.push( 230 new MediaDocObj($r('app.string.allow'), res.appInfo.accessTokenId, this.permissions, 0) 231 ); 232 this.mediaDocListItem.push( 233 new MediaDocObj($r('app.string.ban'), res.appInfo.accessTokenId, this.permissions, 1) 234 ); 235 }).catch((error: BusinessError) => { 236 console.error(TAG + 'bundle.getBundleInfo failed. Cause: ' + JSON.stringify(error)); 237 this.mediaDocListItem.push( 238 new MediaDocObj($r('app.string.allow'), 0, this.permissions, 0) 239 ); 240 this.mediaDocListItem.push( 241 new MediaDocObj($r('app.string.ban'), 0, this.permissions, 1) 242 ); 243 }) 244 // Get permission status 245 if (!this.status) { 246 this.isCheckList = [true, false]; 247 } else { 248 this.isCheckList = [false, true]; 249 } 250 } 251 252 build(){ 253 Column() { 254 Row() { 255 Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { 256 Image(this.applicationInfo.icon) 257 .width(Constants.TERTIARY_IMAGE_WIDTH) 258 .height(Constants.TERTIARY_IMAGE_HEIGHT) 259 .margin({ left: Constants.TERTIARY_IMAGE_MARGIN_LEFT, right: Constants.TERTIARY_IMAGE_MARGIN_RIGHT }) 260 Column() { 261 Row() { 262 Text(this.label) 263 .maxLines(Constants.MAXIMUM_HEADER_LINES) 264 .textOverflow({ overflow: TextOverflow.Ellipsis }) 265 .fontSize(Constants.TEXT_MIDDLE_FONT_SIZE) 266 .fontColor($r('sys.color.ohos_id_color_text_primary')) 267 .fontWeight(FontWeight.Bold) 268 .textAlign(TextAlign.Start) 269 } 270 .width(Constants.TERTIARY_HALF_WIDTH) 271 .margin({ bottom: Constants.TERTIARY_LABEL_MARGIN_BOTTOM }) 272 Row() { 273 Text($r('app.string.version')) 274 .fontSize(Constants.TEXT_SMALL_FONT_SIZE) 275 .fontColor($r('sys.color.ohos_id_color_text_secondary')) 276 .textAlign(TextAlign.Start) 277 Text(this.version) 278 .fontSize(Constants.TEXT_SMALL_FONT_SIZE) 279 .fontColor($r('sys.color.ohos_id_color_text_secondary')) 280 .textAlign(TextAlign.Start) 281 } 282 .width(Constants.TERTIARY_HALF_WIDTH) 283 } 284 }.margin({ left: Constants.TERTIARY_MARGIN_LEFT }) 285 } 286 if (this.reason || this.permissionLabels.length > 0) { 287 Row() { 288 Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { 289 Row() { 290 Text() { 291 if (this.permissionLabels.length > 0) { 292 ForEach(this.permissionLabels, (item: ResourceStr) => { 293 Span(item) 294 }) 295 Span(this.reason ? $r("app.string.comma") : $r("app.string.period")) 296 } 297 Span(this.reason) 298 } 299 .fontSize(Constants.TEXT_SMALL_FONT_SIZE) 300 .fontColor($r('sys.color.ohos_id_color_secondary')) 301 .textAlign(TextAlign.Start) 302 }.margin({ left: Constants.TERTIARY_IMAGE_MARGIN_LEFT, right: Constants.TERTIARY_IMAGE_MARGIN_RIGHT }) 303 } 304 } 305 .margin({ top: Constants.TERTIARY_ROW_MARGIN_TOP, left: Constants.DEFAULT_MARGIN_START, bottom: Constants.DEFAULT_MARGIN_BOTTOM }) 306 } 307 if (this.isRisk) { 308 Row() { 309 Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { 310 Row() { 311 Image($r('app.media.ic_public_fail')) 312 .fillColor($r('sys.color.ohos_id_color_secondary')) 313 .width(Constants.TERTIARY_RADIO_IMAGE_WIDTH) 314 .height(Constants.TERTIARY_RADIO_IMAGE_HEIGHT) 315 .margin({ right: Constants.TERTIARY_IMAGE_MARGIN_RIGHT }) 316 Text($r('app.string.risk_warning')) 317 .fontColor($r('sys.color.ohos_id_color_text_primary')) 318 .fontSize($r('sys.float.ohos_id_text_size_body1')) 319 .fontWeight(FontWeight.Regular) 320 }.margin({ left: Constants.DEFAULT_PADDING_START, right: Constants.DEFAULT_PADDING_END }) 321 } 322 }.backgroundColor($r('sys.color.ohos_id_color_click_effect')) 323 .borderRadius($r('sys.float.ohos_id_corner_radius_default_l')) 324 .padding({ top: Constants.DEFAULT_PADDING_TOP, bottom: Constants.DEFAULT_PADDING_BOTTOM }) 325 .margin({ left: Constants.DEFAULT_MARGIN_START, right: Constants.DEFAULT_MARGIN_END }) 326 } 327 Row() { 328 Text() { 329 Span(this.backTitle) 330 Span($r('app.string.access_permission')) 331 } 332 .fontSize(Constants.TEXT_SMALL_FONT_SIZE) 333 .fontColor($r('sys.color.ohos_id_color_secondary')) 334 .fontWeight(FontWeight.Medium) 335 .textAlign(TextAlign.Start) 336 .lineHeight(Constants.SUBTITLE_LINE_HEIGHT) 337 }.width(Constants.FULL_WIDTH) 338 .constraintSize({ minHeight: Constants.SUBTITLE_MIN_HEIGHT }) 339 .padding({ top: Constants.SUBTITLE_PADDING_TOP, bottom: Constants.SUBTITLE_PADDING_BOTTOM, 340 left: Constants.TERTIARY_TEXT_MARGIN_LEFT, right: Constants.TERTIARY_IMAGE_MARGIN_RIGHT}) 341 Column() { 342 List() { 343 ForEach(this.mediaDocListItem, (item: MediaDocObj) => { 344 ListItem() { 345 Column() { 346 Row() { 347 Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { 348 Row() { 349 Text(item.name) 350 .fontSize(Constants.TEXT_MIDDLE_FONT_SIZE) 351 .fontColor($r('sys.color.ohos_id_color_text_primary')) 352 .fontWeight(FontWeight.Medium) 353 .flexGrow(Constants.FLEX_GROW) 354 Radio({ value: 'Radio', group: 'radioGroup' }) 355 .checked(this.isCheckList[item.index]) 356 .touchable(false) 357 .height(Constants.SHAPE_DIA) 358 .width(Constants.SHAPE_DIA) 359 } 360 .width(Constants.FULL_WIDTH) 361 .height(Constants.LISTITEM_ROW_HEIGHT) 362 .onClick(() => { 363 let index = item.index; 364 item.permissions.forEach((permission): boolean => { 365 if (!index) { 366 if ((this.api >= Constants.API_VERSION_SUPPORT_STAGE) && (permission == PRECISE_LOCATION_PERMISSION)) { 367 return false; 368 } 369 this.grantUserGrantedPermission(item.accessTokenId, permission); 370 if (nowGrantResult != Constants.PERMISSION_INDEX) { 371 GrantResultFlag.push(-1); 372 } else { 373 GrantResultFlag.push(0); 374 } 375 } else { 376 if ((permission == PRECISE_LOCATION_PERMISSION) && (this.api >= Constants.API_VERSION_SUPPORT_STAGE)) { 377 if (this.accurateIsOn) { 378 this.revokeUserGrantedPermission(item.accessTokenId, permission); 379 this.accurateIsOn = false; 380 } 381 } else { 382 this.revokeUserGrantedPermission(item.accessTokenId, permission); 383 } 384 385 if (nowRevokeResult != Constants.PERMISSION_INDEX) { 386 RevokeResultFlag.push(-1); 387 } else { 388 RevokeResultFlag.push(0); 389 } 390 } 391 return true; 392 }) 393 if (!index) { 394 if (GrantResultFlag.indexOf(-1) <= -1) { 395 this.isCheckList = [true, false]; 396 } 397 } else { 398 if (RevokeResultFlag.indexOf(-1) <= -1) { 399 this.isCheckList = [false, true]; 400 } 401 } 402 }) 403 } 404 } 405 } 406 } 407 .padding({ left: $r('sys.float.ohos_id_card_margin_start'), right: $r('sys.float.ohos_id_card_margin_end') }) 408 .borderRadius($r("sys.float.ohos_id_corner_radius_default_l")) 409 .linearGradient((this.isTouch === item.index) ? { 410 angle: 90, 411 direction: GradientDirection.Right, 412 colors: [['#DCEAF9', 0.0], ['#FAFAFA', 1.0]] 413 } : { 414 angle: 90, 415 direction: GradientDirection.Right, 416 colors: [[$r("sys.color.ohos_id_color_list_card_bg"), 1], [$r("sys.color.ohos_id_color_list_card_bg"), 1]] 417 }) 418 .onTouch(event => { 419 if (event === undefined) { 420 return; 421 } 422 if (event.type === TouchType.Down) { 423 this.isTouch = item.index; 424 } 425 if (event.type === TouchType.Up) { 426 this.isTouch = -1; 427 } 428 }) 429 .margin({ top: Constants.TERTIARY_LISTITEM_MARGIN_TOP }) 430 }, (item: MediaDocObj) => JSON.stringify(item)) 431 } 432 .borderRadius($r('sys.float.ohos_id_corner_radius_card')) 433 .backgroundColor($r('sys.color.ohos_id_color_list_card_bg')) 434 .padding(Constants.LIST_PADDING_TOP) 435 .divider({ 436 strokeWidth: Constants.DIVIDER, 437 color: $r('sys.color.ohos_id_color_list_separator'), 438 startMargin: Constants.DEFAULT_MARGIN_START, 439 endMargin: Constants.DEFAULT_MARGIN_END 440 }) 441 442 if ((this.api >= Constants.API_VERSION_SUPPORT_STAGE) && (this.permissions.includes(PRECISE_LOCATION_PERMISSION))) { 443 Column() { 444 Row() { 445 Text($r('app.string.precise_location')) 446 .fontSize(Constants.TEXT_MIDDLE_FONT_SIZE) 447 .fontColor($r('sys.color.ohos_id_color_text_primary')) 448 .fontWeight(FontWeight.Medium) 449 .flexGrow(Constants.FLEX_GROW) 450 Toggle({ type: ToggleType.Switch, isOn: this.accurateIsOn }) 451 .selectedColor($r('sys.color.ohos_id_color_toolbar_icon_actived')) 452 .switchPointColor($r('sys.color.ohos_id_color_foreground_contrary')) 453 .onChange((isOn: boolean) => { 454 let acManager = abilityAccessCtrl.createAtManager() 455 if (isOn) { 456 acManager.grantUserGrantedPermission(accessTokenId, PRECISE_LOCATION_PERMISSION, Constants.PERMISSION_FLAG) 457 .then(() => { this.accurateIsOn = true }) 458 } else { 459 acManager.revokeUserGrantedPermission(accessTokenId, PRECISE_LOCATION_PERMISSION, Constants.PERMISSION_FLAG) 460 .then(() => { this.accurateIsOn = false }) 461 } 462 }) 463 .padding({ right: 0 }) 464 .enabled(this.isCheckList[0]) 465 }.width(Constants.FULL_WIDTH) 466 .height(Constants.LISTITEM_ROW_HEIGHT) 467 }.margin({ top: Constants.LOCATION_MARGIN_TOP, bottom: Constants.LOCATION_MARGIN_BOTTOM }) 468 .padding({ 469 left: Constants.DEFAULT_PADDING_START, 470 right: Constants.DEFAULT_PADDING_END, 471 top: Constants.TERTIARY_LIST_PADDING_TOP, 472 bottom: Constants.TERTIARY_LIST_PADDING_BOTTOM 473 }) 474 .borderRadius($r('sys.float.ohos_id_corner_radius_card')) 475 .backgroundColor($r('sys.color.ohos_id_color_list_card_bg')) 476 477 Row() { 478 Text($r('app.string.get_the_exact_position')) 479 .fontSize(Constants.TEXT_SMALL_FONT_SIZE) 480 .fontColor($r('sys.color.ohos_id_color_text_secondary')) 481 .lineHeight(Constants.TEXT_SMALL_LINE_HEIGHT) 482 }.width(Constants.FULL_WIDTH) 483 .padding({ 484 left: Constants.DEFAULT_PADDING_START, 485 right: Constants.DEFAULT_PADDING_END, 486 }) 487 } 488 } 489 .padding({ 490 left: Constants.LIST_PADDING_LEFT, 491 right: Constants.LIST_PADDING_LEFT 492 }) 493 .width(Constants.FULL_WIDTH) 494 .height(Constants.FULL_HEIGHT) 495 .enabled(!this.isRisk) 496 .opacity(this.isRisk ? $r('sys.float.ohos_id_alpha_disabled') : 1) 497 } 498 .width(Constants.FULL_WIDTH) 499 } 500} 501