diff --git a/common/src/main/ets/default/Constants.ts b/common/src/main/ets/default/Constants.ts index 3eb067e5e3599fe79487d0eb223a19dc6eb15ed6..f6f1d9cebb667505dfa3c3b5bd44c8f6674f7c9b 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 308e50b4bb13f0a1f81513cfd892ee3e857c7cb7..063e8556e2add1ded06771ad0eb278bf2f87536c 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 0000000000000000000000000000000000000000..181c64c74223a0e23199f6c6cdd58179bcb153ab --- /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 0000000000000000000000000000000000000000..387f6bed66f6383b37cedca95134ec2ce991ac7a --- /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 0000000000000000000000000000000000000000..e31f0d6a3973c0144f5cc33a69a95780e1b25509 --- /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 0000000000000000000000000000000000000000..5a7b8a702459ef7c28ea32693d1523a8411109a1 --- /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 0000000000000000000000000000000000000000..8267d3700a9f1e944d471f98a1d3215e1b045672 --- /dev/null +++ b/features/vpncomponent/src/main/ets/default/model/VpnService.ts @@ -0,0 +1,87 @@ +/* + * 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 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; + + startService(connection: vpn.VpnConnection | undefined = undefined) { + if (this.mIsStart) return; + if (connection == undefined || connection == null) { + Log.showInfo(TAG, `startService failed: ${connection}`); + return; + } + this.mIsStart = true; + + try { + // @ts-ignore + connection?.getState((error, data: number) => { + Log.showInfo(TAG, `getState = ${JSON.stringify(error)} data = ${JSON.stringify(data)}`); + if (data == 1) { + this.mListener?.updateState(true); + } else if (data == 2) { + this.mListener?.updateState(false); + } + }) + } catch (error) { + Log.showError(TAG, `getState error = ${JSON.stringify(error)}`); + } + + try { + // @ts-ignore + 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); + } else { + this.mListener?.updateState(false); + } + }) + } catch (error) { + Log.showInfo(TAG, `vpnConnection connect subscribe on error = ${JSON.stringify(error)}`); + } + } + + stopService() { + if (!this.mIsStart) return; + this.mIsStart = false; + + this.mListener = null; + this.unregisterListener(); + } + + 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 0000000000000000000000000000000000000000..8b143c78340c53b13fbc9a79dc004c72c2ea7760 --- /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 0000000000000000000000000000000000000000..5c6f19102e863f109301dd19d6d195ff4dc66027 --- /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 = 'VpnVM'; + +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; + Log.showInfo(TAG, 'startVM') + this.mIsStart = true; + + VpnService.registerListener(this); + VpnService.startService(vpn.createVpnConnection(this.context)); + } + + 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 0000000000000000000000000000000000000000..a8d85d6c6507d4b39d38bd1b3a46438d92ee47d7 --- /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 0000000000000000000000000000000000000000..7a5b2ecb2ae01175b0bd5096a627de139a98e082 --- /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 0000000000000000000000000000000000000000..e55d72de21220392039ab2b7dd576045dd02b72f --- /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 0000000000000000000000000000000000000000..27b59c0f0d927355d39c80c7c534917afdb2e88b --- /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 0000000000000000000000000000000000000000..e55d72de21220392039ab2b7dd576045dd02b72f --- /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 0000000000000000000000000000000000000000..a67d299632b0ec12877a8a4477267d504fb681c9 --- /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 0000000000000000000000000000000000000000..e55d72de21220392039ab2b7dd576045dd02b72f --- /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 cea41f242ec0feb75b565c51fe93291d86f87096..c46612374513ebd2de9e492cf2b9982f4607603b 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 4527d2f2affcb5b991dabf98ce661cc2d3665c61..64a6d8de9935a4946ec7b80844ac55e8d5f93c1d 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 0000000000000000000000000000000000000000..27b59c0f0d927355d39c80c7c534917afdb2e88b --- /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 e3e95b21418b153d5991cf70c635d0fb1b3ceda1..42a9f4a3b403ffcf7158e1a229975b43585758a0 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 ae845a8d0f8bd249bcd173901ca625c14d59514c..d71621abab54e4d8bf768343844d623c6b19cee2 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 0000000000000000000000000000000000000000..27b59c0f0d927355d39c80c7c534917afdb2e88b --- /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