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 { alphabetIndexerComponent } from "../common/components/alphabeticalIndex"; 18import { textInput } from "../common/components/search"; 19import router from '@ohos.router'; 20import bundleManager from "@ohos.bundle.bundleManager"; 21import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl'; 22import { BusinessError } from '@ohos.base'; 23import audio from '@ohos.multimedia.audio' 24import camera from '@ohos.multimedia.camera' 25import common from '@ohos.app.ability.common'; 26import { verifyAccessToken, indexValue, sortByName } from "../common/utils/utils"; 27import { ApplicationObj, GroupInfo, routerParams_1, permissionApplications, appInfo } from "../common/utils/typedef"; 28import { GlobalContext } from "../common/utils/globalContext"; 29import { globalDialog } from "../common/components/dialog"; 30import Constants from '../common/utils/constant'; 31import { polymorphismGroup, globalGroup, groups } from "../common/model/permissionGroup"; 32 33const TAG = 'PermissionManager_MainAbility:'; 34const FUZZY_LOCATION_PERMISSION = 'ohos.permission.APPROXIMATELY_LOCATION'; 35const PRECISE_LOCATION_PERMISSION = 'ohos.permission.LOCATION'; 36let globalIsOn: boolean = (router.getParams() as routerParams_1).globalIsOn; // return title name 37 38@Extend(Image) function customizeImage(width: number, height: number) { 39 .objectFit(ImageFit.Contain) 40 .width(width) 41 .height(height) 42} 43 44@Entry 45@Component 46struct locationInfoPage { 47 private backTitle: ResourceStr = (router.getParams() as routerParams_1).backTitle; 48 private list: permissionApplications[] = (router.getParams() as routerParams_1).list; 49 @State currentGroup: string = GlobalContext.load('currentPermissionGroup'); 50 @State polymorphismIsOn: Array<boolean> = []; 51 52 build() { 53 GridRow({ gutter: Constants.GUTTER, columns: { 54 xs: Constants.XS_COLUMNS, sm: Constants.SM_COLUMNS, md: Constants.MD_COLUMNS, lg: Constants.LG_COLUMNS } }) { 55 GridCol({ span: { xs: Constants.XS_SPAN, sm: Constants.SM_SPAN, md: Constants.MD_SPAN, lg: Constants.LG_SPAN }, 56 offset: { xs: Constants.XS_OFFSET, sm: Constants.SM_OFFSET, md: Constants.MD_OFFSET, lg: Constants.LG_OFFSET } }) { 57 Row() { 58 Column() { 59 Row() { 60 backBar({ title: JSON.stringify(this.backTitle), recordable: false }) 61 } 62 Row() { 63 Column() { 64 applicationItem({ polymorphismIsOn: $polymorphismIsOn }) 65 66 }.width(Constants.FULL_WIDTH) 67 } 68 .layoutWeight(Constants.LAYOUT_WEIGHT) 69 } 70 } 71 .height(Constants.FULL_HEIGHT) 72 .width(Constants.FULL_WIDTH) 73 .backgroundColor($r("sys.color.ohos_id_color_sub_background")) 74 } 75 }.backgroundColor($r("sys.color.ohos_id_color_sub_background")) 76 } 77 78 onPageShow() { 79 console.log(TAG + "onPageShow"); 80 if (polymorphismGroup.indexOf(this.currentGroup) !== -1) { 81 let bundleNames: string[] = []; 82 this.list.forEach(permissionmanager => { 83 permissionmanager.bundleNames.forEach(bundleName => { 84 if (bundleNames.indexOf(bundleName) == -1) { 85 bundleNames.push(bundleName); 86 } 87 }) 88 }) 89 90 bundleNames.forEach((bundleName, index) => { 91 bundleManager.getBundleInfo(bundleName, bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION | bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION).then(res => { 92 // 0: have permission; -1: no permission 93 this.polymorphismIsOn[index] = true; 94 let reqPermissions: Array<string> = []; 95 res.reqPermissionDetails.forEach(item => { 96 reqPermissions.push(item.name); 97 }) 98 for (let j = 0; j < this.list.length; j++) { 99 if ((this.list[j].permission == PRECISE_LOCATION_PERMISSION) && (res.targetVersion >= Constants.API_VERSION_SUPPORT_STAGE)) { 100 continue; 101 } 102 if ((this.list[j].permission == FUZZY_LOCATION_PERMISSION) && (res.targetVersion < Constants.API_VERSION_SUPPORT_STAGE)) { 103 continue; 104 } 105 if (reqPermissions.indexOf(this.list[j].permission) == -1) { 106 continue; 107 } 108 verifyAccessToken(res.appInfo.accessTokenId, this.list[j].permission).then((access) => { 109 if (Number(access) === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED) { 110 this.polymorphismIsOn[index] = false; 111 } 112 }); 113 } 114 }).catch((error: BusinessError) => { 115 console.log(TAG + bundleName + "onPageShow getBundleInfo failed, cause: " + JSON.stringify(error)); 116 }) 117 }) 118 } 119 } 120} 121 122@Component 123struct applicationItem { 124 private context = getContext(this) as common.UIAbilityContext; 125 private backTitle: ResourceStr = (router.getParams() as routerParams_1).backTitle; 126 private list: permissionApplications[] = (router.getParams() as routerParams_1).list; 127 @State permissionNum: number = Constants.PERMISSION_NUM; // permission num 128 @State toggleIsOn: boolean[] = []; // toggle switch state array 129 @State isRisk: boolean[] = []; 130 @State applicationList: ApplicationObj[] = []; // application info array 131 @State searchResult: boolean = true; // search results 132 @Link polymorphismIsOn: Array<boolean>; 133 @State globalIsOn: boolean = true; 134 @State selectedIndex: number = 0; 135 @State isTouch: string = ''; 136 @State groupInfo: GroupInfo = new GroupInfo('', '', '', '', [], '', [], false); 137 @State currentGroup: string = GlobalContext.load('currentPermissionGroup'); 138 @State isMuteSupported: boolean = GlobalContext.load('isMuteSupported'); 139 @State allBundleInfo: appInfo[] = GlobalContext.load('allBundleInfo'); 140 scroller: Scroller = new Scroller(); 141 142 privacyDialogController: CustomDialogController = new CustomDialogController({ 143 builder: globalDialog({ globalIsOn: $globalIsOn }), 144 autoCancel: false, 145 alignment: DialogAlignment.Center, 146 customStyle: true 147 }) 148 149 @Builder ListItemLayout(item: ApplicationObj) { 150 ListItem() { 151 Row() { 152 Column() { 153 Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { 154 Row() { 155 Image(item.icon) 156 .customizeImage(Constants.AUTHORITY_IMAGE_WIDTH, Constants.AUTHORITY_IMAGE_HEIGHT) 157 .margin({ right: Constants.AUTHORITY_IMAGE_MARGIN_RIGHT }) 158 Column() { 159 Text(item.label) 160 .width(Constants.MAXIMUM_HEADER_WIDTH) 161 .maxLines(Constants.MAXIMUM_HEADER_LINES) 162 .textOverflow({ overflow: TextOverflow.Ellipsis }) 163 .fontSize(Constants.TEXT_MIDDLE_FONT_SIZE) 164 .fontWeight(FontWeight.Medium) 165 .fontColor($r('sys.color.ohos_id_color_text_primary')) 166 if (this.isRisk[item.index]) { 167 Text($r('app.string.risk_warning')) 168 .fontSize(Constants.TEXT_SMALL_FONT_SIZE) 169 .fontColor($r('sys.color.ohos_id_color_text_secondary')) 170 } 171 }.flexGrow(Constants.FLEX_GROW) 172 .alignItems(HorizontalAlign.Start) 173 if (polymorphismGroup.indexOf(this.currentGroup) == -1) { 174 Toggle({ type: ToggleType.Switch, isOn: this.toggleIsOn[item.index] }) 175 .selectedColor($r('sys.color.ohos_id_color_toolbar_icon_actived')) 176 .switchPointColor($r('sys.color.ohos_id_color_foreground_contrary')) 177 .padding({ right: 0 }) 178 .width(Constants.AUTHORITY_TOGGLE_WIDTH) 179 .height(Constants.AUTHORITY_TOGGLE_HEIGHT) 180 .onChange((isOn: boolean) => { 181 if (item.permission === undefined) { 182 return; 183 } 184 let _this = this; 185 if (isOn) { 186 let promises = this.list.map(it => new Promise<number>((resolve) => { 187 _this.grantUserGrantedPermission(item.accessTokenId, it.permission, resolve); 188 })); 189 Promise.all(promises).then(() => { 190 _this.toggleIsOn[item.index] = true; 191 let num = _this.toggleIsOn.filter(item => item === true).length; 192 _this.permissionNum = num; 193 }); 194 } else { 195 let promises = this.list.map(it => new Promise<number>((resolve) => { 196 _this.revokeUserGrantedPermission(item.accessTokenId, it.permission, resolve); 197 })); 198 Promise.all(promises).then(() => { 199 _this.toggleIsOn[item.index] = false; 200 let num = _this.toggleIsOn.filter(item => item === true).length; 201 _this.permissionNum = num; 202 }); 203 } 204 }) 205 } else { 206 Text(this.polymorphismIsOn[item.index] ? $r('app.string.allow') : $r('app.string.ban')) 207 .fontSize(Constants.TEXT_SMALL_FONT_SIZE) 208 .fontColor($r('sys.color.ohos_id_color_text_secondary')) 209 .margin({ right: Constants.AUTHORITY_IMAGE_MARGIN_RIGHT }) 210 Image($r('app.media.ic_public_arrow_right')) 211 .fillColor($r('sys.color.ohos_id_color_tertiary')) 212 .customizeImage(Constants.IMAGE_WIDTH, Constants.IMAGE_HEIGHT) 213 } 214 } 215 .width(Constants.FULL_WIDTH) 216 .height(Constants.AUTHORITY_ROW_HEIGHT) 217 .constraintSize({ minHeight: Constants.AUTHORITY_CONSTRAINTSIZE_MINHEIGHT }) 218 } 219 }.onClick(() => { 220 if (polymorphismGroup.indexOf(this.currentGroup) !== -1) { 221 let permissions: string[] = []; 222 this.list.forEach(item => { 223 permissions.push(item.permission); 224 }) 225 this.allBundleInfo.forEach(bundleInfo => { 226 if (bundleInfo.bundleName === item.bundleName) { 227 GlobalContext.store('applicationInfo', bundleInfo); 228 } 229 }) 230 router.pushUrl({ 231 url: 'pages/application-tertiary', 232 params: { 233 bundleName: item.bundleName, 234 backTitle: this.backTitle, 235 permission: permissions, 236 status: this.polymorphismIsOn[item.index] ? Constants.RADIO_ALLOW_INDEX : Constants.RADIO_BAN_INDEX 237 } 238 }); 239 } 240 }) 241 } 242 }.padding({ left: $r('sys.float.ohos_id_card_margin_start'), right: $r('sys.float.ohos_id_card_margin_end') }) 243 .enabled(!this.isRisk[item.index]) 244 .opacity(this.isRisk[item.index] ? $r('sys.float.ohos_id_alpha_disabled') : 1) 245 .borderRadius($r("sys.float.ohos_id_corner_radius_default_l")) 246 .linearGradient((this.isTouch === item.bundleName) ? { 247 angle: 90, 248 direction: GradientDirection.Right, 249 colors: [['#DCEAF9', 0.0], ['#FAFAFA', 1.0]] 250 } : { 251 angle: 90, 252 direction: GradientDirection.Right, 253 colors: [[$r("sys.color.ohos_id_color_list_card_bg"), 1], [$r("sys.color.ohos_id_color_list_card_bg"), 1]] 254 }) 255 .onTouch(event => { 256 if (event === undefined) { 257 return; 258 } 259 if (event.type === TouchType.Down && polymorphismGroup.indexOf(this.currentGroup) !== -1) { 260 this.isTouch = item.bundleName ? item.bundleName : ''; 261 } 262 if (event.type === TouchType.Up) { 263 this.isTouch = ''; 264 } 265 }) 266 } 267 268 /** 269 * Take the total number of access applications 270 */ 271 getGrantApplicationNumber() { 272 if (polymorphismGroup.indexOf(this.currentGroup) !== -1) { 273 let sum = this.polymorphismIsOn.filter(item => item == true); 274 return sum.length; 275 } else { 276 return this.permissionNum; 277 } 278 } 279 280 /** 281 * Grant permissions to the app 282 * @param {Number} accessTokenId 283 * @param {String} permission permission name 284 * @param {Number} index Array index to modify permission status 285 */ 286 grantUserGrantedPermission(accessTokenId: number, permission: Permissions, resolve: (value: number) => void) { 287 abilityAccessCtrl.createAtManager().grantUserGrantedPermission(accessTokenId, permission, Constants.PERMISSION_FLAG).then(() => { 288 resolve(0); 289 }).catch((error: BusinessError) => { 290 resolve(-1); 291 console.error(TAG + 'abilityAccessCtrl.createAtManager.grantUserGrantedPermission failed. Cause: ' + JSON.stringify(error)); 292 }) 293 } 294 295 /** 296 * Deauthorize the app 297 * @param {Number} accessTokenId 298 * @param {String} permission permission name 299 * @param {Number} index Array index to modify permission status 300 */ 301 revokeUserGrantedPermission(accessTokenId: number, permission: Permissions, resolve: (value: number) => void) { 302 abilityAccessCtrl.createAtManager().revokeUserGrantedPermission(accessTokenId, permission, Constants.PERMISSION_FLAG).then(() => { 303 resolve(0); 304 }).catch((error: BusinessError) => { 305 resolve(-1); 306 console.error(TAG + 'abilityAccessCtrl.createAtManager.revokeUserGrantedPermission failed. Cause: ' + JSON.stringify(error)); 307 }) 308 } 309 310 /** 311 * Lifecycle function, executed when the page is initialized 312 */ 313 aboutToAppear() { 314 let bundleNames: string[] = []; 315 this.applicationList = []; 316 this.list.forEach(permissionmanager => { 317 permissionmanager.bundleNames.forEach(bundleName => { 318 if (bundleNames.indexOf(bundleName) == -1) { 319 bundleNames.push(bundleName); 320 } 321 }) 322 }) 323 groups.forEach(group => { 324 if (group.name === this.currentGroup) { 325 this.groupInfo = group; 326 } 327 }) 328 329 let atManager = abilityAccessCtrl.createAtManager(); 330 for (let i = 0; i < bundleNames.length; i++) { 331 // Get BundleInfo based on bundle name 332 this.allBundleInfo.forEach(bundleInfo => { 333 if (bundleInfo.bundleName === bundleNames[i]) { 334 this.applicationList.push( 335 new ApplicationObj( 336 bundleInfo.label, 337 bundleInfo.icon, 338 i, 339 bundleInfo.tokenId, 340 this.list[0].permission, 341 bundleInfo.zhTag, 342 bundleInfo.indexTag, 343 bundleInfo.language, 344 bundleInfo.bundleName) // Get the first letter in the returned initials array 345 ); 346 this.isRisk[i] = false; 347 try { 348 atManager.getPermissionFlags(bundleInfo.tokenId, this.list[0].permission).then(data => { 349 if (data == Constants.PERMISSION_POLICY_FIXED) { 350 this.isRisk[i] = true; 351 } 352 }) 353 } 354 catch(err) { 355 console.log(TAG + 'getPermissionFlags error: ' + JSON.stringify(err)); 356 } 357 // 0: have permission; -1: no permission 358 let boole = true; 359 this.permissionNum++; 360 for (let j = 0; j < this.list.length; j++) { 361 if (bundleInfo.permissions.indexOf(this.list[j].permission) == -1) { 362 continue; 363 } 364 verifyAccessToken(bundleInfo.tokenId, this.list[j].permission).then((access) => { 365 if (Number(access) === Constants.PERMISSION_INDEX) { 366 if (boole) { 367 this.toggleIsOn[i] = true; 368 } 369 } else { 370 if (boole) { 371 this.permissionNum--; 372 } 373 boole = false; 374 this.toggleIsOn[i] = false; 375 } 376 }); 377 } 378 } 379 }) 380 } 381 if (globalGroup.indexOf(this.currentGroup) !== -1) { 382 this.globalIsOn = globalIsOn; 383 if (this.currentGroup == "CAMERA") { 384 let cameraManager = camera.getCameraManager(GlobalContext.load('context')); 385 cameraManager.on('cameraMute', (err, curMuted) => { 386 console.log(TAG + 'curMuted: ' + JSON.stringify(curMuted) + ' err: ' + JSON.stringify(err)); 387 this.globalIsOn = !curMuted; 388 }) 389 } else { 390 let audioManager = audio.getAudioManager(); 391 let audioVolumeManager = audioManager.getVolumeManager(); 392 let groupid = audio.DEFAULT_VOLUME_GROUP_ID; 393 audioVolumeManager.getVolumeGroupManager(groupid).then(audioVolumeGroupManager => { 394 audioVolumeGroupManager.on('micStateChange', micStateChange => { 395 this.globalIsOn = !micStateChange.mute; 396 }) 397 }) 398 } 399 } 400 } 401 402 build() { 403 Column() { 404 Row() { 405 textInput({ 406 applicationItem: $applicationList, 407 searchResult: $searchResult 408 }) 409 }.padding({ 410 left: Constants.AUTHORITY_TEXTINPUT_PADDING_LEFT, 411 right: Constants.AUTHORITY_TEXTINPUT_PADDING_RIGHT 412 }) 413 Flex({ alignItems:ItemAlign.Start, justifyContent: FlexAlign.Start }) { 414 Column() { 415 if (globalGroup.indexOf(this.currentGroup) !== -1 && this.isMuteSupported === true) { 416 Row() { 417 Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { 418 Text(this.currentGroup == "CAMERA" ? $r('app.string.camera') : $r('app.string.microphone')) 419 .fontSize(Constants.TEXT_MIDDLE_FONT_SIZE).fontColor($r('sys.color.ohos_id_color_text_primary')) 420 .fontWeight(FontWeight.Medium) 421 Row() { 422 Toggle({ type: ToggleType.Switch, isOn: this.globalIsOn }) 423 .selectedColor($r('sys.color.ohos_id_color_toolbar_icon_actived')) 424 .switchPointColor($r('sys.color.ohos_id_color_foreground_contrary')) 425 .padding({ right: 0 }) 426 .onChange((isOn: boolean) => { 427 if (isOn) { 428 if (this.currentGroup == "CAMERA") { 429 let cameraManager = camera.getCameraManager(GlobalContext.load('context')); 430 cameraManager.muteCamera(false); 431 } else { 432 let audioManager = audio.getAudioManager(); 433 let audioVolumeManager = audioManager.getVolumeManager(); 434 let groupid = audio.DEFAULT_VOLUME_GROUP_ID; 435 audioVolumeManager.getVolumeGroupManager(groupid).then(audioVolumeGroupManager => { 436 audioVolumeGroupManager.setMicrophoneMute(false) 437 }) 438 } 439 } 440 }) 441 Row().onClick(() => { this.privacyDialogController.open() }) 442 .width(Constants.DEFAULT_SLIDER_WIDTH).height(Constants.DEFAULT_SLIDER_HEIGHT) 443 .position({ x: this.globalIsOn ? 0 : Constants.OFFSET, y: 0 }) 444 }.clip(true) 445 }.height(Constants.LISTITEM_ROW_HEIGHT) 446 .padding({ left: Constants.DEFAULT_PADDING_START, right: Constants.DEFAULT_PADDING_END }) 447 }.padding({ top: Constants.LIST_PADDING_TOP, bottom: Constants.LIST_PADDING_BOTTOM }) 448 .backgroundColor($r('sys.color.ohos_id_color_list_card_bg')) 449 .borderRadius($r('sys.float.ohos_id_corner_radius_card')) 450 .margin({ top: Constants.TERTIARY_ROW_MARGIN_TOP }) 451 } 452 Flex({ justifyContent: FlexAlign.Start }) { 453 if (this.globalIsOn) { 454 if (this.getGrantApplicationNumber() > 0) { 455 Text() { 456 Span(this.groupInfo.enable_description_start ? this.groupInfo.enable_description_start : '') 457 Span(String(this.getGrantApplicationNumber())) 458 Span(this.groupInfo.enable_description_end ? this.groupInfo.enable_description_end : '') 459 } 460 .fontSize(Constants.TEXT_SMALL_FONT_SIZE) 461 .fontColor($r('sys.color.ohos_id_color_text_secondary')) 462 .margin({ top: Constants.AUTHORITY_TEXT_MARGIN_TOP }) 463 } else { 464 Text(this.groupInfo.forbidden_description) 465 .fontSize(Constants.TEXT_SMALL_FONT_SIZE) 466 .fontColor($r('sys.color.ohos_id_color_text_secondary')) 467 .margin({ top: Constants.AUTHORITY_TEXT_MARGIN_TOP }) 468 } 469 } else { 470 Text(this.currentGroup == "CAMERA" ? $r('app.string.camera_is_off') : $r('app.string.microphone_is_off')) 471 .fontSize(Constants.TEXT_SMALL_FONT_SIZE) 472 .fontColor($r('sys.color.ohos_id_color_text_secondary')) 473 .margin({ top: Constants.AUTHORITY_TEXT_MARGIN_TOP }) 474 } 475 }.padding({ left: Constants.DEFAULT_PADDING_START, right: Constants.DEFAULT_PADDING_END }) 476 .margin({ bottom: Constants.AUTHORITY_ROW_MARGIN_BOTTOM }) 477 Row() { 478 Column() { 479 if (!this.applicationList.length) { 480 if (this.searchResult) { 481 Row() {} 482 } else { 483 Row() { 484 Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { 485 Image($r('app.media.searchnoresult')) 486 .customizeImage(Constants.SEARCHNORESULT_IMAGE_WIDTH, Constants.SEARCHNORESULT_IMAGE_HEIGHT) 487 } 488 } 489 } 490 } else { 491 Row() { 492 List({ scroller: this.scroller }) { 493 ForEach(sortByName(this.applicationList), (item: ApplicationObj) => { 494 this.ListItemLayout(item) 495 }, (item: ApplicationObj) => JSON.stringify(item)) 496 } 497 .backgroundColor($r('sys.color.ohos_id_color_list_card_bg')) 498 .borderRadius($r('sys.float.ohos_id_corner_radius_card')) 499 .padding(Constants.LIST_PADDING_TOP) 500 .divider({ 501 strokeWidth: Constants.DIVIDER, 502 color: $r('sys.color.ohos_id_color_list_separator'), 503 startMargin: Constants.DIVIDER_MARGIN_RIGHT_APPLICATION, 504 endMargin: Constants.DEFAULT_MARGIN_END 505 }) 506 .onScrollIndex((start, end) => { 507 GlobalContext.getContext().set('scroller', this.scroller); 508 if (this.applicationList.length > 0) { 509 let alphabeticalIndex: string = sortByName(this.applicationList)[start].indexTag; 510 let index = indexValue.indexOf(alphabeticalIndex); 511 this.selectedIndex = index >= 0 ? index : 0; 512 } 513 }) 514 } 515 } 516 }.width(Constants.FULL_WIDTH) 517 .margin({ bottom: globalGroup.includes(this.currentGroup) && this.isMuteSupported === true ? Constants.AUTHORITY_LIST_MARGIN_BOTTOM_GLOBAL : Constants.AUTHORITY_LIST_MARGIN_BOTTOM }) 518 } 519 }.padding({ left: Constants.AUTHORITY_LISTITEM_PADDING_LEFT }) 520 Column() { 521 alphabetIndexerComponent({ applicationItem: $applicationList, index: $selectedIndex }) 522 }.width(Constants.AUTHORITY_ALPHABETINDEX_WIDTH) 523 .padding({ top: Constants.AUTHORITY_ALPHABETINDEX_PADDING_TOP }) 524 .margin({ bottom: Constants.APPLICATION_LIST_MARGIN_BOTTOM }) 525 }.flexGrow(Constants.FLEX_GROW) 526 }.height(Constants.FULL_HEIGHT) 527 } 528} 529