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 window from '@ohos.window'; 17import common from '@ohos.app.ability.common'; 18import Constants from '../common/utils/constant'; 19import { CustomContentDialog } from '@ohos.arkui.advanced.Dialog'; 20import { Log, getFontSizeScale, } from '../common/utils/utils'; 21import { EventObserver } from '../common/observer/EventObserver'; 22import { PermissionGroup, ButtonStatus } from '../common/model/definition'; 23import { CallerAppInfo, PermissionGroupConfig } from '../common/model/typedef'; 24import { LocationCanvas } from '../common/components/location'; 25import { buttonResource } from '../common/model/permissionGroup'; 26import { GrantDialogIntent } from '../ServiceExtAbility/GrantDialogIntent'; 27import { GrantDialogViewModel } from '../ServiceExtAbility/GrantDialogViewModel'; 28import { GrantDialogViewState } from '../ServiceExtAbility/GrantDialogViewState'; 29 30@Extend(Button)function customizeButton() { 31 .buttonStyle(ButtonStyleMode.TEXTUAL) 32 .fontColor($r('sys.color.font_emphasize')) 33 .width(Constants.HALF_LENGTH) 34} 35 36@Extend(Text)function titleText() { 37 .fontWeight(FontWeight.Bold) 38 .fontColor($r('sys.color.font_primary')) 39 .textAlign(TextAlign.Center) 40 .textOverflow({ overflow: TextOverflow.Ellipsis }) 41 .maxLines(Constants.SECURITY_HEADER_MAX_LINES) 42} 43 44const EVENT_BUNDLE_RESOURCES_CHANGED = 'usual.event.BUNDLE_RESOURCES_CHANGED'; 45 46@Entry({ useSharedStorage: true }) 47@Component 48struct dialogPlusPage { 49 private context = this.getUIContext().getHostContext() as common.ServiceExtensionContext; 50 private resourceChangeObserver: EventObserver = new EventObserver([EVENT_BUNDLE_RESOURCES_CHANGED]); 51 @LocalStorageLink('callerAppInfo') callerAppInfo: CallerAppInfo = {} as CallerAppInfo; 52 @LocalStorageLink('win') win: window.Window = {} as window.Window; 53 @State viewModel: GrantDialogViewModel = new GrantDialogViewModel(this.callerAppInfo, this.win); 54 @State viewState: GrantDialogViewState = this.viewModel.getViewState(); 55 private refreshData = (): void => { 56 if (this.callerAppInfo === undefined) { 57 Log.error(`event error, callerAppInfo is undefined.`); 58 return; 59 } 60 try { 61 this.viewModel.processIntent( 62 new GrantDialogIntent.RefreshIntent(this.context, this.callerAppInfo) 63 ); 64 } catch (error) { 65 Log.error(`try to refresh data faild, code: ${error.code}, message: ${error.message}.`); 66 } 67 } 68 69 dialogController: CustomDialogController | null = new CustomDialogController({ 70 builder: CustomContentDialog({ 71 contentBuilder: () => { 72 this.buildContent(); 73 }, 74 contentAreaPadding: { right: 0 } 75 }), 76 autoCancel: false 77 }); 78 79 @Builder 80 DialogTitle() { 81 Row() { 82 Column() { 83 if (getFontSizeScale()) { 84 Text(this.viewState.grantGroups[this.viewState.curIndex].title) 85 .titleText() 86 .fontSize($r('sys.float.Title_S')) 87 } else { 88 Text(this.viewState.grantGroups[this.viewState.curIndex].title) 89 .titleText() 90 .minFontSize(Constants.TEXT_MIDDLE_FONT_SIZE) 91 .maxFontSize($r('sys.float.Title_S')) 92 .heightAdaptivePolicy(TextHeightAdaptivePolicy.MAX_LINES_FIRST) 93 } 94 } 95 .constraintSize({ minHeight: Constants.HEADLINE_HEIGHT }) 96 .justifyContent(FlexAlign.Center) 97 .padding({ 98 top: Constants.DEFAULT_PADDING_TOP, 99 bottom: Constants.DEFAULT_PADDING_BOTTOM, 100 left: Constants.PADDING_24, 101 right: Constants.PADDING_24 102 }) 103 } 104 } 105 106 @Builder 107 buildContent(): void { 108 Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { 109 Column() { 110 Image(this.viewState.grantGroups[this.viewState.curIndex].icon) 111 .width(Constants.DIALOG_ICON_WIDTH) 112 .height(Constants.DIALOG_ICON_HEIGHT) 113 .fillColor($r('sys.color.font_primary')) 114 .margin({ top: Constants.DIALOG_ICON_MARGIN_TOP }) 115 if (this.viewState.grantGroups.length > 1) { 116 Text(`${this.viewState.curIndex + 1} / ${this.viewState.grantGroups.length}`) 117 .fontSize(Constants.DIALOG_LABEL_FONT_SIZE) 118 .fontColor($r('sys.color.font_secondary')) 119 .lineHeight(Constants.DIALOG_LABEL_LINE_HEIGHT) 120 .margin({ top: Constants.DIALOG_LABEL_MARGIN_TOP }) 121 } 122 Scroll() { 123 Column() { 124 this.DialogTitle(); 125 126 Row() { 127 Flex({ justifyContent: FlexAlign.Center }) { 128 Text() { 129 if (this.viewState.grantGroups[this.viewState.curIndex].readAndWrite) { 130 Span(this.viewState.grantGroups[this.viewState.curIndex].readAndWrite) 131 Span(this.viewState.grantGroups[this.viewState.curIndex].reason ? $r('app.string.comma') : $r('app.string.period')) 132 } 133 Span(this.showReason()) 134 } 135 .textAlign(TextAlign.Start) 136 .fontColor($r('sys.color.font_primary')) 137 .fontSize($r('sys.float.Body_L')) 138 .maxFontScale(Constants.DIALOG_TEXT_MAX_SCALE) 139 .margin({ 140 left: Constants.MARGIN_24, 141 right: Constants.MARGIN_24, 142 bottom: Constants.MARGIN_8 143 }) 144 } 145 } 146 147 if ( 148 this.viewState.locationFlag > Constants.LOCATION_NONE && 149 this.viewState.grantGroups[this.viewState.curIndex].groupName === PermissionGroup.LOCATION 150 ) { 151 LocationCanvas({ viewState: $viewState }) 152 } 153 } 154 }.constraintSize({ maxHeight: Constants.MAXIMUM_HEADER_HEIGHT }) 155 if ( 156 this.viewState.grantGroups[this.viewState.curIndex].buttonList.length <= 2 && 157 this.calculateButtonWidth(this.viewState.grantGroups[this.viewState.curIndex].buttonList) 158 ) { 159 //横向布局 160 Row() { 161 Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { 162 Button(buttonResource.get(this.viewState.grantGroups[this.viewState.curIndex].buttonList[0])) 163 .customizeButton() 164 .onClick(() => { 165 this.processClick(this.viewState.grantGroups[this.viewState.curIndex].buttonList[0]); 166 }) 167 Divider() 168 .color($r('sys.color.comp_divider')) 169 .vertical(true) 170 .strokeWidth(Constants.DIALOG_DIVIDER) 171 .height(Constants.DIVIDER_HEIGHT) 172 .opacity(0.2) 173 .margin({ left: Constants.MARGIN_8, right: Constants.MARGIN_8 }) 174 Button(buttonResource.get(this.viewState.grantGroups[this.viewState.curIndex].buttonList[1])) 175 .customizeButton() 176 .onClick(() => { 177 this.processClick(this.viewState.grantGroups[this.viewState.curIndex].buttonList[1]); 178 }) 179 }.margin({ 180 left: Constants.MARGIN_16, 181 right: Constants.MARGIN_16, 182 bottom: Constants.MARGIN_8 183 }) 184 } 185 } else { 186 //纵向布局 187 Column() { 188 ForEach( 189 this.viewState.grantGroups[this.viewState.curIndex].buttonList, (buttonStatus: ButtonStatus, idx: number 190 ) => { 191 Button(buttonResource.get(buttonStatus)) 192 .customizeButton() 193 .width(Constants.FULL_WIDTH) 194 .margin({ 195 bottom: idx + 1 < this.viewState.grantGroups[this.viewState.curIndex].buttonList.length ? 196 Constants.MARGIN_4 : 0 197 }) 198 .onClick(() => { 199 this.processClick(buttonStatus); 200 }) 201 }) 202 } 203 .padding({ left: Constants.PADDING_16, right: Constants.PADDING_16 }) 204 } 205 } 206 .padding({ bottom: Constants.PADDING_8 }) 207 .clip(true) 208 } 209 } 210 211 build() {} 212 213 showReason(): ResourceStr { 214 if (this.viewState.grantGroups[this.viewState.curIndex].groupName === PermissionGroup.LOCATION) { 215 if ( 216 (this.viewState.locationFlag === Constants.LOCATION_FUZZY) || 217 (this.viewState.locationFlag === Constants.LOCATION_BOTH_FUZZY) 218 ) { 219 return $r('app.string.close_exact_position'); 220 } 221 } 222 return this.viewState.grantGroups[this.viewState.curIndex].reason; 223 } 224 225 calculateButtonWidth(buttonStatus: ButtonStatus[]): boolean { 226 let denyText = buttonResource.get(buttonStatus[0]); 227 let allowText = buttonResource.get(buttonStatus[1]); 228 let maxButtonTextLength = Math.max( 229 this.getUIContext().getMeasureUtils().measureText({ textContent: denyText }), 230 this.getUIContext().getMeasureUtils().measureText({ textContent: allowText }) 231 ) 232 233 Log.info(`px2vp(maxButtonTextLength): ${this.getUIContext().px2vp(maxButtonTextLength)}`); 234 if (this.getUIContext().px2vp(maxButtonTextLength) > Constants.DIALOG_BUTTON_MAX_WIDTH) { 235 return false; 236 } 237 238 return true; 239 } 240 241 aboutToAppear() { 242 this.viewModel.processIntent(new GrantDialogIntent.InitIntent(this.context)).then(result => { 243 this.dialogController?.open(); 244 }) 245 this.context.eventHub.on('refresh', () => { 246 this.refreshData(); 247 }) 248 this.resourceChangeObserver.register(this.refreshData); 249 } 250 251 aboutToDisappear() { 252 this.dialogController = null; 253 this.context.eventHub.off('refresh'); 254 this.resourceChangeObserver.unregister(); 255 } 256 257 processClick(buttonStatus: ButtonStatus) { 258 let groupConfig: PermissionGroupConfig = this.viewState.grantGroups[this.viewState.curIndex]; 259 if (this.callerAppInfo === undefined) { 260 Log.error(`processClick faild, callerAppInfo is undefined.`); 261 return; 262 } 263 let clickIntent: GrantDialogIntent.ClickIntent = 264 new GrantDialogIntent.ClickIntent(this.context, groupConfig, this.callerAppInfo, buttonStatus); 265 this.viewModel.processIntent(clickIntent); 266 } 267 268}