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