diff --git a/.idea/libraries/Flutter_Plugins.xml b/.idea/libraries/Flutter_Plugins.xml index 53449da..b0f6971 100644 --- a/.idea/libraries/Flutter_Plugins.xml +++ b/.idea/libraries/Flutter_Plugins.xml @@ -1,8 +1,6 @@ - - - + diff --git a/CHANGELOG.md b/CHANGELOG.md index dbfd18c..5897269 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 2.1.1 +* feat: 适配 OHOS (HarmonyOS) 平台 + # 2.1.0 * fix: #4 diff --git a/README.md b/README.md index d31300c..2d4e1d0 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@ [![Pub](https://img.shields.io/pub/v/fk_user_agent.svg)](https://pub.dartlang.org/packages/fk_user_agent) [![Awesome Flutter](https://img.shields.io/badge/Awesome-Flutter-blue.svg?longCache=true&style=flat-square)]() -[![Awesome Flutter](https://img.shields.io/badge/Platform-Android_iOS-blue.svg?longCache=true&style=flat-square)]() +[![Awesome Flutter](https://img.shields.io/badge/Platform-Android_iOS_OHOS-blue.svg?longCache=true&style=flat-square)]() [![License](https://img.shields.io/badge/License-MIT-blue.svg)](/LICENSE) -Retrieve Android/iOS device user agents in Flutter. +Retrieve Android/iOS/OHOS device user agents in Flutter. ### Example user-agents: @@ -13,6 +13,7 @@ Retrieve Android/iOS device user agents in Flutter. | ------ | ---------- | ------------------ | | iOS | CFNetwork/897.15 Darwin/17.5.0 (iPhone/6s iOS/11.3) | Mozilla/5.0 (iPhone; CPU iPhone OS 11_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E217 | | Android | Dalvik/2.1.0 (Linux; U; Android 5.1.1; Android SDK built for x86 Build/LMY48X) | Mozilla/5.0 (Linux; Android 5.1.1; Android SDK built for x86 Build/LMY48X) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Mobile Safari/537.36 | +| OHOS | HarmonyOS/6.0.0 (phone; HUAWEI BLK-AL80; OpenHarmony-6.0.2.130) | Mozilla/5.0 (Phone; OpenHarmony 6.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 ArkWeb/6.0.0.130 Mobile | ### Additionally: @@ -40,6 +41,18 @@ Android version returns: - applicationBuildNumber - packageUserAgent +OHOS (HarmonyOS) version returns: +- systemName +- systemVersion +- packageName +- shortPackageName +- applicationName +- applicationVersion +- applicationBuildNumber +- packageUserAgent +- userAgent +- webViewUserAgent + ### Credits 👍 Based of https://github.com/j0j00/flutter_user_agent \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart index 003e17c..26a71eb 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'package:fk_user_agent/fk_user_agent.dart'; import 'package:flutter/material.dart'; @@ -14,7 +15,9 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State { - String _platformVersion = 'Unknown'; + String _userAgent = 'Unknown'; + String _webViewUserAgent = 'Unknown'; + String _properties = 'Unknown'; @override void initState() { @@ -27,13 +30,18 @@ class _MyAppState extends State { // Platform messages are asynchronous, so we initialize in an async method. Future initPlatformState() async { - String platformVersion; + String userAgent, webViewUserAgent, properties; // Platform messages may fail, so we use a try/catch PlatformException. try { - platformVersion = FkUserAgent.userAgent!; - print(platformVersion); + userAgent = FkUserAgent.userAgent ?? 'Unknown'; + webViewUserAgent = FkUserAgent.webViewUserAgent ?? 'Unknown'; + properties = + const JsonEncoder.withIndent(' ').convert(FkUserAgent.properties); + print('userAgent: $userAgent'); + print('webViewUserAgent: $webViewUserAgent'); + print('properties: $properties'); } on PlatformException { - platformVersion = 'Failed to get platform version.'; + userAgent = webViewUserAgent = properties = 'Failed to get platform version.'; } // If the widget was removed from the tree while the asynchronous platform @@ -42,7 +50,9 @@ class _MyAppState extends State { if (!mounted) return; setState(() { - _platformVersion = platformVersion; + _userAgent = userAgent; + _webViewUserAgent = webViewUserAgent; + _properties = properties; }); } @@ -54,7 +64,17 @@ class _MyAppState extends State { title: const Text('Plugin example app'), ), body: Center( - child: Text('Running on: $_platformVersion\n'), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('userAgent:'), + Text('$_userAgent\n'), + Text('webViewUserAgent:'), + Text('$_webViewUserAgent\n'), + Text('properties:'), + Text('$_properties\n'), + ], + ), ), ), ); diff --git a/example/ohos/.gitignore b/example/ohos/.gitignore new file mode 100644 index 0000000..6ca13b3 --- /dev/null +++ b/example/ohos/.gitignore @@ -0,0 +1,19 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +*.har +**/BuildProfile.ets +**/oh-package-lock.json5 + +**/src/main/resources/rawfile/flutter_assets/ +**/libs/arm64-v8a/libapp.so +**/libs/arm64-v8a/libflutter.so +**/libs/arm64-v8a/libvmservice_snapshot.so diff --git a/example/ohos/AppScope/app.json5 b/example/ohos/AppScope/app.json5 new file mode 100644 index 0000000..831ef95 --- /dev/null +++ b/example/ohos/AppScope/app.json5 @@ -0,0 +1,10 @@ +{ + "app": { + "bundleName": "io.flutterfastkit.fk_user_agent.fk_user_agent_ohos_example", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} \ No newline at end of file diff --git a/example/ohos/AppScope/resources/base/element/string.json b/example/ohos/AppScope/resources/base/element/string.json new file mode 100644 index 0000000..c428e52 --- /dev/null +++ b/example/ohos/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "fk_user_agent_ohos_example" + } + ] +} diff --git a/example/ohos/AppScope/resources/base/media/app_icon.png b/example/ohos/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000..ce307a8 Binary files /dev/null and b/example/ohos/AppScope/resources/base/media/app_icon.png differ diff --git a/example/ohos/build-profile.json5 b/example/ohos/build-profile.json5 new file mode 100644 index 0000000..b45e627 --- /dev/null +++ b/example/ohos/build-profile.json5 @@ -0,0 +1,41 @@ +{ + "app": { + "signingConfigs": [ + { + "name": "default", + "type": "HarmonyOS", + "material": { + "certpath": "C:\\Users\\dqh0334\\.ohos\\config\\default_ohos_DtFdLho8PHcsZv1JTh3N5Z4Pn8IUtW2fXtEFc3pCPi8=.cer", + "keyAlias": "debugKey", + "keyPassword": "0000001BB78A7ACB355051C4AAE8D45409334F5DEBA6697D194EE7C08ADDF30125B0EFB740A65DC00C14B9", + "profile": "C:\\Users\\dqh0334\\.ohos\\config\\default_ohos_DtFdLho8PHcsZv1JTh3N5Z4Pn8IUtW2fXtEFc3pCPi8=.p7b", + "signAlg": "SHA256withECDSA", + "storeFile": "C:\\Users\\dqh0334\\.ohos\\config\\default_ohos_DtFdLho8PHcsZv1JTh3N5Z4Pn8IUtW2fXtEFc3pCPi8=.p12", + "storePassword": "0000001B8CCB238AEB70CEFC976533B15145FE9AFCF304CCE62C50ACA5DA05153B465195C7691D30EF278B" + } + } + ], + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.0(12)", + "runtimeOS": "HarmonyOS" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/example/ohos/entry/.gitignore b/example/ohos/entry/.gitignore new file mode 100644 index 0000000..2795a1c --- /dev/null +++ b/example/ohos/entry/.gitignore @@ -0,0 +1,7 @@ + +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/example/ohos/entry/build-profile.json5 b/example/ohos/entry/build-profile.json5 new file mode 100644 index 0000000..ac329f6 --- /dev/null +++ b/example/ohos/entry/build-profile.json5 @@ -0,0 +1,14 @@ +{ + "apiType": 'stageMode', + "buildOption": { + }, + "targets": [ + { + "name": "default", + "runtimeOS": "HarmonyOS" + }, + { + "name": "ohosTest", + } + ] +} diff --git a/example/ohos/entry/hvigorfile.ts b/example/ohos/entry/hvigorfile.ts new file mode 100644 index 0000000..80e4ec5 --- /dev/null +++ b/example/ohos/entry/hvigorfile.ts @@ -0,0 +1,2 @@ +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +export { hapTasks } from '@ohos/hvigor-ohos-plugin'; diff --git a/example/ohos/entry/oh-package.json5 b/example/ohos/entry/oh-package.json5 new file mode 100644 index 0000000..15a8db2 --- /dev/null +++ b/example/ohos/entry/oh-package.json5 @@ -0,0 +1,12 @@ +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "fk_user_agent": "file:../har/fk_user_agent.har", + "@ohos/flutter_ohos": "har/flutter.har" + } +} \ No newline at end of file diff --git a/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets b/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000..5287fd3 --- /dev/null +++ b/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,9 @@ +import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos'; +import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant'; + +export default class EntryAbility extends FlutterAbility { + configureFlutterEngine(flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + GeneratedPluginRegistrant.registerWith(flutterEngine) + } +} diff --git a/example/ohos/entry/src/main/ets/pages/Index.ets b/example/ohos/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000..e84a394 --- /dev/null +++ b/example/ohos/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,23 @@ +import common from '@ohos.app.ability.common'; +import { FlutterPage } from '@ohos/flutter_ohos' + +let storage = LocalStorage.getShared() +const EVENT_BACK_PRESS = 'EVENT_BACK_PRESS' + +@Entry(storage) +@Component +struct Index { + private context = getContext(this) as common.UIAbilityContext + @LocalStorageLink('viewId') viewId: string = ""; + + build() { + Column() { + FlutterPage({ viewId: this.viewId }) + } + } + + onBackPress(): boolean { + this.context.eventHub.emit(EVENT_BACK_PRESS) + return true + } +} diff --git a/example/ohos/entry/src/main/ets/plugins/GeneratedPluginRegistrant.ets b/example/ohos/entry/src/main/ets/plugins/GeneratedPluginRegistrant.ets new file mode 100644 index 0000000..2ac094d --- /dev/null +++ b/example/ohos/entry/src/main/ets/plugins/GeneratedPluginRegistrant.ets @@ -0,0 +1,26 @@ +import { FlutterEngine, Log } from '@ohos/flutter_ohos'; +import FkUserAgentPlugin from 'fk_user_agent'; + +/** + * Generated file. Do not edit. + * This file is generated by the Flutter tool based on the + * plugins that support the Ohos platform. + */ + +const TAG = "GeneratedPluginRegistrant"; + +export class GeneratedPluginRegistrant { + + static registerWith(flutterEngine: FlutterEngine) { + try { + flutterEngine.getPlugins()?.add(new FkUserAgentPlugin()); + } catch (e) { + Log.e( + TAG, + "Tried to register plugins with FlutterEngine (" + + flutterEngine + + ") failed."); + Log.e(TAG, "Received exception while registering", e); + } + } +} diff --git a/example/ohos/entry/src/main/module.json5 b/example/ohos/entry/src/main/module.json5 new file mode 100644 index 0000000..5664f68 --- /dev/null +++ b/example/ohos/entry/src/main/module.json5 @@ -0,0 +1,39 @@ +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "phone" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:icon", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "requestPermissions": [ + {"name" : "ohos.permission.INTERNET"}, + ] + } +} diff --git a/example/ohos/entry/src/main/resources/base/element/color.json b/example/ohos/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000..3c71296 --- /dev/null +++ b/example/ohos/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/example/ohos/entry/src/main/resources/base/element/string.json b/example/ohos/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000..c6676af --- /dev/null +++ b/example/ohos/entry/src/main/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "fk_user_agent_ohos_example" + } + ] +} \ No newline at end of file diff --git a/example/ohos/entry/src/main/resources/base/media/icon.png b/example/ohos/entry/src/main/resources/base/media/icon.png new file mode 100644 index 0000000..ce307a8 Binary files /dev/null and b/example/ohos/entry/src/main/resources/base/media/icon.png differ diff --git a/example/ohos/entry/src/main/resources/base/profile/main_pages.json b/example/ohos/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000..1898d94 --- /dev/null +++ b/example/ohos/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/example/ohos/entry/src/main/resources/en_US/element/string.json b/example/ohos/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000..c6676af --- /dev/null +++ b/example/ohos/entry/src/main/resources/en_US/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "fk_user_agent_ohos_example" + } + ] +} \ No newline at end of file diff --git a/example/ohos/entry/src/main/resources/zh_CN/element/string.json b/example/ohos/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000..6fb96a3 --- /dev/null +++ b/example/ohos/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "fk_user_agent_ohos_example" + } + ] +} \ No newline at end of file diff --git a/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets b/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000..6017d37 --- /dev/null +++ b/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,35 @@ +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function abilityTest() { + describe('ActsAbilityTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }) + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }) + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }) + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + it('assertContain', 0, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + hilog.info(0x0000, 'testTag', '%{public}s', 'it begin'); + let a = 'abc'; + let b = 'b'; + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b); + expect(a).assertEqual(a); + }) + }) +} diff --git a/example/ohos/entry/src/ohosTest/ets/test/List.test.ets b/example/ohos/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000..e03f925 --- /dev/null +++ b/example/ohos/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,5 @@ +import abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} diff --git a/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts b/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts new file mode 100644 index 0000000..c4b8b48 --- /dev/null +++ b/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts @@ -0,0 +1,49 @@ +import hilog from '@ohos.hilog'; +import TestRunner from '@ohos.application.testRunner'; +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; + +var abilityDelegator = undefined +var abilityDelegatorArguments = undefined + +async function onAbilityCreateCallback() { + hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback'); +} + +async function addAbilityMonitorCallback(err: any) { + hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? ''); +} + +export default class OpenHarmonyTestRunner implements TestRunner { + constructor() { + } + + onPrepare() { + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare '); + } + + async onRun() { + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run'); + abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() + var testAbilityName = abilityDelegatorArguments.bundleName + '.TestAbility' + let lMonitor = { + abilityName: testAbilityName, + onAbilityCreate: onAbilityCreateCallback, + }; + abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback) + var cmd = 'aa start -d 0 -a TestAbility' + ' -b ' + abilityDelegatorArguments.bundleName + var debug = abilityDelegatorArguments.parameters['-D'] + if (debug == 'true') + { + cmd += ' -D' + } + hilog.info(0x0000, 'testTag', 'cmd : %{public}s', cmd); + abilityDelegator.executeShellCommand(cmd, + (err: any, d: any) => { + hilog.info(0x0000, 'testTag', 'executeShellCommand : err : %{public}s', JSON.stringify(err) ?? ''); + hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.stdResult ?? ''); + hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.exitCode ?? ''); + }) + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end'); + } +} diff --git a/example/ohos/entry/src/ohosTest/module.json5 b/example/ohos/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000..55725a9 --- /dev/null +++ b/example/ohos/entry/src/ohosTest/module.json5 @@ -0,0 +1,13 @@ +{ + "module": { + "name": "entry_test", + "type": "feature", + "deviceTypes": [ + "phone", + "tablet", + "2in1" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/example/ohos/entry/src/test/List.test.ets b/example/ohos/entry/src/test/List.test.ets new file mode 100644 index 0000000..957e75b --- /dev/null +++ b/example/ohos/entry/src/test/List.test.ets @@ -0,0 +1,5 @@ +import localUnitTest from './LocalUnit.test'; + +export default function testsuite() { + localUnitTest(); +} diff --git a/example/ohos/entry/src/test/LocalUnit.test.ets b/example/ohos/entry/src/test/LocalUnit.test.ets new file mode 100644 index 0000000..d9643bd --- /dev/null +++ b/example/ohos/entry/src/test/LocalUnit.test.ets @@ -0,0 +1,11 @@ +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; +import FkUserAgentPlugin from 'fk_user_agent'; + +const FkUserAgentPlugin: FkUserAgentPlugin = new FkUserAgentPlugin() +export default function localUnitTest() { + describe('localUnitTest', () => { + it('getUniqueClassName', 0, () => { + expect(fkUserAgentPlugin.getUniqueClassName()).not().assertNull(); + }); + }); +} diff --git a/example/ohos/hvigor/hvigor-config.json5 b/example/ohos/hvigor/hvigor-config.json5 new file mode 100644 index 0000000..941ccf5 --- /dev/null +++ b/example/ohos/hvigor/hvigor-config.json5 @@ -0,0 +1,5 @@ +{ + "modelVersion": "5.0.0", + "dependencies": { + } +} diff --git a/example/ohos/hvigorfile.ts b/example/ohos/hvigorfile.ts new file mode 100644 index 0000000..f3cb9f1 --- /dev/null +++ b/example/ohos/hvigorfile.ts @@ -0,0 +1,6 @@ +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/example/ohos/oh-package.json5 b/example/ohos/oh-package.json5 new file mode 100644 index 0000000..619fa86 --- /dev/null +++ b/example/ohos/oh-package.json5 @@ -0,0 +1,20 @@ +{ + "modelVersion": "5.0.0", + "name": "fk_user_agent_ohos_example", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "@ohos/flutter_ohos": "file:./har/flutter.har" + }, + "devDependencies": { + "@ohos/hypium": "1.0.6" + }, + "overrides": { + "@ohos/flutter_ohos": "file:./har/flutter.har", + "fk_user_agent": "file:./har/fk_user_agent.har", + "@ohos/flutter_module": "file:./entry" + } +} \ No newline at end of file diff --git a/example/pubspec.lock b/example/pubspec.lock deleted file mode 100644 index 6dd4be9..0000000 --- a/example/pubspec.lock +++ /dev/null @@ -1,161 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - async: - dependency: transitive - description: - name: async - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.5.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.1.0" - characters: - dependency: transitive - description: - name: characters - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.1.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.2.0" - clock: - dependency: transitive - description: - name: clock - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.1.0" - collection: - dependency: transitive - description: - name: collection - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.15.0" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.0.2" - fake_async: - dependency: transitive - description: - name: fake_async - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.2.0" - fk_user_agent: - dependency: "direct main" - description: - path: ".." - relative: true - source: path - version: "2.0.0" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - matcher: - dependency: transitive - description: - name: matcher - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.12.10" - meta: - dependency: transitive - description: - name: meta - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.3.0" - path: - dependency: transitive - description: - name: path - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.8.0" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.8.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.10.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.1.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.1.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.2.0" - test_api: - dependency: transitive - description: - name: test_api - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.2.19" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.3.0" - vector_math: - dependency: transitive - description: - name: vector_math - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.1.0" -sdks: - dart: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" diff --git a/ohos/.gitignore b/ohos/.gitignore new file mode 100644 index 0000000..b6d2839 --- /dev/null +++ b/ohos/.gitignore @@ -0,0 +1,10 @@ +/node_modules +/oh_modules +/.preview +/.idea +/build +/.cxx +/.test +/BuildProfile.ets +/oh-package-lock.json5 +/local.properties diff --git a/ohos/build-profile.json5 b/ohos/build-profile.json5 new file mode 100644 index 0000000..79961f9 --- /dev/null +++ b/ohos/build-profile.json5 @@ -0,0 +1,10 @@ +{ + "apiType": "stageMode", + "buildOption": { + }, + "targets": [ + { + "name": "default" + } + ] +} diff --git a/ohos/hvigorfile.ts b/ohos/hvigorfile.ts new file mode 100644 index 0000000..eba2944 --- /dev/null +++ b/ohos/hvigorfile.ts @@ -0,0 +1 @@ +export { harTasks } from '@ohos/hvigor-ohos-plugin'; diff --git a/ohos/index.ets b/ohos/index.ets new file mode 100644 index 0000000..cc3d056 --- /dev/null +++ b/ohos/index.ets @@ -0,0 +1,2 @@ +import FkUserAgentPlugin from './src/main/ets/components/plugin/FkUserAgentPlugin'; +export default FkUserAgentPlugin; diff --git a/ohos/oh-package.json5 b/ohos/oh-package.json5 new file mode 100644 index 0000000..8a4e320 --- /dev/null +++ b/ohos/oh-package.json5 @@ -0,0 +1,11 @@ +{ + "name": "fk_user_agent", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "index.ets", + "author": "", + "license": "Apache-2.0", + "dependencies": { + "@ohos/flutter_ohos": "file:./har/flutter.har" + } +} diff --git a/ohos/src/main/ets/components/plugin/FkUserAgentPlugin.ets b/ohos/src/main/ets/components/plugin/FkUserAgentPlugin.ets new file mode 100644 index 0000000..fca9987 --- /dev/null +++ b/ohos/src/main/ets/components/plugin/FkUserAgentPlugin.ets @@ -0,0 +1,131 @@ +import { + FlutterPlugin, + FlutterPluginBinding, + MethodCall, + MethodCallHandler, + MethodChannel, + MethodResult, +} from '@ohos/flutter_ohos'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { bundleManager } from '@kit.AbilityKit'; +import { webview } from '@kit.ArkWeb'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import common from '@ohos.app.ability.common'; +import deviceInfo from '@ohos.deviceInfo'; + +const DOMAIN = 0x0001; +const TAG = 'FkUserAgentPlugin'; + +/** FkUserAgentPlugin **/ +export default class FkUserAgentPlugin implements FlutterPlugin, MethodCallHandler { + private channel: MethodChannel | null = null; + private webEngineInitialized: boolean = false; + + constructor() { + } + + getUniqueClassName(): string { + return "FkUserAgentPlugin" + } + + onAttachedToEngine(binding: FlutterPluginBinding): void { + this.channel = new MethodChannel(binding.getBinaryMessenger(), "fk_user_agent"); + this.channel.setMethodCallHandler(this) + } + + onDetachedFromEngine(binding: FlutterPluginBinding): void { + if (this.channel != null) { + this.channel.setMethodCallHandler(null) + } + } + + onMethodCall(call: MethodCall, result: MethodResult): void { + if (call.method == "getProperties") { + this.getProperties(result) + } else { + result.notImplemented() + } + } + + /** + * 初始化 ArkWeb 引擎,以便通过静态方法 getDefaultUserAgent() 获取真实 User-Agent。 + * 全局只需调用一次。 + */ + private initWebEngine() { + if (this.webEngineInitialized) { + return; + } + try { + webview.WebviewController.initializeWebEngine(); + this.webEngineInitialized = true; + } catch (error) { + hilog.error(DOMAIN, TAG, `initializeWebEngine failed: ${(error as BusinessError).message}`); + } + } + + /** + * 通过 ArkWeb 静态方法获取系统默认 Web User-Agent。 + * getDefaultUserAgent() 从 API version 14 开始支持,无需绑定 Web 组件。 + * 如果调用失败则返回空字符串。 + */ + private getWebViewUserAgent(): string { + this.initWebEngine(); + try { + return webview.WebviewController.getDefaultUserAgent(); + } catch (error) { + hilog.error(DOMAIN, TAG, `getDefaultUserAgent failed: ${(error as BusinessError).message}`); + return ''; + } + } + + getProperties(result: MethodResult) { + // deviceInfo 提供的设备信息(无需权限) + const deviceType = deviceInfo.deviceType; //设备类型,如 phone/tablet/2in1 + const systemName = deviceInfo.distributionOSName; //发行版系统名称,如 OpenHarmony + const systemVersion = deviceInfo.distributionOSVersion; //发行版系统版本号,如 5.0.0 + const deviceBrand = deviceInfo.brand; //设备品牌 + const systemModel = deviceInfo.productModel; //认证型号 + const osFullName = deviceInfo.osFullName; //系统完整名称,如 OpenHarmony-6.0.2.130 + + bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION) + .then((bundleInfo) => { + const packageName = bundleInfo.name; + const shortPackageName = packageName.substring(packageName.lastIndexOf(".") + 1); + const applicationVersion = bundleInfo.versionName; //应用版本名 + const buildNumber = bundleInfo.versionCode.toString(); //应用版本号 + let applicationName = ""; + + try { + const context = getContext(this) as common.UIAbilityContext + applicationName = context.resourceManager.getStringSync(bundleInfo.appInfo.labelId); //应用名称 + } catch (error) { + hilog.error(DOMAIN, TAG, 'getStringSync failed'); + } + + // 系统名称/系统版本 (设备类型; 设备品牌 认证型号; 系统完整名称) + const userAgent = `${systemName}/${systemVersion} (${deviceType}; ${deviceBrand} ${systemModel}; ${osFullName})`; + const packageUserAgent = `${shortPackageName}/${applicationVersion}.${buildNumber} ${userAgent}`; + + // 通过 ArkWeb API 获取真实的 WebView User-Agent + const webViewUserAgent = this.getWebViewUserAgent(); + + const properties: Record = { + "systemName": "OpenHarmony", + "systemVersion": systemVersion, + "packageName": packageName, + "shortPackageName": shortPackageName, + "applicationName": applicationName, + "applicationVersion": applicationVersion, + "applicationBuildNumber": buildNumber, + "packageUserAgent": packageUserAgent, + "userAgent": userAgent, + "webViewUserAgent": webViewUserAgent, + }; + result.success(properties) + }) + .catch((error: BusinessError) => { + hilog.error(DOMAIN, TAG, `get bundleInfo failed, error is ${error}`); + result.error("BUNDLE_INFO_ERROR", "Failed to get bundle info", error.message); + }) + } +} diff --git a/ohos/src/main/module.json5 b/ohos/src/main/module.json5 new file mode 100644 index 0000000..e858214 --- /dev/null +++ b/ohos/src/main/module.json5 @@ -0,0 +1,10 @@ +{ + "module": { + "name": "fk_user_agent", + "type": "har", + "deviceTypes": [ + "default", + "tablet" + ] + } +} diff --git a/pubspec.lock b/pubspec.lock deleted file mode 100644 index 6c77e39..0000000 --- a/pubspec.lock +++ /dev/null @@ -1,147 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - async: - dependency: transitive - description: - name: async - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.5.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.1.0" - characters: - dependency: transitive - description: - name: characters - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.1.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.2.0" - clock: - dependency: transitive - description: - name: clock - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.1.0" - collection: - dependency: transitive - description: - name: collection - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.15.0" - fake_async: - dependency: transitive - description: - name: fake_async - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.2.0" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - matcher: - dependency: transitive - description: - name: matcher - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.12.10" - meta: - dependency: transitive - description: - name: meta - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.3.0" - path: - dependency: transitive - description: - name: path - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.8.0" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.8.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.10.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.1.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.1.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.2.0" - test_api: - dependency: transitive - description: - name: test_api - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.2.19" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.3.0" - vector_math: - dependency: transitive - description: - name: vector_math - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.1.0" -sdks: - dart: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index f3e54f6..7fd573b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fk_user_agent -description: Retrieve Android/iOS device user agents in Flutter. -version: 2.1.0 +description: Retrieve Android/iOS/OHOS device user agents in Flutter. +version: 2.1.1 homepage: https://github.com/flutter-fast-kit/fk_user_agent environment: @@ -31,6 +31,8 @@ flutter: pluginClass: FkUserAgentPlugin ios: pluginClass: FkUserAgentPlugin + ohos: + pluginClass: FkUserAgentPlugin # To add assets to your plugin package, add an assets section, like this: # assets: