From 2d3ca42bb5d4e0c9f75b011d8c2f0a4255312e1d Mon Sep 17 00:00:00 2001 From: "xiaohu.xie" Date: Mon, 1 Apr 2024 15:00:11 +0800 Subject: [PATCH 1/3] add vpn icon --- common/src/main/ets/default/Constants.ts | 1 + .../ets/com/ohos/pages/StatusBarComponent.ets | 3 + features/vpncomponent/build-profile.json5 | 8 ++ features/vpncomponent/hvigorfile.js | 17 +++ features/vpncomponent/oh-package.json5 | 7 ++ .../ets/default/common/StyleConfiguration.ts | 30 +++++ .../src/main/ets/default/model/VpnService.ts | 104 ++++++++++++++++++ .../pages/StatusBarIconItemVpnComponent.ets | 50 +++++++++ .../src/main/ets/default/viewmodel/VpnVM.ts | 80 ++++++++++++++ features/vpncomponent/src/main/module.json5 | 10 ++ .../main/resources/base/element/float.json | 16 +++ .../main/resources/base/element/string.json | 8 ++ .../resources/base/media/ic_statusbar_vpn.svg | 3 + .../main/resources/en_US/element/string.json | 8 ++ .../main/resources/phone/element/float.json | 16 +++ .../main/resources/zh_CN/element/string.json | 8 ++ .../main/ets/pages/common/StatusbarConfig.ts | 1 + .../main/resources/base/element/float.json | 8 ++ .../resources/base/media/ic_statusbar_vpn.svg | 3 + .../main/ets/pages/common/StatusbarConfig.ts | 4 +- .../main/resources/base/element/float.json | 8 ++ .../resources/base/media/ic_statusbar_vpn.svg | 3 + signature/OpenHarmony.p12 | Bin 0 -> 8252 bytes signature/OpenHarmonyApplication.cer | 44 ++++++++ .../ac/f9d9d313be7042d4933e55eb1e8402f6 | 1 + .../ce/f7cfa6201a1644c5a0ba82969741aea2 | Bin 0 -> 48 bytes .../fd/0/4667d093e8bc476ca6f801834e185457 | 1 + .../fd/1/f5ed619c706f4a4db277ca6beb69de56 | 1 + .../fd/2/3aa4a14d432a48efb922fca804629ff8 | 1 + 29 files changed, 443 insertions(+), 1 deletion(-) create mode 100755 features/vpncomponent/build-profile.json5 create mode 100755 features/vpncomponent/hvigorfile.js create mode 100755 features/vpncomponent/oh-package.json5 create mode 100755 features/vpncomponent/src/main/ets/default/common/StyleConfiguration.ts create mode 100755 features/vpncomponent/src/main/ets/default/model/VpnService.ts create mode 100755 features/vpncomponent/src/main/ets/default/pages/StatusBarIconItemVpnComponent.ets create mode 100755 features/vpncomponent/src/main/ets/default/viewmodel/VpnVM.ts create mode 100755 features/vpncomponent/src/main/module.json5 create mode 100755 features/vpncomponent/src/main/resources/base/element/float.json create mode 100755 features/vpncomponent/src/main/resources/base/element/string.json create mode 100755 features/vpncomponent/src/main/resources/base/media/ic_statusbar_vpn.svg create mode 100755 features/vpncomponent/src/main/resources/en_US/element/string.json create mode 100755 features/vpncomponent/src/main/resources/phone/element/float.json create mode 100755 features/vpncomponent/src/main/resources/zh_CN/element/string.json create mode 100755 product/pc/statusbar/src/main/resources/base/media/ic_statusbar_vpn.svg create mode 100755 product/phone/statusbar/src/main/resources/base/media/ic_statusbar_vpn.svg create mode 100755 signature/OpenHarmony.p12 create mode 100755 signature/OpenHarmonyApplication.cer create mode 100755 signature/material/ac/f9d9d313be7042d4933e55eb1e8402f6 create mode 100755 signature/material/ce/f7cfa6201a1644c5a0ba82969741aea2 create mode 100755 signature/material/fd/0/4667d093e8bc476ca6f801834e185457 create mode 100755 signature/material/fd/1/f5ed619c706f4a4db277ca6beb69de56 create mode 100755 signature/material/fd/2/3aa4a14d432a48efb922fca804629ff8 diff --git a/common/src/main/ets/default/Constants.ts b/common/src/main/ets/default/Constants.ts index 3eb067e5..f6f1d9ce 100644 --- a/common/src/main/ets/default/Constants.ts +++ b/common/src/main/ets/default/Constants.ts @@ -69,6 +69,7 @@ export enum FASlotName { SIGNAL = 'signal', WIFI = 'wifi', NFC = 'nfc', + VPN = 'vpn', } export function isNfcAvailable(){ diff --git a/features/statusbarcomponent/src/main/ets/com/ohos/pages/StatusBarComponent.ets b/features/statusbarcomponent/src/main/ets/com/ohos/pages/StatusBarComponent.ets index 308e50b4..063e8556 100644 --- a/features/statusbarcomponent/src/main/ets/com/ohos/pages/StatusBarComponent.ets +++ b/features/statusbarcomponent/src/main/ets/com/ohos/pages/StatusBarComponent.ets @@ -28,6 +28,7 @@ import IconItemComponent from './IconItemComponent' import BatteryIcon from '../../../../../../../batterycomponent/src/main/ets/default/pages/batteryIcon' import ClockIcon from '../../../../../../../clockcomponent/src/main/ets/default/pages/clockIcon' import AirplaneIcon from '../../../../../../../airplanecomponent/src/main/ets/default/pages/StatusBarIconItemAirplaneComponent' +import VpnIcon from '../../../../../../../vpncomponent/src/main/ets/default/pages/StatusBarIconItemVpnComponent' import WifiIcon from '../../../../../../../wificomponent/src/main/ets/default/pages/wifiIcon' import BluetoothIcon from '../../../../../../../bluetoothcomponent/src/main/ets/com/ohos/pages/StatusBarIconItemBluetoothComponent' import SignalIcon from '../../../../../../../signalcomponent/src/main/ets/default/pages/signalIcon' @@ -266,6 +267,8 @@ struct StatusBarItemLoadComponent { BatteryIcon() } else if (this.mComponentName == FASlotName.AIR_PLANE) { AirplaneIcon() + } else if (this.mComponentName == FASlotName.VPN) { + VpnIcon() } else if (this.mComponentName == FASlotName.CAPSULE) { CapsuleIcon() } else if (this.mComponentName == FASlotName.NOTIFICATION) { diff --git a/features/vpncomponent/build-profile.json5 b/features/vpncomponent/build-profile.json5 new file mode 100755 index 00000000..181c64c7 --- /dev/null +++ b/features/vpncomponent/build-profile.json5 @@ -0,0 +1,8 @@ +{ + "apiType": 'stageMode', + "targets": [ + { + "name": "default", + } + ] +} \ No newline at end of file diff --git a/features/vpncomponent/hvigorfile.js b/features/vpncomponent/hvigorfile.js new file mode 100755 index 00000000..387f6bed --- /dev/null +++ b/features/vpncomponent/hvigorfile.js @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2022 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. + */ + +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +module.exports = require('@ohos/hvigor-ohos-plugin').harTasks diff --git a/features/vpncomponent/oh-package.json5 b/features/vpncomponent/oh-package.json5 new file mode 100755 index 00000000..e31f0d6a --- /dev/null +++ b/features/vpncomponent/oh-package.json5 @@ -0,0 +1,7 @@ +{ + "devDependencies": {}, + "name": "@ohos/vpncomponent", + "description": "a npm package which contains vpncomponent function", + "version": "1.0.0", + "dependencies": {} +} \ No newline at end of file diff --git a/features/vpncomponent/src/main/ets/default/common/StyleConfiguration.ts b/features/vpncomponent/src/main/ets/default/common/StyleConfiguration.ts new file mode 100755 index 00000000..5a7b8a70 --- /dev/null +++ b/features/vpncomponent/src/main/ets/default/common/StyleConfiguration.ts @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021-2023 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 CommonStyleManager from '../../../../../../../common/src/main/ets/default/CommonStyleManager'; + +const TAG = 'vpn-StyleConfiguration'; + +export class StatusBarVpnComponentStyle { + statusBarVpnWidth: Length = $r("app.float.status_bar_vpn_width"); + statusBarVpnHeight: Length = $r("app.float.status_bar_vpn_height"); +} + +export default class StyleConfiguration { + static getStatusBarVpnComponentStyle(): StatusBarVpnComponentStyle { + const key = TAG + '-StatusBarVpnComponent'; + return CommonStyleManager.getStyle(key, StatusBarVpnComponentStyle); + } +} diff --git a/features/vpncomponent/src/main/ets/default/model/VpnService.ts b/features/vpncomponent/src/main/ets/default/model/VpnService.ts new file mode 100755 index 00000000..02a4cf9b --- /dev/null +++ b/features/vpncomponent/src/main/ets/default/model/VpnService.ts @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2021-2023 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 vpn from '@ohos.net.vpn'; +import Context from 'application/ServiceExtensionContext'; +import CommonEvent from '@ohos.commonEvent' +import connection from '@ohos.net.connection' +import { CommonEventSubscriber } from 'commonEvent/commonEventSubscriber'; +import { CommonEventData } from 'commonEvent/commonEventData'; + +import createOrGet from '../../../../../../../common/src/main/ets/default/SingleInstanceHelper'; +import Log from '../../../../../../../common/src/main/ets/default/Log'; + +const TAG = 'VpnModel'; + +export interface VpnServiceListener { + updateState: (state: boolean) => void; +} + +class VpnService { + mIsStart = false; + mListener: VpnServiceListener; + mSubscriber: CommonEventSubscriber; + mConnection: vpn.VpnConnection | undefined = undefined; + + startService(connection: vpn.VpnConnection | undefined = undefined) { + if (this.mIsStart) return; + if (connection == undefined || connection == null) { + Log.showInfo(TAG, `startService failed: ${connection}`); + return; + } + + this.mConnection = connection; + this.mIsStart = true; + this.subscribe(); + } + + stopService() { + if (!this.mIsStart) return; + this.mIsStart = false; + + this.mListener = null; + this.unsubscribe(); + this.unregisterListener(); + } + + async subscribe() { + this.unsubscribe(); + try { + // @ts-ignore + this.mConnection?.on('connect', (result, data) => { + Log.showInfo(TAG, `vpnConnection connect subscribe on = ${JSON.stringify(result)} data = ${data}`); + if (JSON.stringify(result).match("true")) { + this.mListener?.updateState(true); + } else { + this.mListener?.updateState(false); + } + }) + } catch (error) { + Log.showInfo(TAG, `vpnConnection connect subscribe on error = ${JSON.stringify(error)}`); + } + } + + unsubscribe() { + if (!this.mSubscriber) return; + try { + // @ts-ignore + this.mConnection?.off('connect', (result, data) => { + Log.showInfo(TAG, `vpnConnection connect subscribe on = ${JSON.stringify(result)} data = ${data}`); + if (JSON.stringify(result).match("true")) { + this.mListener?.updateState(true); + } else { + this.mListener?.updateState(false); + } + }) + } catch (error) { + Log.showInfo(TAG, `vpnConnection connect subscribe on error = ${JSON.stringify(error)}`); + } + } + + registerListener(listener: VpnServiceListener) { + this.mListener = listener; + } + + unregisterListener() { + this.mListener = null; + } +} + +const sVpnService = createOrGet(VpnService, TAG); + +export default sVpnService; diff --git a/features/vpncomponent/src/main/ets/default/pages/StatusBarIconItemVpnComponent.ets b/features/vpncomponent/src/main/ets/default/pages/StatusBarIconItemVpnComponent.ets new file mode 100755 index 00000000..8b143c78 --- /dev/null +++ b/features/vpncomponent/src/main/ets/default/pages/StatusBarIconItemVpnComponent.ets @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021-2023 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 StyleConfigurationCommon, { CommonStyle } from '../../../../../../../common/src/main/ets/default/StyleConfiguration' +import { TintContentInfo } from '../../../../../../../common/src/main/ets/default/TintStateManager'; +import Log from '../../../../../../../common/src/main/ets/default/Log'; +import StyleConfiguration, { StatusBarVpnComponentStyle } from '../common/StyleConfiguration' +import ViewModel from '../viewmodel/VpnVM' + +const TAG = 'vpn-StatusBarIconItemVpnComponent'; + +@Component +export default struct StatusBarIconItemVpnComponent { + @StorageLink('Vpn_Status') vpnStatus: boolean = false; + @State mTintContentInfo: TintContentInfo = ViewModel.getTintContentInfo(); + @State styleCommon: CommonStyle = StyleConfigurationCommon.getCommonStyle(); + @State style: StatusBarVpnComponentStyle = StyleConfiguration.getStatusBarVpnComponentStyle(); + + aboutToAppear() { + ViewModel.startVM(); + } + + build() { + Row() { + if (this.vpnStatus) { + Row().width(this.styleCommon.statusBarMarginLeftRight).height('100%') + Image($r("app.media.ic_statusbar_vpn")) + .objectFit(ImageFit.Contain) + .width(this.style.statusBarVpnWidth) + .height(this.style.statusBarVpnHeight) + .fillColor(this.mTintContentInfo.contentColor) + Row().width(this.styleCommon.statusBarMarginLeftRight).height('100%') + } + } + .height('100%') + .opacity($r("app.float.icon_component_opacity")) + } +} diff --git a/features/vpncomponent/src/main/ets/default/viewmodel/VpnVM.ts b/features/vpncomponent/src/main/ets/default/viewmodel/VpnVM.ts new file mode 100755 index 00000000..f1ca9689 --- /dev/null +++ b/features/vpncomponent/src/main/ets/default/viewmodel/VpnVM.ts @@ -0,0 +1,80 @@ +//@ts-nocheck +/* + * Copyright (c) 2021-2023 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 vpn from '@ohos.net.vpn'; +import Context from 'application/ServiceExtensionContext'; +import Log from '../../../../../../../common/src/main/ets/default/Log'; +import createOrGet from '../../../../../../../common/src/main/ets/default/SingleInstanceHelper'; +import { TintContentInfo, getOrCreateTintContentInfo +} from '../../../../../../../common/src/main/ets/default/TintStateManager'; +import { FASlotName } from '../../../../../../../common/src/main/ets/default/Constants'; +import AbilityManager from '../../../../../../../common/src/main/ets/default/abilitymanager/abilityManager'; +import VpnService from '../model/VpnService'; + +const TAG = 'VpnVMxxh'; + +class VpnVM { + mIsStart: boolean = false; + mVpnStatus: boolean = false; + mTintContentInfo: TintContentInfo = getOrCreateTintContentInfo(FASlotName.VPN); + mIsToggling: boolean = false; + context: Context; + mConnection: vpn.VpnConnection | undefined = undefined; + + constructor() { + this.context = AbilityManager.getContext(AbilityManager.getContextName(AbilityManager.ABILITY_NAME_CONTROL_PANEL)); + if (this.context == undefined || this.context == null) { + Log.showInfo(TAG, `constructor: ${context}`); + return; + } + AppStorage.SetOrCreate('Vpn_Status', this.mVpnStatus); + } + + startVM() { + if (this.mIsStart) return; + this.mIsStart = true; + + VpnService.registerListener(this); + VpnService.startService(vpn.createVpnConnection(this.context)); + + Log.showInfo(TAG, 'startVM') + } + + stopVM() { + if (!this.mIsStart) return; + this.mIsStart = false; + + VpnService.stopService(); + } + + updateState(status: boolean) { + Log.showInfo(TAG, `updateState ${status}`) + this.mVpnStatus = status; + AppStorage.Set('Vpn_Status', this.mVpnStatus); + } + + getVpnStatus() { + return this.mVpnStatus; + } + + getTintContentInfo(): TintContentInfo { + return this.mTintContentInfo; + } +} + +const sVpnVM = createOrGet(VpnVM, TAG); + +export default sVpnVM; diff --git a/features/vpncomponent/src/main/module.json5 b/features/vpncomponent/src/main/module.json5 new file mode 100755 index 00000000..a8d85d6c --- /dev/null +++ b/features/vpncomponent/src/main/module.json5 @@ -0,0 +1,10 @@ +{ + "module": { + "name": "vpncomponent", + "type": "har", + "deviceTypes": [ + "default" + ], + "uiSyntax": "ets" + } +} \ No newline at end of file diff --git a/features/vpncomponent/src/main/resources/base/element/float.json b/features/vpncomponent/src/main/resources/base/element/float.json new file mode 100755 index 00000000..7a5b2ecb --- /dev/null +++ b/features/vpncomponent/src/main/resources/base/element/float.json @@ -0,0 +1,16 @@ +{ + "float": [ + { + "name": "status_bar_vpn_width", + "value": "15vp" + }, + { + "name": "status_bar_vpn_height", + "value": "8vp" + }, + { + "name": "icon_component_opacity", + "value": "1" + } + ] +} \ No newline at end of file diff --git a/features/vpncomponent/src/main/resources/base/element/string.json b/features/vpncomponent/src/main/resources/base/element/string.json new file mode 100755 index 00000000..e55d72de --- /dev/null +++ b/features/vpncomponent/src/main/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "SystemUI" + } + ] +} diff --git a/features/vpncomponent/src/main/resources/base/media/ic_statusbar_vpn.svg b/features/vpncomponent/src/main/resources/base/media/ic_statusbar_vpn.svg new file mode 100755 index 00000000..27b59c0f --- /dev/null +++ b/features/vpncomponent/src/main/resources/base/media/ic_statusbar_vpn.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/features/vpncomponent/src/main/resources/en_US/element/string.json b/features/vpncomponent/src/main/resources/en_US/element/string.json new file mode 100755 index 00000000..e55d72de --- /dev/null +++ b/features/vpncomponent/src/main/resources/en_US/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "SystemUI" + } + ] +} diff --git a/features/vpncomponent/src/main/resources/phone/element/float.json b/features/vpncomponent/src/main/resources/phone/element/float.json new file mode 100755 index 00000000..a67d2996 --- /dev/null +++ b/features/vpncomponent/src/main/resources/phone/element/float.json @@ -0,0 +1,16 @@ +{ + "float": [ + { + "name": "status_bar_vpn_width", + "value": "15vp" + }, + { + "name": "status_bar_vpn_height", + "value": "8vp" + }, + { + "name": "icon_component_opacity", + "value": "0.7" + } + ] +} \ No newline at end of file diff --git a/features/vpncomponent/src/main/resources/zh_CN/element/string.json b/features/vpncomponent/src/main/resources/zh_CN/element/string.json new file mode 100755 index 00000000..e55d72de --- /dev/null +++ b/features/vpncomponent/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "SystemUI" + } + ] +} diff --git a/product/pc/statusbar/src/main/ets/pages/common/StatusbarConfig.ts b/product/pc/statusbar/src/main/ets/pages/common/StatusbarConfig.ts index cea41f24..c4661237 100644 --- a/product/pc/statusbar/src/main/ets/pages/common/StatusbarConfig.ts +++ b/product/pc/statusbar/src/main/ets/pages/common/StatusbarConfig.ts @@ -47,6 +47,7 @@ const statusbarConfig: StatusBarConfig = { LocalSlots: [ FASlotName.BATTERY, FASlotName.BLUETOOTH, + FASlotName.VPN, FASlotName.CAPSULE, FASlotName.CLOCK, FASlotName.LOCATION, diff --git a/product/pc/statusbar/src/main/resources/base/element/float.json b/product/pc/statusbar/src/main/resources/base/element/float.json index 4527d2f2..64a6d8de 100644 --- a/product/pc/statusbar/src/main/resources/base/element/float.json +++ b/product/pc/statusbar/src/main/resources/base/element/float.json @@ -155,6 +155,14 @@ { "name": "status_bar_airplane_height", "value": "24vp" + }, + { + "name": "status_bar_vpn_width", + "value": "15vp" + }, + { + "name": "status_bar_vpn_height", + "value": "8vp" } ] } \ No newline at end of file diff --git a/product/pc/statusbar/src/main/resources/base/media/ic_statusbar_vpn.svg b/product/pc/statusbar/src/main/resources/base/media/ic_statusbar_vpn.svg new file mode 100755 index 00000000..27b59c0f --- /dev/null +++ b/product/pc/statusbar/src/main/resources/base/media/ic_statusbar_vpn.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/product/phone/statusbar/src/main/ets/pages/common/StatusbarConfig.ts b/product/phone/statusbar/src/main/ets/pages/common/StatusbarConfig.ts index e3e95b21..42a9f4a3 100644 --- a/product/phone/statusbar/src/main/ets/pages/common/StatusbarConfig.ts +++ b/product/phone/statusbar/src/main/ets/pages/common/StatusbarConfig.ts @@ -38,6 +38,7 @@ const statusbarConfig: StatusBarConfig = { 'demosystemuisbrplugin', FASlotName.LOCATION, FASlotName.BLUETOOTH, + FASlotName.VPN, FASlotName.RING_MODE, FASlotName.NFC, FASlotName.AIR_PLANE, @@ -57,7 +58,8 @@ const statusbarConfig: StatusBarConfig = { FASlotName.NFC, FASlotName.SIGNAL, FASlotName.WIFI, - FASlotName.AIR_PLANE + FASlotName.AIR_PLANE, + FASlotName.VPN ], MetaSlots: [ { diff --git a/product/phone/statusbar/src/main/resources/base/element/float.json b/product/phone/statusbar/src/main/resources/base/element/float.json index ae845a8d..d71621ab 100644 --- a/product/phone/statusbar/src/main/resources/base/element/float.json +++ b/product/phone/statusbar/src/main/resources/base/element/float.json @@ -155,6 +155,14 @@ { "name": "status_bar_airplane_height", "value": "15vp" + }, + { + "name": "status_bar_vpn_width", + "value": "15vp" + }, + { + "name": "status_bar_vpn_height", + "value": "8vp" } ] } \ No newline at end of file diff --git a/product/phone/statusbar/src/main/resources/base/media/ic_statusbar_vpn.svg b/product/phone/statusbar/src/main/resources/base/media/ic_statusbar_vpn.svg new file mode 100755 index 00000000..27b59c0f --- /dev/null +++ b/product/phone/statusbar/src/main/resources/base/media/ic_statusbar_vpn.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/signature/OpenHarmony.p12 b/signature/OpenHarmony.p12 new file mode 100755 index 0000000000000000000000000000000000000000..60f9bb48e677960e9dd7257e9f55c80915417ecf GIT binary patch literal 8252 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 Date: Wed, 3 Apr 2024 15:25:24 +0800 Subject: [PATCH 2/3] add init state --- .../src/main/ets/default/model/VpnService.ts | 45 ++++++------------- .../src/main/ets/default/viewmodel/VpnVM.ts | 6 +-- 2 files changed, 17 insertions(+), 34 deletions(-) diff --git a/features/vpncomponent/src/main/ets/default/model/VpnService.ts b/features/vpncomponent/src/main/ets/default/model/VpnService.ts index 02a4cf9b..8267d370 100755 --- a/features/vpncomponent/src/main/ets/default/model/VpnService.ts +++ b/features/vpncomponent/src/main/ets/default/model/VpnService.ts @@ -14,11 +14,6 @@ */ import vpn from '@ohos.net.vpn'; -import Context from 'application/ServiceExtensionContext'; -import CommonEvent from '@ohos.commonEvent' -import connection from '@ohos.net.connection' -import { CommonEventSubscriber } from 'commonEvent/commonEventSubscriber'; -import { CommonEventData } from 'commonEvent/commonEventData'; import createOrGet from '../../../../../../../common/src/main/ets/default/SingleInstanceHelper'; import Log from '../../../../../../../common/src/main/ets/default/Log'; @@ -32,8 +27,6 @@ export interface VpnServiceListener { class VpnService { mIsStart = false; mListener: VpnServiceListener; - mSubscriber: CommonEventSubscriber; - mConnection: vpn.VpnConnection | undefined = undefined; startService(connection: vpn.VpnConnection | undefined = undefined) { if (this.mIsStart) return; @@ -41,43 +34,25 @@ class VpnService { Log.showInfo(TAG, `startService failed: ${connection}`); return; } - - this.mConnection = connection; this.mIsStart = true; - this.subscribe(); - } - - stopService() { - if (!this.mIsStart) return; - this.mIsStart = false; - this.mListener = null; - this.unsubscribe(); - this.unregisterListener(); - } - - async subscribe() { - this.unsubscribe(); try { // @ts-ignore - this.mConnection?.on('connect', (result, data) => { - Log.showInfo(TAG, `vpnConnection connect subscribe on = ${JSON.stringify(result)} data = ${data}`); - if (JSON.stringify(result).match("true")) { + connection?.getState((error, data: number) => { + Log.showInfo(TAG, `getState = ${JSON.stringify(error)} data = ${JSON.stringify(data)}`); + if (data == 1) { this.mListener?.updateState(true); - } else { + } else if (data == 2) { this.mListener?.updateState(false); } }) } catch (error) { - Log.showInfo(TAG, `vpnConnection connect subscribe on error = ${JSON.stringify(error)}`); + Log.showError(TAG, `getState error = ${JSON.stringify(error)}`); } - } - unsubscribe() { - if (!this.mSubscriber) return; try { // @ts-ignore - this.mConnection?.off('connect', (result, data) => { + connection?.on('connect', (result, data) => { Log.showInfo(TAG, `vpnConnection connect subscribe on = ${JSON.stringify(result)} data = ${data}`); if (JSON.stringify(result).match("true")) { this.mListener?.updateState(true); @@ -90,6 +65,14 @@ class VpnService { } } + stopService() { + if (!this.mIsStart) return; + this.mIsStart = false; + + this.mListener = null; + this.unregisterListener(); + } + registerListener(listener: VpnServiceListener) { this.mListener = listener; } diff --git a/features/vpncomponent/src/main/ets/default/viewmodel/VpnVM.ts b/features/vpncomponent/src/main/ets/default/viewmodel/VpnVM.ts index f1ca9689..5c6f1910 100755 --- a/features/vpncomponent/src/main/ets/default/viewmodel/VpnVM.ts +++ b/features/vpncomponent/src/main/ets/default/viewmodel/VpnVM.ts @@ -24,7 +24,7 @@ import { FASlotName } from '../../../../../../../common/src/main/ets/default/Con import AbilityManager from '../../../../../../../common/src/main/ets/default/abilitymanager/abilityManager'; import VpnService from '../model/VpnService'; -const TAG = 'VpnVMxxh'; +const TAG = 'VpnVM'; class VpnVM { mIsStart: boolean = false; @@ -40,17 +40,17 @@ class VpnVM { Log.showInfo(TAG, `constructor: ${context}`); return; } + AppStorage.SetOrCreate('Vpn_Status', this.mVpnStatus); } startVM() { if (this.mIsStart) return; + Log.showInfo(TAG, 'startVM') this.mIsStart = true; VpnService.registerListener(this); VpnService.startService(vpn.createVpnConnection(this.context)); - - Log.showInfo(TAG, 'startVM') } stopVM() { -- Gitee From 5cb50b3226870d37265de097fc8a55d8a48a192b Mon Sep 17 00:00:00 2001 From: "xiaohui.xie" Date: Thu, 11 Apr 2024 17:20:52 +0800 Subject: [PATCH 3/3] delete useless signature --- signature/OpenHarmony.p12 | Bin 8252 -> 0 bytes signature/OpenHarmonyApplication.cer | 44 ------------------ .../ac/f9d9d313be7042d4933e55eb1e8402f6 | 1 - .../ce/f7cfa6201a1644c5a0ba82969741aea2 | Bin 48 -> 0 bytes .../fd/0/4667d093e8bc476ca6f801834e185457 | 1 - .../fd/1/f5ed619c706f4a4db277ca6beb69de56 | 1 - .../fd/2/3aa4a14d432a48efb922fca804629ff8 | 1 - 7 files changed, 48 deletions(-) delete mode 100755 signature/OpenHarmony.p12 delete mode 100755 signature/OpenHarmonyApplication.cer delete mode 100755 signature/material/ac/f9d9d313be7042d4933e55eb1e8402f6 delete mode 100755 signature/material/ce/f7cfa6201a1644c5a0ba82969741aea2 delete mode 100755 signature/material/fd/0/4667d093e8bc476ca6f801834e185457 delete mode 100755 signature/material/fd/1/f5ed619c706f4a4db277ca6beb69de56 delete mode 100755 signature/material/fd/2/3aa4a14d432a48efb922fca804629ff8 diff --git a/signature/OpenHarmony.p12 b/signature/OpenHarmony.p12 deleted file mode 100755 index 60f9bb48e677960e9dd7257e9f55c80915417ecf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8252 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