/*
* Copyright (c) 2022-2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import deviceManager from '@ohos.distributedHardware.deviceManager';
import UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession'
import mediaquery from '@ohos.mediaquery';
import deviceInfo from '@ohos.deviceInfo';
import display from '@ohos.display';
import inputMethod from '@ohos.inputMethod';
import Constant from '../common/constant';
import accessibility from '@ohos.accessibility';
import common from '@ohos.app.ability.common';
import i18n from '@ohos.i18n';
let dmClass: deviceManager.DeviceManager | null;
let TAG = '[DeviceManagerUI:InputPinDialog]==>'
let metaType: number = 11;
const ACTION_CANCEL_PINCODE_INPUT: number = 4
const ACTION_DONE_PINCODE_INPUT: number = 5
const MSG_PIN_CODE_ERROR: number = 0
const MSG_CANCEL_PIN_CODE_INPUT: number = 3
const MSG_DOING_AUTH: number = 4
const MODEL_PIN: string = 'pin';
const MODEL_PASSWORD: string = 'password';
@CustomDialog
struct InputCustomDialog {
@State password: string = '';
@State passwordCircle: string[] = ['', '', '', '', '', ''];
@State isTimes: number = 3;
@State errorTips: Resource = $r('app.plural.dm_incorrect_code', this.isTimes, this.isTimes);
@State errorTipsVisible: Visibility = Visibility.None;
@State heightNum: number = 600;
@State targetDeviceName: string = '';
@State model: string = MODEL_PIN;
@State isPC: boolean = false;
@State isPhone: boolean = false;
@State btnColor: ResourceColor = Color.Transparent;
@State mLocalHeight: number = 1;
listener: mediaquery.MediaQueryListener = mediaquery.matchMediaSync('(orientation: landscape)');
controller?: CustomDialogController;
private scroller: Scroller = new Scroller();
onPortrait(mediaQueryResult: mediaquery.MediaQueryResult) {
try {
this.mLocalHeight = display.getDefaultDisplaySync().height;
} catch (e) {
console.error('Failed to get display height:', e);
this.mLocalHeight = 1;
}
let heightRatio = px2vp(this.mLocalHeight) * 0.2;
if (mediaQueryResult.matches as boolean) {
if (this.isPhone) {
try {
if (display.isFoldable() &&
display.getFoldDisplayMode() === display.FoldDisplayMode.FOLD_DISPLAY_MODE_MAIN) {
this.heightNum = heightRatio;
} else {
try {
if (!display.isFoldable()) {
this.heightNum = heightRatio;
}
} catch (e) {
console.error('Failed to check isFoldable (else if):', e);
}
}
} catch (e) {
console.error('Failed to check isFoldable or fold mode:', e);
}
} else {
this.heightNum = 300;
}
} else {
this.heightNum = 800;
}
}
aboutToDisappear() {
console.info(TAG + 'InputCustomDialog aboutToDisappear');
let ims = inputMethod.getSetting();
ims.off('imeShow');
if (this.listener) {
this.listener.off('change');
}
}
aboutToAppear() {
console.info(TAG + 'InputCustomDialog aboutToAppear');
try {
this.mLocalHeight = display.getDefaultDisplaySync().height;
} catch (err) {
console.error('Failed to get display height:', err);
this.mLocalHeight = 1;
}
this.isPC = Constant.isPC();
this.isPhone = Constant.isPhone();
let ims = inputMethod.getSetting();
ims.on('imeShow', (info: Array<inputMethod.InputWindowInfo>) => {
this.scroller.scrollTo({yOffset: 72, xOffset: 0});
});
if (AppStorage.get('mediaQueryResult') != null && AppStorage.get('mediaQueryResult') as boolean && this.isPhone) {
let heightRatio = px2vp(this.mLocalHeight) * 0.2;
try {
if (display.isFoldable() &&
display.getFoldDisplayMode() === display.FoldDisplayMode.FOLD_DISPLAY_MODE_MAIN) {
this.heightNum = heightRatio;
} else {
try {
if (!display.isFoldable()) {
this.heightNum = heightRatio;
}
} catch (err) {
console.error('Failed to check isFoldable (else if):', err);
}
}
} catch (err) {
console.error('Failed to check isFoldable or fold mode:', err);
}
}
if (AppStorage.get('targetDeviceName') != null) {
this.targetDeviceName = AppStorage.get('targetDeviceName') as string;
}
if (AppStorage.get('model') != null) {
this.model = AppStorage.get('model') as string;
console.log('model is ' + this.model);
}
if (AppStorage.get('metaType') != null) {
metaType = AppStorage.get('metaType') as number;
}
deviceManager.createDeviceManager('com.ohos.devicemanagerui.input',
(err: Error, dm: deviceManager.DeviceManager) => {
if (err) {
console.log('createDeviceManager err:' + JSON.stringify(err) + ' --fail:' + '${dm}');
return;
}
dmClass = dm;
dmClass.on('uiStateChange', (data: Record<string, string>) => {
console.log('uiStateChange executed, dialog closed' + JSON.stringify(data));
let tmpStr: Record<string, number> = JSON.parse(data.param);
let msg: number = tmpStr.uiStateMsg as number;
if (msg === MSG_DOING_AUTH) {
this.errorTips = $r('app.string.dm_authenticating');
this.errorTipsVisible = Visibility.Visible;
return;
}
if (msg === MSG_CANCEL_PIN_CODE_INPUT) {
this.destruction();
return;
}
if (msg === MSG_PIN_CODE_ERROR) {
this.inputCodeError();
}
})
});
this.listener.on('change', (mediaQueryResult: mediaquery.MediaQueryResult) => {
this.onPortrait(mediaQueryResult);
});
}
sendAccessibilityEvent(times: number) {
console.log(TAG + 'sendAccessibilityEvent in');
let context = getContext(this) as common.UIAbilityContext;
let str = context.resourceManager.getPluralStringValueSync($r('app.plural.dm_incorrect_code').id, times)
let eventInfo: accessibility.EventInfo = ({
type: 'announceForAccessibility',
bundleName: 'com.ohos.devicemanagerui',
triggerAction: 'common',
textAnnouncedForAccessibility: str
})
try {
accessibility.sendAccessibilityEvent(eventInfo).then(()=>{
console.info(`${TAG} Succeeded in send event, eventInfo is ${JSON.stringify(eventInfo)}`);
});
} catch (error) {
console.info(`${TAG} Failed in send event, error.message is ${error.message}`);
}
}
inputCodeError() {
console.log(TAG + 'inputCodeError in');
if (this.model == MODEL_PASSWORD) {
this.errorTips = $r('app.string.dm_password_error');
} else {
this.isTimes--;
this.errorTips = $r('app.plural.dm_incorrect_code', this.isTimes, this.isTimes);
this.sendAccessibilityEvent(this.isTimes);
}
this.password = '';
this.errorTipsVisible = Visibility.Visible;
this.passwordCircle = ['', '', '', '', '', ''];
}
cancel() {
console.log('cancle');
if (dmClass) {
console.log('deviceManager exist');
} else {
console.log('createDeviceManager is null');
return;
}
console.log('cancle' + ACTION_CANCEL_PINCODE_INPUT);
const param = {
'META_TYPE': metaType
};
const jsonStr = JSON.stringify(param);
this.setUserOperation(ACTION_CANCEL_PINCODE_INPUT, jsonStr);
this.destruction();
}
confirm() {
console.log('confirm');
if (this.password == null || this.password == '') {
return;
}
if (dmClass) {
console.log('deviceManager exist');
} else {
console.log('createDeviceManager is null');
return;
}
console.log('confirm' + JSON.stringify(ACTION_DONE_PINCODE_INPUT));
const param = {
'pinCode': this.password,
'META_TYPE': metaType
};
const jsonStr = JSON.stringify(param);
this.setUserOperation(ACTION_DONE_PINCODE_INPUT, jsonStr);
}
setUserOperation(operation: number, extra: string) {
console.log('setUserOperation: ' + operation);
if (dmClass == null) {
console.log('setUserOperation: ' + 'dmClass null');
return;
}
try {
dmClass.setUserOperation(operation, extra);
} catch (error) {
console.log('dmClass setUserOperation failed');
}
}
destruction() {
console.info(TAG + 'destruction');
let inputMethodController = inputMethod.getController();
inputMethodController.hideTextInput();
let session = AppStorage.get<UIExtensionContentSession>('inputSession');
if (session) {
console.info(TAG + 'terminateSelf');
session.terminateSelf();
}
}
isNumberSix(str: string): boolean {
console.info(TAG + 'isNumber6 in');
const reg: RegExp = new RegExp('^[0-9]{6}$');
return reg.test(str);
}
passwordOnChange(value: string) {
console.info(TAG + 'passwordOnChange in');
if (this.isNumberSix(value)) {
this.confirm();
}
}
private isTibetanLanguages(): boolean {
console.info(`${TAG} isTibetanLanguages in`);
let locale = new Intl.Locale(i18n.System.getSystemLanguage()).toString();
console.info(`${TAG} isTibetanLanguages: ${locale}`);
return Constant.TIBETAN_LANGUAGES.includes(locale);
}
build() {
GridRow({
columns: { xs: 4, sm: 8, md: this.isPC ? 24 : 12 },
gutter: { x: 4 },
breakpoints: { value: ['600vp', '840vp'] }
}) {
GridCol({ span: { xs: 4, sm: 4, md: this.isPC ? 6 : 4 }, offset: { sm: 2, md: this.isPC ? 9 : 4 } }) {
Scroll(this.scroller) {
Column() {
Column() {
Text($r('app.string.dm_connect', this.targetDeviceName))
.fontSize($r('sys.float.ohos_id_text_size_dialog_tittle'))
.fontWeight(FontWeight.Bold)
.fontColor($r('sys.color.ohos_id_color_text_primary'))
.margin({ top: 12, bottom: 3 })
.width('auto')
.textAlign(TextAlign.Center)
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.minFontSize(12)
.maxFontSize($r('sys.float.ohos_id_text_size_dialog_tittle'))
.heightAdaptivePolicy(TextHeightAdaptivePolicy.LAYOUT_CONSTRAINT_FIRST)
.lineHeight(this.isTibetanLanguages() ? 31 : 0)
Text($r('app.string.dm_enter_connect_code'))
.fontSize($r('sys.float.ohos_id_text_size_body2'))
.fontWeight(FontWeight.Regular)
.fontColor($r('sys.color.ohos_id_color_text_secondary'))
.margin({ bottom: 8 })
.width('auto')
.maxLines(2)
.textAlign(TextAlign.Center)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.minFontSize(12)
.maxFontSize($r('sys.float.ohos_id_text_size_body2'))
.heightAdaptivePolicy(TextHeightAdaptivePolicy.LAYOUT_CONSTRAINT_FIRST)
.lineHeight(this.isTibetanLanguages() ? 22 : 0)
}
.margin({ left: 24, right: 24 })
.constraintSize({ minHeight: 72 })
.justifyContent(FlexAlign.Center)
Stack() {
List() {
ListItem() {
Flex({ justifyContent: FlexAlign.Center }) {
ForEach(this.passwordCircle, (item:string) => {
Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
Text(item)
.fontSize($r('sys.float.ohos_id_text_size_headline7'))
.fontColor($r('sys.color.ohos_id_color_text_primary'))
.fontWeight(FontWeight.Medium)
}.width('10%')
.height('100%')
.visibility(item === '' ? Visibility.None : Visibility.Visible)
})
ForEach(this.passwordCircle, (item: string) => {
Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
Column()
.width(12)
.height(12)
.border({ width: 2, color: $r('sys.color.ohos_id_color_primary'), radius: 12})
}.width('10%')
.height('100%')
.visibility(item === '' ? Visibility.Visible : Visibility.None)
})
}
}
}
TextInput({ placeholder: '', text: this.password})
.defaultFocus(true)
.onAppear(() => {
focusControl.requestFocus('inputpin')
})
.id('inputpin')
.type(8)
.height(60)
.opacity(0)
.fontColor(('rgba(0,0,0,0)'))
.backgroundColor(('rgba(0,0,0,0)'))
.caretColor(('rgba(0,0,0,0)'))
.maxLength(6)
.margin({ bottom: 8 })
.width('100%')
.onChange((value: string) => {
this.password = value;
if (value.length > 6) {
return;
}
let length = value.length;
for (let i = 0; i < 6; i++) {
if (i < length) {
this.passwordCircle[i] = value[i];
} else {
this.passwordCircle[i] = '';
}
}
let gThis = this;
setTimeout(()=> {
gThis.passwordOnChange(value);
}, 50)
})
}.height(48)
.margin({ top: 12, bottom: 16})
Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
Text(this.errorTips)
.fontSize($r('sys.float.ohos_id_text_size_body2'))
.fontWeight(FontWeight.Medium)
.fontColor($r('sys.color.ohos_id_color_warning'))
.lineHeight(this.isTibetanLanguages() ? 22 : 0)
}.visibility(this.errorTipsVisible)
.margin({ bottom: 16,
left: $r('sys.float.ohos_id_corner_radius_dialog'),
right: $r('sys.float.ohos_id_corner_radius_dialog') })
Flex({ justifyContent: FlexAlign.Center }) {
Button($r('app.string.dm_cancel'))
.constraintSize({ minHeight: 40 })
.fontSize($r('sys.float.ohos_id_text_size_button1'))
.onClick(() => {
if (this.controller) {
this.controller.close();
}
this.cancel();
})
.width('100%')
.backgroundColor(this.btnColor)
.fontColor($r('sys.color.ohos_id_color_text_primary_activated'))
.onHover((isHover?: boolean, event?: HoverEvent): void => {
if (isHover) {
this.btnColor = $r('sys.color.ohos_id_color_hover');
} else {
this.btnColor = this.isPC ? $r('sys.color.ohos_id_color_button_normal') : Color.Transparent;
}
})
.stateStyles({
pressed: {
.backgroundColor($r('sys.color.ohos_id_color_click_effect'))
},
normal: {
.backgroundColor(this.isPC ? $r('sys.color.ohos_id_color_button_normal') : Color.Transparent)
}
})
}.margin({
left: 16,
right: 16,
bottom: this.isPC ? 24 : 16 })
}
}
.scrollable(ScrollDirection.Vertical)
.scrollBar(BarState.On)
.constraintSize({ maxHeight: `${this.heightNum}`})
.borderRadius($r('sys.float.ohos_id_corner_radius_dialog'))
.backgroundBlurStyle(BlurStyle.COMPONENT_ULTRA_THICK)
.margin({
left: $r('sys.float.ohos_id_dialog_margin_bottom'),
right: $r('sys.float.ohos_id_dialog_margin_bottom')
})
}
}.margin({top: 8, bottom: 20})
.constraintSize({
maxHeight: '40%'
})
}
}
@Entry
@Component
struct dialogPlusPage {
mediaQueryListener: mediaquery.MediaQueryListener = mediaquery.matchMediaSync('(orientation: landscape)');
private portraitHandler = this.onPortrait.bind(this);
dialogController: CustomDialogController | null = new CustomDialogController({
builder: InputCustomDialog(),
autoCancel: false,
alignment: DialogAlignment.Center,
offset: { dx: 0, dy: 0 },
customStyle: true,
maskColor: $r('sys.color.ohos_id_color_mask_thin')
});
aboutToAppear() {
console.log(TAG + 'aboutToAppear aboutToAppear');
this.mediaQueryListener.on('change', this.portraitHandler);
}
onPortrait(mediaQueryResult: mediaquery.MediaQueryResult) {
AppStorage.setOrCreate('mediaQueryResult', mediaQueryResult.matches as boolean);
}
aboutToDisappear() {
console.log(TAG + 'aboutToDisappear aboutToDisappear')
if (this.mediaQueryListener) {
this.mediaQueryListener.off('change', this.portraitHandler);
}
if (this.dialogController) {
this.dialogController = null;
}
if (dmClass != null) {
try {
dmClass.off('uiStateChange');
dmClass.release();
} catch (error) {
console.log('dmClass release failed');
}
dmClass = null
}
}
build() {
if (this.dialogController) {
Column(this.dialogController.open())
}
}
}