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 { backBar } from "../common/components/backBar"; 17import { alphabetIndexerComponent } from "../common/components/alphabeticalIndex"; 18import { textInput } from "../common/components/search"; 19import router from '@ohos.router'; 20import bundle from "@ohos.bundle"; 21import abilityAccessCtrl from '@ohos.abilityAccessCtrl'; 22import { getAppLabel, getAppIcon, verifyAccessToken } from "../common/utils/utils"; 23import { makePy } from "../common/utils/utils"; 24import { authorizeDialog } from "../common/components/dialog"; 25import Constants from '../common/utils/constant'; 26 27var TAG = 'PermissionManager_MainAbility:' 28 29@Extend(Image) function customizeImage(width: number, height: number) { 30 .objectFit(ImageFit.Contain) 31 .width(width) 32 .height(height) 33} 34 35let routerData: any = router.getParams()['routerData']; // Routing jump data 36let backTitle = router.getParams()['backTitle']; // return title name 37class ApplicationObj { 38 labelId: string 39 iconId: string 40 index: number 41 accessTokenId: number 42 permission: string 43 alphabeticalIndex: string 44 constructor( 45 labelId: string, 46 iconId: string, 47 index: number, 48 accessTokenId: number, 49 permission: string, 50 alphabeticalIndex: string) { 51 this.labelId = labelId 52 this.iconId = iconId 53 this.index = index 54 this.accessTokenId = accessTokenId 55 this.permission = permission 56 this.alphabeticalIndex = alphabeticalIndex 57 } 58} // application information 59 60@Entry 61@Component 62struct locationInfoPage { 63 build() { 64 GridContainer({ gutter: Constants.GUTTER, margin: Constants.GRID_MARGIN }) { 65 Row() { 66 Row() 67 .useSizeType({ 68 xs: { span: Constants.LEFT_XS_SPAN, offset: Constants.LEFT_XS_OFFSET }, 69 sm: { span: Constants.LEFT_SM_SPAN, offset: Constants.LEFT_SM_OFFSET }, 70 md: { span: Constants.LEFT_MD_SPAN, offset: Constants.LEFT_MD_OFFSET }, 71 lg: { span: Constants.LEFT_LG_SPAN, offset: Constants.LEFT_LG_OFFSET } 72 }) 73 .height(Constants.FULL_HEIGHT) 74 Row() { 75 Column() { 76 Row() { 77 backBar({ title: JSON.stringify(backTitle), recordable: false }) 78 } 79 Row() { 80 Column() { 81 applicationItem() 82 83 }.width(Constants.FULL_WIDTH) 84 } 85 .layoutWeight(Constants.LAYOUT_WEIGHT) 86 } 87 } 88 .useSizeType({ 89 xs: { span: Constants.MIDDLE_XS_SPAN, offset: Constants.MIDDLE_XS_OFFSET }, 90 sm: { span: Constants.MIDDLE_SM_SPAN, offset: Constants.MIDDLE_SM_OFFSET }, 91 md: { span: Constants.MIDDLE_MD_SPAN, offset: Constants.MIDDLE_MD_OFFSET }, 92 lg: { span: Constants.MIDDLE_LG_SPAN, offset: Constants.MIDDLE_LG_OFFSET } 93 }) 94 .height(Constants.FULL_HEIGHT) 95 Row() 96 .useSizeType({ 97 xs: { span: Constants.RIGHT_XS_SPAN, offset: Constants.RIGHT_XS_OFFSET }, 98 sm: { span: Constants.RIGHT_SM_SPAN, offset: Constants.RIGHT_SM_OFFSET }, 99 md: { span: Constants.RIGHT_MD_SPAN, offset: Constants.RIGHT_MD_OFFSET }, 100 lg: { span: Constants.RIGHT_LG_SPAN, offset: Constants.RIGHT_LG_OFFSET } 101 }) 102 .height(Constants.FULL_HEIGHT) 103 } 104 .height(Constants.FULL_HEIGHT) 105 .width(Constants.FULL_WIDTH) 106 .backgroundColor($r("sys.color.ohos_id_color_sub_background")) 107 } 108 } 109} 110 111@Component 112struct applicationItem { 113 @State permissionNum: number = Constants.PERMISSION_NUM; // permission num 114 @State toggleIsOn: object = {}; // toggle switch state array 115 @State applicationList: ApplicationObj[] = []; // application info array 116 @State searchResult: boolean = true; // search results 117 @State placeholder: string = '' 118 119 authorizeDialogController: CustomDialogController = new CustomDialogController({ 120 builder: authorizeDialog({ }), 121 autoCancel: true, 122 alignment: DialogAlignment.Center 123 }); 124 125 @Builder ListItemLayout(item, index) { 126 ListItem() { 127 Row() { 128 Column() { 129 Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { 130 Row() { 131 Image(item.iconId) 132 .customizeImage(Constants.AUTHORITY_IMAGE_WIDTH, Constants.AUTHORITY_IMAGE_HEIGHT) 133 .margin({ right: Constants.AUTHORITY_IMAGE_MARGIN_RIGHT }) 134 Text(item.labelId) 135 .fontSize(Constants.TEXT_MIDDLE_FONT_SIZE) 136 .fontWeight(FontWeight.Medium) 137 .fontColor($r('app.color.label_color')) 138 .flexGrow(Constants.FLEX_GROW) 139 Toggle({ type: ToggleType.Switch, isOn: this.toggleIsOn[item.index] }) 140 .selectedColor($r('app.color.button_color')) 141 .padding({ right: 0 }) 142 .width(Constants.AUTHORITY_TOGGLE_WIDTH) 143 .height(Constants.AUTHORITY_TOGGLE_HEIGHT) 144 .onChange((isOn: boolean) => { 145 if (item.accessTokenId === '' || item.permission === '') { 146 return; 147 } 148 if (isOn) { 149 this.grantUserGrantedPermission(item.accessTokenId, item.permission, item.index); 150 } else { 151 this.revokeUserGrantedPermission(item.accessTokenId, item.permission, item.index); 152 } 153 }) 154 } 155 .width(Constants.FULL_WIDTH) 156 .height(Constants.AUTHORITY_ROW_HEIGHT) 157 .constraintSize({ minHeight: Constants.AUTHORITY_CONSTRAINTSIZE_MINHEIGHT }) 158 } 159 if (!index) { 160 Row() { 161 Flex() { 162 Column().width(Constants.APPLICATION_TEXT_DECORATION_MARGIN_LEFT) 163 Column() 164 .backgroundColor($r('app.color.text_decoration_color')) 165 .height(Constants.TEXT_DECORATION_HEIGHT) 166 .flexGrow(Constants.FLEX_GROW) 167 } 168 } 169 } 170 }.onClick(() => { 171 }) 172 } 173 }.padding({ left: Constants.DEFAULT_PADDING_START, right: Constants.DEFAULT_PADDING_END }) 174 } 175 176 /** 177 * Grant permissions to the app 178 * @param {Number} accessTokenId 179 * @param {String} permission permission name 180 * @param {Number} index Array index to modify permission status 181 */ 182 grantUserGrantedPermission(accessTokenId, permission, index) { 183 abilityAccessCtrl.createAtManager().grantUserGrantedPermission( 184 accessTokenId, permission, Constants.PERMISSION_FLAG).then(() => { 185 // result: 0 Authorization succeeded; result: -1 Authorization failed 186 this.toggleIsOn[index] = true; 187 let num = Constants.PERMISSION_NUM; 188 for(let key in this.toggleIsOn){ 189 if(this.toggleIsOn[key]){ 190 num++; 191 } 192 } 193 this.permissionNum = num; 194 }).catch(error => { 195 this.authorizeDialogController.open(); 196 this.toggleIsOn[index] = false; 197 setTimeout(()=> { 198 this.authorizeDialogController.close(); 199 }, Constants.DELAY_TIME) 200 console.error(TAG + 'abilityAccessCtrl.createAtManager.grantUserGrantedPermission failed. Cause: ' + JSON.stringify(error)); 201 }) 202 } 203 204 /** 205 * Deauthorize the app 206 * @param {Number} accessTokenId 207 * @param {String} permission permission name 208 * @param {Number} index Array index to modify permission status 209 */ 210 revokeUserGrantedPermission(accessTokenId, permission, index) { 211 abilityAccessCtrl.createAtManager().revokeUserGrantedPermission( 212 accessTokenId, permission, Constants.PERMISSION_FLAG).then(() => { 213 // result: 0 successfully cancel the authorization; result: -1 cancel the authorization failed 214 this.toggleIsOn[index] = false; 215 let num = Constants.PERMISSION_NUM; 216 for(let key in this.toggleIsOn){ 217 if(this.toggleIsOn[key]){ 218 num++; 219 } 220 } 221 this.permissionNum = num; 222 }).catch(() => { 223 this.authorizeDialogController.open(); 224 this.toggleIsOn[index] = true; 225 setTimeout(()=> { 226 this.authorizeDialogController.close(); 227 }, Constants.DELAY_TIME) 228 }) 229 } 230 231 /** 232 * Lifecycle function, executed when the page is initialized 233 */ 234 aboutToAppear() { 235 let bundleNames = routerData.length > 0 ? routerData[0].bundleNames : routerData; 236 237 globalThis.context.resourceManager.getString($r("app.string.textInput_placeholder").id).then(val => { 238 this.placeholder = val 239 }) 240 241 // initial then fill values when sync return which may cause sync panic 242 for (let i = 0; i < bundleNames.length; i++) { 243 this.applicationList.push(new ApplicationObj('', '', i, 0, '', '')); 244 } 245 246 for (let i = 0; i < bundleNames.length; i++) { 247 // Get BundleInfo based on bundle name 248 bundle.getBundleInfo(bundleNames[i], Constants.PARMETER_BUNDLE_FLAG).then(res => { 249 Promise.all([getAppLabel(res.appInfo.labelId, res.name), 250 getAppIcon(res.appInfo.iconId, res.name), 251 verifyAccessToken(res.appInfo.accessTokenId, routerData[0].permission)]) 252 .then((values) => { 253 this.applicationList[i] = ( 254 new ApplicationObj( 255 String(values[0]), 256 String(values[1]), 257 i, 258 res.appInfo.accessTokenId, 259 routerData[0].permission, 260 makePy(values[0])[0].slice(0, 1)) // Get the first letter in the returned initials array 261 ); 262 // 0: have permission; -1: no permission 263 if (values[2] === Constants.PERMISSION_INDEX) { 264 this.toggleIsOn[i] = true; 265 this.permissionNum++; 266 } else { 267 this.toggleIsOn[i] = false; 268 } 269 }); 270 }).catch(error => { 271 console.log(TAG + bundleNames[i] + "getBundleInfo failed, cause: " + JSON.stringify(error)); 272 }) 273 } 274 } 275 276 build() { 277 Column() { 278 Row() { 279 textInput({ 280 placeholder: this.placeholder, 281 applicationItem: $applicationList, 282 searchResult: $searchResult 283 }) 284 }.padding({ 285 left: Constants.AUTHORITY_TEXTINPUT_PADDING_LEFT, 286 right: Constants.AUTHORITY_TEXTINPUT_PADDING_RIGHT 287 }) 288 Flex({ alignItems:ItemAlign.Start, justifyContent: FlexAlign.Start }) { 289 Column() { 290 Flex({ justifyContent: FlexAlign.Start }) { 291 Text(String(this.permissionNum)) 292 .fontSize(Constants.TEXT_SMAL_FONT_SIZE) 293 .fontColor($r('app.color.secondary_font_color')) 294 .margin({ top: Constants.AUTHORITY_TEXT_MARGIN_TOP, left: Constants.AUTHORITY_TEXT_MARGIN_LEFT }) 295 Text($r('app.string.number_of_authorized_applications')) 296 .fontSize(Constants.TEXT_SMAL_FONT_SIZE) 297 .fontColor($r('app.color.secondary_font_color')) 298 .margin({ top: Constants.AUTHORITY_TEXT_MARGIN_TOP, left: Constants.AUTHORITY_TEXT_MARGIN_LEFT }) 299 }.padding({ left: Constants.DEFAULT_PADDING_START, right: Constants.DEFAULT_PADDING_END }) 300 .margin({ bottom: Constants.AUTHORITY_ROW_MARGIN_BOTTOM }) 301 Scroll() { 302 Row() { 303 Column() { 304 if (!this.applicationList.length) { 305 if (this.searchResult) { 306 Row() {} 307 } else { 308 Row() { 309 Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { 310 Image($r('app.media.searchnoresult')) 311 .customizeImage(Constants.SEARCHNORESULT_IMAGE_WIDTH, Constants.SEARCHNORESULT_IMAGE_HEIGHT) 312 } 313 } 314 } 315 } else { 316 Row() { 317 List() { 318 ForEach(this.applicationList.slice(Constants.SLICE_START, this.applicationList.length - 1), 319 (item) => { 320 this.ListItemLayout(item, Constants.SLICE_START_INDEX) 321 }, item => item.toString()) 322 ForEach(this.applicationList.slice(Constants.SLICE_END), (item) => { 323 this.ListItemLayout(item, Constants.SLICE_END_INDEX) 324 }, item => item.toString()) 325 } 326 .backgroundColor($r('app.color.default_background_color')) 327 .borderRadius(Constants.BORDER_RADIUS) 328 .padding({ top: Constants.LIST_PADDING_TOP, bottom: Constants.LIST_PADDING_BOTTOM }) 329 } 330 } 331 }.width(Constants.FULL_WIDTH) 332 .margin({ bottom: Constants.AUTHORITY_LIST_MARGIN_BOTTOM }) 333 } 334 }.scrollBar(BarState.Off) 335 .borderRadius(Constants.BORDER_RADIUS) 336 }.padding({ left: Constants.AUTHORITY_LISTITEM_PADDING_LEFT }) 337 Column() { 338 alphabetIndexerComponent({ applicationItem: $applicationList }) 339 }.width(Constants.AUTHORITY_ALPHABETINDEX_WIDTH) 340 .padding({ top: Constants.AUTHORITY_ALPHABETINDEX_PADDING_TOP }) 341 }.flexGrow(Constants.FLEX_GROW) 342 }.height(Constants.FULL_HEIGHT) 343 } 344} 345