Compare commits

...

26 Commits
dev ... master

Author SHA1 Message Date
fuenmao d008eebe64 加载等待红屏改为透明 2025-01-08 10:08:24 +08:00
fuenmao bc3b05403d 优化声网token刷新逻辑 2025-01-08 09:55:56 +08:00
fuenmao a019645104 1.7下午2-UI优化 2025-01-07 17:57:13 +08:00
fuenmao f5ecc0eb51 1.7下午1 2025-01-07 17:09:17 +08:00
fuenmao 44180f9924 1.7下午 2025-01-07 15:45:20 +08:00
fuenmao 976b76adae text优化 2025-01-07 12:35:22 +08:00
fuenmao bd635a53b6 背景色优化 2025-01-07 12:11:46 +08:00
fuenmao 01b4adeac7 点击区域优化2 2025-01-07 11:59:49 +08:00
fuenmao 9161c33741 点击区域优化1 2025-01-07 11:45:00 +08:00
fuenmao 87bea363b6 点击区域优化 2025-01-07 11:36:57 +08:00
fuenmao cf8f710a38 会话过期优化 2025-01-07 11:13:16 +08:00
fuenmao a6c8b475f4 1.会议号复制
2.账号登录密码框优化
3.会话过期优化
2025-01-07 11:11:27 +08:00
fuenmao 68288fdc00 更改桌面图标文件 2025-01-07 10:01:59 +08:00
fuenmao c98a1db1d3 1.6日第9条BUG解决 2025-01-07 09:48:00 +08:00
fuenmao 363fd0b77c Merge branch 'master' of https://gitea.23544.com/marking/WGShare.Mobile.Flutter
# Conflicts:
#	wgshare/pubspec.lock
2025-01-06 17:42:25 +08:00
fuenmao 1938e02207 1.6日2-8条、9条BUG解决,刷新token 2025-01-06 17:41:54 +08:00
豌杂 045c730899 完成启动图设置 2025-01-06 17:33:13 +08:00
豌杂 51442d27f3 补充提交 2025-01-06 17:15:04 +08:00
豌杂 0b5f41ee12 完成IOS图标 2025-01-06 17:13:00 +08:00
fuenmao 54594ade8c 判断是安卓才初始化下载 2025-01-06 10:37:02 +08:00
fuenmao 0ec8987acc 会议信息越界处理 2025-01-06 09:43:46 +08:00
fuenmao 307ba4f020 1.android开屏页
2.权限申请弹窗
3.后台下载
2025-01-02 14:35:08 +08:00
fuenmao 8f08223a18 权限和app内前台升级 2024-12-30 14:56:43 +08:00
fuenmao 8df34b0fa7 Merge branch 'dev' 2024-12-27 11:57:09 +08:00
fuenmao bb41882757 Merge branch 'dev'
# Conflicts:
#	wgshare/ios/Podfile
#	wgshare/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
#	wgshare/ios/Runner/Info.plist
#	wgshare/lib/pages/loginPage/agreement/agreement_read_view.dart
#	wgshare/pubspec.lock
2024-12-13 17:31:12 +08:00
豌杂 c5b55d8660 no message 2024-12-11 18:37:52 +08:00
86 changed files with 2840 additions and 1114 deletions

2
wgshare/.gitignore vendored
View File

@ -5,9 +5,11 @@
*.swp *.swp
.DS_Store .DS_Store
.atom/ .atom/
.build/
.buildlog/ .buildlog/
.history .history
.svn/ .svn/
.swiftpm/
migrate_working_dir/ migrate_working_dir/
.fvm/ .fvm/

View File

@ -1,14 +1,28 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 摄像头 -->
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<!-- 麦克风 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- 网络 -->
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<!-- 安装 -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<!-- 写权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- 读权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!-- 屏幕常亮 -->
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<!-- 高android版本读写权限 -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<application <application
android:label="智汇享" android:label="智汇享"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:enableOnBackInvokedCallback="true"> android:enableOnBackInvokedCallback="true"
android:usesCleartextTraffic="true">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
@ -36,47 +50,19 @@
<meta-data <meta-data
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:value="2" /> android:value="2" />
</application>
<!-- 访问电话状态 -->
<uses-permission
android:name="android.permission.READ_PHONE_STATE"/>
<!-- 允许全部网络访问 -->
<uses-permission
android:name="android.permission.INTERNET"/>
<!-- 获取网络信息状态 -->
<uses-permission
android:name="android.permission.ACCESS_NETWORK_STATE"/>
<!-- 获取当前WiFi接入的状态以及WLAN热点的信息 -->
<uses-permission
android:name="android.permission.ACCESS_WIFI_STATE"/>
<!-- 获取当前设备存储权限 -->
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<uses-permission
android:name="android.permission.ACTION_MANAGE_UNKNOWN_APP_SOURCES"/>
<!-- 这个权限用于app安装 -->
<uses-permission
android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<!-- 屏幕常亮权限 -->
<uses-permission
android:name="android.permission.WAKE_LOCK"/>
<uses-permission
android:name="android.permission.CAMERA"/>
<!-- Permissions options for the `access notification policy` group -->
<uses-permission
android:name="android.permission.ACCESS_NOTIFICATION_POLICY"/>
<!-- Permissions options for the `notification` group -->
<uses-permission
android:name="android.permission.POST_NOTIFICATIONS"/>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. --> <provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_path" />
</provider>
<!-- ${applicationId}会被替换为你的应用ID -->
<!-- android:resource="@xml/file_paths"指定了存放共享文件路径的资源文件。 -->
</application>
<queries> <queries>
<intent> <intent>
<action android:name="android.intent.action.PROCESS_TEXT"/> <action android:name="android.intent.action.PROCESS_TEXT"/>

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen --> <!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" /> <!--<item android:drawable="?android:colorBackground"/ >-->
<!-- You can insert your own image assets here --> <!-- You can insert your own image assets here -->
<!-- <item> <item>
<bitmap <bitmap
android:gravity="center" android:gravity="fill"
android:src="@mipmap/launch_image" /> android:src="@mipmap/launch_image" />
</item> --> </item>
</layer-list> </layer-list>

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen --> <!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" /> <!--<item android:drawable="@android:color/white"/ >-->
<!-- You can insert your own image assets here --> <!-- You can insert your own image assets here -->
<!-- <item> <item>
<bitmap <bitmap
android:gravity="center" android:gravity="fill"
android:src="@mipmap/launch_image" /> android:src="@mipmap/launch_image" />
</item> --> </item>
</layer-list> </layer-list>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 976 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<paths>
<root-path name="root" path="" />
<files-path name="files" path="" />
<cache-path name="cache" path="" />
<external-path name="external" path="" />
<external-files-path name="external_files" path="" />
<external-cache-path name="external_cache" path="" />
</paths>
</resources>

View File

@ -1,3 +1,4 @@
org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
android.suppressUnsupportedCompileSdk=34

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -7,6 +7,7 @@ import UIKit
_ application: UIApplication, _ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool { ) -> Bool {
Thread.sleep(forTimeInterval: 3) // 便2
GeneratedPluginRegistrant.register(with: self) GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions) return super.application(application, didFinishLaunchingWithOptions: launchOptions)
} }

View File

@ -2,115 +2,153 @@
"images": [ "images": [
{ {
"size": "20x20", "size": "20x20",
"idiom": "universal", "idiom": "iphone",
"filename": "icon-20@2x.png", "filename": "icon-20@2x.png",
"scale": "2x", "scale": "2x"
"platform": "ios"
}, },
{ {
"size": "20x20", "size": "20x20",
"idiom": "universal", "idiom": "iphone",
"filename": "icon-20@3x.png", "filename": "icon-20@3x.png",
"scale": "3x", "scale": "3x"
"platform": "ios"
}, },
{ {
"size": "29x29", "size": "29x29",
"idiom": "universal", "idiom": "iphone",
"filename": "icon-29.png",
"scale": "1x"
},
{
"size": "29x29",
"idiom": "iphone",
"filename": "icon-29@2x.png", "filename": "icon-29@2x.png",
"scale": "2x", "scale": "2x"
"platform": "ios"
}, },
{ {
"size": "29x29", "size": "29x29",
"idiom": "universal", "idiom": "iphone",
"filename": "icon-29@3x.png", "filename": "icon-29@3x.png",
"scale": "3x", "scale": "3x"
"platform": "ios"
},
{
"size": "38x38",
"idiom": "universal",
"filename": "icon-38@2x.png",
"scale": "2x",
"platform": "ios"
},
{
"size": "38x38",
"idiom": "universal",
"filename": "icon-38@3x.png",
"scale": "3x",
"platform": "ios"
}, },
{ {
"size": "40x40", "size": "40x40",
"idiom": "universal", "idiom": "iphone",
"filename": "icon-40@2x.png", "filename": "icon-40@2x.png",
"scale": "2x", "scale": "2x"
"platform": "ios"
}, },
{ {
"size": "40x40", "size": "40x40",
"idiom": "universal", "idiom": "iphone",
"filename": "icon-40@3x.png", "filename": "icon-40@3x.png",
"scale": "3x", "scale": "3x"
"platform": "ios" },
{
"size": "57x57",
"idiom": "iphone",
"filename": "icon-57.png",
"scale": "1x"
},
{
"size": "57x57",
"idiom": "iphone",
"filename": "icon-57@2x.png",
"scale": "2x"
}, },
{ {
"size": "60x60", "size": "60x60",
"idiom": "universal", "idiom": "iphone",
"filename": "icon-60@2x.png", "filename": "icon-60@2x.png",
"scale": "2x", "scale": "2x"
"platform": "ios"
}, },
{ {
"size": "60x60", "size": "60x60",
"idiom": "universal", "idiom": "iphone",
"filename": "icon-60@3x.png", "filename": "icon-60@3x.png",
"scale": "3x", "scale": "3x"
"platform": "ios"
}, },
{ {
"size": "64x64", "size": "20x20",
"idiom": "universal", "idiom": "ipad",
"filename": "icon-64@2x.png", "filename": "icon-20-ipad.png",
"scale": "2x", "scale": "1x"
"platform": "ios"
}, },
{ {
"size": "64x64", "size": "20x20",
"idiom": "universal", "idiom": "ipad",
"filename": "icon-64@3x.png", "filename": "icon-20@2x-ipad.png",
"scale": "3x", "scale": "2x"
"platform": "ios"
}, },
{ {
"size": "68x68", "size": "29x29",
"idiom": "universal", "idiom": "ipad",
"filename": "icon-68@2x.png", "filename": "icon-29-ipad.png",
"scale": "2x", "scale": "1x"
"platform": "ios" },
{
"size": "29x29",
"idiom": "ipad",
"filename": "icon-29@2x-ipad.png",
"scale": "2x"
},
{
"size": "40x40",
"idiom": "ipad",
"filename": "icon-40.png",
"scale": "1x"
},
{
"size": "40x40",
"idiom": "ipad",
"filename": "icon-40@2x.png",
"scale": "2x"
},
{
"size": "50x50",
"idiom": "ipad",
"filename": "icon-50.png",
"scale": "1x"
},
{
"size": "50x50",
"idiom": "ipad",
"filename": "icon-50@2x.png",
"scale": "2x"
},
{
"size": "72x72",
"idiom": "ipad",
"filename": "icon-72.png",
"scale": "1x"
},
{
"size": "72x72",
"idiom": "ipad",
"filename": "icon-72@2x.png",
"scale": "2x"
}, },
{ {
"size": "76x76", "size": "76x76",
"idiom": "universal", "idiom": "ipad",
"filename": "icon-76.png",
"scale": "1x"
},
{
"size": "76x76",
"idiom": "ipad",
"filename": "icon-76@2x.png", "filename": "icon-76@2x.png",
"scale": "2x", "scale": "2x"
"platform": "ios"
}, },
{ {
"size": "83.5x83.5", "size": "83.5x83.5",
"idiom": "universal", "idiom": "ipad",
"filename": "icon-83.5@2x.png", "filename": "icon-83.5@2x.png",
"scale": "2x", "scale": "2x"
"platform": "ios"
}, },
{ {
"size": "1024x1024", "size": "1024x1024",
"idiom": "universal", "idiom": "ios-marketing",
"filename": "icon-1024.png", "filename": "icon-1024.png",
"scale": "1x", "scale": "1x"
"platform": "ios"
} }
], ],
"info": { "info": {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 920 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 B

View File

@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<scenes> <scenes>
<!--View Controller--> <!--View Controller-->
@ -14,9 +16,11 @@
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/> <viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides> </layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"> <imageView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" misplaced="YES" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
<rect key="frame" x="2" y="93" width="375" height="759"/>
</imageView> </imageView>
</subviews> </subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -28,10 +32,10 @@
</viewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects> </objects>
<point key="canvasLocation" x="53" y="375"/> <point key="canvasLocation" x="121.37404580152672" y="185.91549295774649"/>
</scene> </scene>
</scenes> </scenes>
<resources> <resources>
<image name="LaunchImage" width="168" height="185"/> <image name="LaunchImage" width="375" height="667"/>
</resources> </resources>
</document> </document>

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina6_12" orientation="portrait" appearance="light"/> <device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<scenes> <scenes>
@ -18,12 +18,21 @@
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/> <rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="0Yj-sA-xJG">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view> </view>
</viewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects> </objects>
<point key="canvasLocation" x="9" y="-76"/> <point key="canvasLocation" x="8.3969465648854964" y="-76.056338028169023"/>
</scene> </scene>
</scenes> </scenes>
<resources>
<image name="LaunchImage" width="375" height="667"/>
</resources>
</document> </document>

View File

@ -33,7 +33,7 @@
<key>UIApplicationSupportsIndirectInputEvents</key> <key>UIApplicationSupportsIndirectInputEvents</key>
<true/> <true/>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string></string> <string>Main.storyboard</string>
<key>UIMainStoryboardFile</key> <key>UIMainStoryboardFile</key>
<string>Main</string> <string>Main</string>
<key>UIRequiresFullScreen</key> <key>UIRequiresFullScreen</key>

View File

@ -103,4 +103,10 @@ abstract class RetrofitClient {
@Query("enableCamera") bool enableCamera, @Query("enableCamera") bool enableCamera,
@Query("uid") String uid, @Query("uid") String uid,
); );
/// Token
@POST("/auth/refresh")
Future<BaseStructureResult<UserInfoEntity>> refreshToken(
@Query("refreshToken") String refreshToken,
);
} }

View File

@ -5,7 +5,7 @@ class RequestConfig {
// static const _devBaseUrl = "https://zyapitest.23544.com:16440"; // // static const _devBaseUrl = "https://zyapitest.23544.com:16440"; //
static const _devBaseUrl = "http://192.168.2.9:5192"; // static const _devBaseUrl = "http://192.168.2.9:5192"; //
// static const _proBaseUrl = "http://192.168.2.119:1091"; // // static const _proBaseUrl = "http://192.168.2.119:1091"; //
static const _proBaseUrl = "https://zyapi.23544.com/ipadapi"; // static const _proBaseUrl = "http://192.168.2.9:5192"; //
static const imgUrl = 'https://dpc-job-oss.23544.com/'; static const imgUrl = 'https://dpc-job-oss.23544.com/';
static RequestConfig? _instance; static RequestConfig? _instance;

View File

@ -0,0 +1,34 @@
import 'package:json_annotation/json_annotation.dart';
part 'app_updata_info_entity.g.dart';
@JsonSerializable(checked: true)
class AppUpdataInfoEntity extends Object{
@JsonKey(name: 'versionCode')
int versionCode;
@JsonKey(name: 'versionName')
String versionName;
@JsonKey(name: 'updateType')
int updateType;
@JsonKey(name: 'versionDescribe')
String versionDescribe;
@JsonKey(name: 'androidurl')
String androidurl;
@JsonKey(name: 'appStoreUrl')
String appStoreUrl;
@JsonKey(name: 'apkSavePath')
String apkSavePath;
AppUpdataInfoEntity(this.versionCode,this.versionName,this.updateType,this.versionDescribe,this.androidurl,this.appStoreUrl,this.apkSavePath);
factory AppUpdataInfoEntity.fromJson(Map<String, dynamic> srcJson) => _$AppUpdataInfoEntityFromJson(srcJson);
Map<String, dynamic> toJson() => _$AppUpdataInfoEntityToJson(this);
}

View File

@ -6,6 +6,8 @@ import 'package:dio/dio.dart';
import 'package:wgshare/common/api/retrofit_client.dart'; import 'package:wgshare/common/api/retrofit_client.dart';
import 'package:wgshare/common/config/request_config.dart'; import 'package:wgshare/common/config/request_config.dart';
import 'package:wgshare/common/store/user_store.dart'; import 'package:wgshare/common/store/user_store.dart';
import 'package:wgshare/utils/anti_shake_throttling.dart';
import 'package:wgshare/utils/routeUtil.dart';
import 'package:wgshare/utils/storage.dart'; import 'package:wgshare/utils/storage.dart';
import 'package:wgshare/utils/toast_utils.dart'; import 'package:wgshare/utils/toast_utils.dart';
import 'package:wgshare/routes/app_routes.dart'; import 'package:wgshare/routes/app_routes.dart';
@ -111,9 +113,9 @@ class ResponseHandle extends Interceptor {
((data['code'] != null && (data['code'] == 401 || data['code'] == '401')) || ((data['code'] != null && (data['code'] == 401 || data['code'] == '401')) ||
(data['Code'] != null && (data['Code'] == 401 || data['Code'] == '401'))); (data['Code'] != null && (data['Code'] == 401 || data['Code'] == '401')));
if (statusCode == 401 || flag) { if ((statusCode == 401 || flag) && RouteUtil.getRoute() != Routes.loginPage) {
Future.delayed(const Duration(seconds: 2), () { easyThrottle("toLogin",(){
StorageService.to.erase(); UserStore.to.erase();
getx.Get.offAllNamed(Routes.loginPage); getx.Get.offAllNamed(Routes.loginPage);
}); });
} }
@ -163,12 +165,12 @@ class TheError extends Interceptor {
switch (statusCode) { switch (statusCode) {
case 401: case 401:
message = '用户登录失效,请重新登录'; message = '用户登录失效,请重新登录';
if(RouteUtil.getRoute() != Routes.loginPage){
Future.delayed(const Duration(seconds: 2), () { easyThrottle("toLogin",(){
// UserStore.to.erase(); UserStore.to.erase();
StorageService.to.erase(); getx.Get.offAllNamed(Routes.loginPage);
getx.Get.offAllNamed(Routes.loginPage); });
}); }
break; break;
case 404: case 404:
message = '无效地址'; message = '无效地址';

View File

@ -2,10 +2,14 @@
enum AppStorageKey { enum AppStorageKey {
token(value: 'TOKEN', label: "登录用户的token"), token(value: 'TOKEN', label: "登录用户的token"),
refreshToken(value: 'REFRESHTOKEN', label: "刷新用的token"),
userInfo(value: 'USERINFO', label: "登录用户的基本信息"), userInfo(value: 'USERINFO', label: "登录用户的基本信息"),
account(value: 'ACCOUNT', label: "用户名"), account(value: 'ACCOUNT', label: "用户名"),
pwd(value: 'PWD', label: "密码"), pwd(value: 'PWD', label: "密码"),
loginType(value: 'LOGINTYPE', label: "登录类型"); loginType(value: 'LOGINTYPE', label: "登录类型"),
appUpDataInfo(value: 'APPUPDATAINFO', label: "app更新信息"),
isRefuseHomeCheckPermission(value: 'ISREFUSEHOMECHECKPERMISSION', label: "是否拒绝会议列表页请求文件访问权限"),
skipUpVersion(value: 'SKIPUPVERSION', label: "跳过此版本更新的版本号"),;
final String label; final String label;
final String value; final String value;

View File

@ -0,0 +1,58 @@
import 'package:get/get.dart';
import 'package:wgshare/common/mixins/request_tool_mixin.dart';
import 'package:wgshare/common/store/app_storage_key.dart';
import 'package:wgshare/utils/storage.dart';
import '../models/app_updata_info_entity.dart';
class BusinessStore extends GetxController with RequestToolMixin {
static BusinessStore get to => Get.find();
/// app版本更新信息
Rx<AppUpdataInfoEntity?> appUpdataInfoEntity = Rx(null);
/// 访
bool? isRefuseHomeCheckPermission = false;
///
int? skipUpVersion = 0;
BusinessStore init() {
isRefuseHomeCheckPermission = StorageService.to.read(AppStorageKey.isRefuseHomeCheckPermission.value);
skipUpVersion = StorageService.to.read(AppStorageKey.skipUpVersion.value);
try {
var appUpdataInfo = StorageService.to.read(AppStorageKey.appUpDataInfo.value);
if (appUpdataInfo != null) {
appUpdataInfoEntity.value = AppUpdataInfoEntity.fromJson(appUpdataInfo);
}
} catch (err) {
StorageService.to.remove(AppStorageKey.userInfo.value);
appUpdataInfoEntity.value = null;
}
return this;
}
/// app版本更新信息
void setAppUpdataInfo(AppUpdataInfoEntity info) {
appUpdataInfoEntity.value = info;
StorageService.to.write(AppStorageKey.appUpDataInfo.value, info);
}
/// 访
void setIsRefuseHomeCheckPermission(bool type) {
isRefuseHomeCheckPermission = type;
StorageService.to.write(AppStorageKey.isRefuseHomeCheckPermission.value, type);
}
///
void setSkipUpVersion(int code) {
skipUpVersion = code;
StorageService.to.write(AppStorageKey.skipUpVersion.value, code);
}
/// app版本更新信息
void erase() {
appUpdataInfoEntity.value = null;
StorageService.to.write(AppStorageKey.appUpDataInfo.value, null);
}
}

View File

@ -14,6 +14,9 @@ class UserStore extends GetxController with RequestToolMixin {
/// ///
String? token; String? token;
/// token
String? refreshToken;
/// ///
String? loginType; String? loginType;
@ -23,6 +26,7 @@ class UserStore extends GetxController with RequestToolMixin {
UserStore init() { UserStore init() {
loginType = StorageService.to.read(AppStorageKey.loginType.value); loginType = StorageService.to.read(AppStorageKey.loginType.value);
token = StorageService.to.read(AppStorageKey.token.value); token = StorageService.to.read(AppStorageKey.token.value);
refreshToken = StorageService.to.read(AppStorageKey.refreshToken.value);
try { try {
var userDetail = StorageService.to.read(AppStorageKey.userInfo.value); var userDetail = StorageService.to.read(AppStorageKey.userInfo.value);
if (userDetail != null) { if (userDetail != null) {
@ -45,6 +49,12 @@ class UserStore extends GetxController with RequestToolMixin {
StorageService.to.write(AppStorageKey.token.value, token); StorageService.to.write(AppStorageKey.token.value, token);
} }
/// refreshToken
void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
StorageService.to.write(AppStorageKey.refreshToken.value, refreshToken);
}
/// ///
void setLoginType(String loginType) { void setLoginType(String loginType) {
this.loginType = loginType; this.loginType = loginType;
@ -61,9 +71,14 @@ class UserStore extends GetxController with RequestToolMixin {
void erase() { void erase() {
userInfoEntity.value = null; userInfoEntity.value = null;
token = null; token = null;
refreshToken = null;
loginType = null; loginType = null;
StorageService.to.erase(); StorageService.to.write(AppStorageKey.userInfo.value, null);
StorageService.to.write(AppStorageKey.token.value, null);
StorageService.to.write(AppStorageKey.refreshToken.value, null);
StorageService.to.write(AppStorageKey.loginType.value, null);
// StorageService.to.erase();
} }
// //

View File

@ -1,3 +1,6 @@
import 'dart:io';
import 'package:al_downloader/al_downloader.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
@ -12,6 +15,7 @@ import 'package:wgshare/utils/utils.dart';
import 'common/config/app_config.dart'; import 'common/config/app_config.dart';
import 'common/config/colorUtils.dart'; import 'common/config/colorUtils.dart';
import 'common/store/business_store.dart';
import 'routes/app_pages.dart'; import 'routes/app_pages.dart';
import 'routes/app_routes.dart'; import 'routes/app_routes.dart';
@ -26,6 +30,12 @@ void main() async {
/// UserStore /// UserStore
Get.put<UserStore>(UserStore().init()); Get.put<UserStore>(UserStore().init());
Get.put<BusinessStore>(BusinessStore().init());
///
if(Platform.isAndroid){
ALDownloader.initialize();
}
runApp(const MyApp()); runApp(const MyApp());
} }

View File

@ -1,12 +1,20 @@
import 'package:al_downloader/al_downloader.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:signalr_core/signalr_core.dart'; import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:wgshare/common/models/common/base_structure_result.dart'; import 'package:wgshare/common/models/common/base_structure_result.dart';
import 'package:wgshare/common/mixins/request_tool_mixin.dart'; import 'package:wgshare/common/mixins/request_tool_mixin.dart';
import 'package:wgshare/common/store/business_store.dart';
import 'package:wgshare/common/store/user_store.dart'; import 'package:wgshare/common/store/user_store.dart';
import 'package:wgshare/utils/storage.dart';
import '../../common/models/meeting_room_item.dart'; import '../../common/models/meeting_room_item.dart';
import '../../routes/app_routes.dart';
import '../../utils/permission/PermissionService.dart';
import '../../utils/toast_utils.dart'; import '../../utils/toast_utils.dart';
import '../../view/public_dialog.dart';
import '../../view/upgrade/loadJson/hide_load_network_json.dart';
import 'home_state.dart'; import 'home_state.dart';
class HomeLogic extends GetxController with RequestToolMixin { class HomeLogic extends GetxController with RequestToolMixin {
@ -16,7 +24,38 @@ class HomeLogic extends GetxController with RequestToolMixin {
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
doHttpGetMeetingRoomList(state.pageIndex.value,state.pageSize.value); ///
HideCheckVersion hideCheckVersion = HideCheckVersion();
hideCheckVersion.doHttpHideCheckVersion();
///
/*PermissionService.checkPermission(permissionList: [Permission.manageExternalStorage]).then((value){
if(value == true){
hideCheckVersion.doHttpHideCheckVersion();
}else{
if(null == BusinessStore.to.isRefuseHomeCheckPermission || BusinessStore.to.isRefuseHomeCheckPermission == false){
publicDialog(Get.context!,
hideCancelBtn: true,
title: '请求权限说明',
describe:
'APP需要获取您的文件访问权限用以确保新版本下载后可以正常安装。',
leftBtnStr: '拒绝',
rightBtnStr: '同意',
leftBtnCallback: () {
BusinessStore.to.setIsRefuseHomeCheckPermission(true);
}, rightBtnCallback: () {
PermissionService.requestStoragePermissions().then((value){
if(value == true){
hideCheckVersion.doHttpHideCheckVersion();
}
});
});
}else{
hideCheckVersion.doHttpHideCheckVersion();
}
}
});*/
doHttpGetMeetingRoomList(state.pageIndex.value, state.pageSize.value);
} }
@override @override
@ -28,18 +67,19 @@ class HomeLogic extends GetxController with RequestToolMixin {
/// ///
Future<void> doHttpGetMeetingRoomList(int pageIndex, int pageSize) async { Future<void> doHttpGetMeetingRoomList(int pageIndex, int pageSize) async {
debugPrint("wgs输出===token${UserStore.to.token}"); debugPrint("wgs输出===token${UserStore.to.token}");
BaseStructureResult<MeetingRoomItem> res = await getClient().getMeetingRoomList(pageIndex,pageSize); BaseStructureResult<MeetingRoomItem> res = await getClient()
if(null != res.data){ .getMeetingRoomList(pageIndex, pageSize);
if(state.pageIndex == 1){ if (null != res.data) {
if (state.pageIndex == 1) {
state.meetingRooms.value = res.data!.items; state.meetingRooms.value = res.data!.items;
state.totalPage.value = res.data!.totalPage; state.totalPage.value = res.data!.totalPage;
state.total.value = res.data!.total; state.total.value = res.data!.total;
state.refreshController.refreshCompleted(resetFooterState: true); state.refreshController.refreshCompleted(resetFooterState: true);
}else{ } else {
if(state.pageIndex.value < state.totalPage.value){ if (state.pageIndex.value < state.totalPage.value) {
state.meetingRooms.value.addAll(res.data!.items); state.meetingRooms.value.addAll(res.data!.items);
state.refreshController.loadComplete(); state.refreshController.loadComplete();
}else{ } else {
state.refreshController.loadNoData(); state.refreshController.loadNoData();
} }
} }
@ -47,15 +87,15 @@ class HomeLogic extends GetxController with RequestToolMixin {
} }
/// ///
void onRefresh(){ void onRefresh() {
state.pageIndex.value = 1; state.pageIndex.value = 1;
doHttpGetMeetingRoomList(state.pageIndex.value,state.pageSize.value); doHttpGetMeetingRoomList(state.pageIndex.value, state.pageSize.value);
} }
/// ///
void onLoading(){ void onLoading() {
state.pageIndex.value += 1; state.pageIndex.value += 1;
doHttpGetMeetingRoomList(state.pageIndex.value,state.pageSize.value); doHttpGetMeetingRoomList(state.pageIndex.value, state.pageSize.value);
} }
/// 退 /// 退
@ -63,7 +103,8 @@ class HomeLogic extends GetxController with RequestToolMixin {
DateTime now = DateTime.now(); DateTime now = DateTime.now();
// 4, 退 // 4, 退
if (state.currentBackPressTime == null || if (state.currentBackPressTime == null ||
now.difference(state.currentBackPressTime!) > const Duration(seconds: 4)) { now.difference(state.currentBackPressTime!) >
const Duration(seconds: 4)) {
state.currentBackPressTime = now; state.currentBackPressTime = now;
ToastUtils.showInfo("再按一次退出"); ToastUtils.showInfo("再按一次退出");
return false; return false;
@ -73,5 +114,45 @@ class HomeLogic extends GetxController with RequestToolMixin {
return true; return true;
} }
///
void gotoMeetingRoom(BuildContext context, int index) {
PermissionService.checkPermission(
permissionList: [Permission.microphone, Permission.camera]).then((
value) {
if (value == true) {
Get.back();
Get.toNamed(Routes.meetingMainPage,
arguments: {
"roomNumber": state.meetingRooms.value[index].roomNum
});
} else {
publicDialog(
context,
hideCancelBtn: true,
title: '请求权限说明',
describe: '进入会议室需要获取您的麦克风以及摄像头权限,用以确保能够正常参会发言。',
leftBtnStr: '拒绝',
rightBtnStr: '同意',
leftBtnCallback: () {
Get.back();
Get.toNamed(Routes.meetingMainPage,
arguments: {
"roomNumber": state.meetingRooms.value[index].roomNum
});
},
rightBtnCallback: () {
PermissionService.requestPermissions().then((value) {
if (value == true) {
Get.back();
Get.toNamed(Routes.meetingMainPage,
arguments: {
"roomNumber": state.meetingRooms.value[index].roomNum
});
}
});
}
);
}
});
}
} }

View File

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -9,6 +10,7 @@ import 'package:wgshare/routes/app_routes.dart';
import 'package:wgshare/utils/cus_behavior.dart'; import 'package:wgshare/utils/cus_behavior.dart';
import 'package:wgshare/utils/toast_utils.dart'; import 'package:wgshare/utils/toast_utils.dart';
import '../../utils/color_util.dart'; import '../../utils/color_util.dart';
import '../../view/upgrade/loadJson/load_network_json.dart';
import 'home_logic.dart'; import 'home_logic.dart';
class HomePage extends StatefulWidget { class HomePage extends StatefulWidget {
@ -189,11 +191,7 @@ class HomePageState extends State<HomePage> with AutomaticKeepAliveClientMixin {
), ),
), ),
onTap: () { onTap: () {
Get.toNamed(Routes.meetingMainPage, logic.gotoMeetingRoom(context, index);
arguments: {
"roomNumber": state.meetingRooms
.value[index].roomNum
});
}, },
) )
], ],

View File

@ -3,6 +3,7 @@ import 'dart:convert';
import 'package:crypto/crypto.dart'; import 'package:crypto/crypto.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:wgshare/common/mixins/request_tool_mixin.dart'; import 'package:wgshare/common/mixins/request_tool_mixin.dart';
import 'package:wgshare/utils/device_info.dart'; import 'package:wgshare/utils/device_info.dart';
import 'package:wgshare/utils/toast_utils.dart'; import 'package:wgshare/utils/toast_utils.dart';
@ -10,22 +11,31 @@ import 'package:wgshare/utils/toast_utils.dart';
import '../../common/config/app_config.dart'; import '../../common/config/app_config.dart';
import '../../common/models/common/base_structure_result.dart'; import '../../common/models/common/base_structure_result.dart';
import '../../common/models/user_info_entity.dart'; import '../../common/models/user_info_entity.dart';
import '../../common/store/app_storage_key.dart';
import '../../common/store/user_store.dart'; import '../../common/store/user_store.dart';
import '../../routes/app_routes.dart'; import '../../routes/app_routes.dart';
import '../../utils/storage.dart'; import '../../utils/permission/PermissionService.dart';
import '../../view/public_dialog.dart';
import '../../view/upgrade/loadJson/hide_load_network_json.dart';
import '../../view/upgrade/loadJson/load_network_json.dart';
import 'login_state.dart'; import 'login_state.dart';
class LoginLogic extends GetxController with RequestToolMixin { class LoginLogic extends GetxController with RequestToolMixin {
final LoginState state = LoginState(); late LoginState state;
@override
void onInit() {
print("初始化登录init................");
state = LoginState();
HideCheckVersion hideCheckVersion = HideCheckVersion();
hideCheckVersion.doHttpHideCheckVersion();
super.onInit();
}
@override @override
void onClose() { void onClose() {
print("销毁onClose................");
super.onClose(); super.onClose();
state.passwordController.dispose();
state.userNameController.dispose();
state.meetingCodeController.dispose();
state.nickNameCodeController.dispose();
} }
/// ///
@ -40,23 +50,24 @@ class LoginLogic extends GetxController with RequestToolMixin {
/// ///
Future<void> doHttpLogin() async { Future<void> doHttpLogin() async {
if(state.userNameController.text.isEmpty){ if(state.userNameController!.text.isEmpty){
ToastUtils.showError("请输入账号"); ToastUtils.showError("请输入账号");
}else if(state.passwordController.text.isEmpty){ }else if(state.passwordController!.text.isEmpty){
ToastUtils.showError("请输入密码"); ToastUtils.showError("请输入密码");
}else if(state.checkAgreementBool.value != true){ }else if(state.checkAgreementBool.value != true){
ToastUtils.showError("请阅读并勾选相关协议"); ToastUtils.showError("请阅读并勾选相关协议");
}else{ }else{
BaseStructureResult<UserInfoEntity> res = await getClient().login(state.userNameController.text, md5.convert(utf8.encode(state.passwordController.text)).toString()); BaseStructureResult<UserInfoEntity> res = await getClient().login(state.userNameController!.text, md5.convert(utf8.encode(state.passwordController!.text)).toString());
if (null != res.data) { if (null != res.data) {
UserStore.to.setToken(res.data!.token); UserStore.to.setToken(res.data!.token);
UserStore.to.setRefreshToken(res.data!.refreshToken);
UserStore.to.setUserDetailInfo(res.data!); UserStore.to.setUserDetailInfo(res.data!);
UserStore.to.setLoginType(AppConfig.NORMAL_LOGIN); UserStore.to.setLoginType(AppConfig.NORMAL_LOGIN);
Get.offAllNamed(Routes.startPage); Get.offAllNamed(Routes.startPage);
state.userNameController.text = ""; state.userNameController!.text = "";
state.passwordController.text = ""; state.passwordController!.text = "";
state.meetingCodeController.text = ""; state.meetingCodeController!.text = "";
state.nickNameCodeController.text = ""; state.nickNameCodeController!.text = "";
state.checkAgreementBool.value = false; state.checkAgreementBool.value = false;
} }
} }
@ -64,25 +75,26 @@ class LoginLogic extends GetxController with RequestToolMixin {
/// ///
Future<void> doHttpAnonymousLogin() async { Future<void> doHttpAnonymousLogin() async {
if(state.meetingCodeController.text.isEmpty){ if(state.meetingCodeController!.text.isEmpty){
ToastUtils.showError("请输入会议号"); ToastUtils.showError("请输入会议号");
}else if(state.meetingCodeController.text.length != 8){ }else if(state.meetingCodeController!.text.length != 8){
ToastUtils.showError("请输入正确的会议号"); ToastUtils.showError("请输入正确的会议号");
}else if(state.nickNameCodeController.text.isEmpty){ }else if(state.nickNameCodeController!.text.isEmpty){
ToastUtils.showError("请输入昵称"); ToastUtils.showError("请输入昵称");
}else if(state.checkAgreementBool != true){ }else if(state.checkAgreementBool != true){
ToastUtils.showError("请阅读并勾选相关协议"); ToastUtils.showError("请阅读并勾选相关协议");
}else{ }else{
BaseStructureResult<UserInfoEntity> res = await getClient().anonLogin(await DeviceInfo.getDeviceId(),state.nickNameCodeController.text, state.meetingCodeController.text); BaseStructureResult<UserInfoEntity> res = await getClient().anonLogin(await DeviceInfo.getDeviceId(),state.nickNameCodeController!.text, state.meetingCodeController!.text);
if (null != res.data) { if (null != res.data) {
UserStore.to.setToken(res.data!.token); UserStore.to.setToken(res.data!.token);
UserStore.to.setRefreshToken(res.data!.refreshToken);
UserStore.to.setUserDetailInfo(res.data!); UserStore.to.setUserDetailInfo(res.data!);
UserStore.to.setLoginType(AppConfig.ANONYMOUS_LOGIN); UserStore.to.setLoginType(AppConfig.ANONYMOUS_LOGIN);
Get.toNamed(Routes.meetingMainPage, arguments: {"roomNumber": state.meetingCodeController.text}); Get.toNamed(Routes.meetingMainPage, arguments: {"roomNumber": state.meetingCodeController!.text});
state.userNameController.text = ""; state.userNameController!.text = "";
state.passwordController.text = ""; state.passwordController!.text = "";
state.meetingCodeController.text = ""; state.meetingCodeController!.text = "";
state.nickNameCodeController.text = ""; state.nickNameCodeController!.text = "";
state.checkAgreementBool.value = false; state.checkAgreementBool.value = false;
} }
} }
@ -90,16 +102,16 @@ class LoginLogic extends GetxController with RequestToolMixin {
/// ///
Future<void> doHttpCheckMeetingRoom() async { Future<void> doHttpCheckMeetingRoom() async {
if(state.meetingCodeController.text.isEmpty){ if(state.meetingCodeController!.text.isEmpty){
ToastUtils.showError("请输入会议号"); ToastUtils.showError("请输入会议号");
}else if(state.meetingCodeController.text.length != 8){ }else if(state.meetingCodeController!.text.length != 8){
ToastUtils.showError("请输入正确的会议号"); ToastUtils.showError("请输入正确的会议号");
}else if(state.nickNameCodeController.text.isEmpty){ }else if(state.nickNameCodeController!.text.isEmpty){
ToastUtils.showError("请输入昵称"); ToastUtils.showError("请输入昵称");
}else if(state.checkAgreementBool != true){ }else if(state.checkAgreementBool != true){
ToastUtils.showError("请阅读并勾选相关协议"); ToastUtils.showError("请阅读并勾选相关协议");
}else{ }else{
BaseStructureResult<bool> res = await getClient().checkout(state.meetingCodeController.text); BaseStructureResult<bool> res = await getClient().checkout(state.meetingCodeController!.text);
if (null != res.data) { if (null != res.data) {
if(res.data == true){ if(res.data == true){
doHttpAnonymousLogin(); doHttpAnonymousLogin();

View File

@ -3,12 +3,14 @@ import 'package:get/get.dart';
class LoginState { class LoginState {
LoginState() {} TextEditingController? userNameController;
TextEditingController? passwordController;
TextEditingController? meetingCodeController;
TextEditingController? nickNameCodeController;
LoginState() {
}
late TextEditingController userNameController = TextEditingController();
late TextEditingController passwordController = TextEditingController();
late TextEditingController meetingCodeController = TextEditingController();
late TextEditingController nickNameCodeController = TextEditingController();
/// 01 /// 01
late RxInt pageState = 0.obs; late RxInt pageState = 0.obs;

View File

@ -1,3 +1,4 @@
import 'package:al_downloader/al_downloader.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
@ -22,6 +23,10 @@ class _LoginPageState extends State<LoginPage> {
@override @override
void initState() { void initState() {
Utils.hideKeyboard(); Utils.hideKeyboard();
state.userNameController = TextEditingController();
state.passwordController = TextEditingController();
state.meetingCodeController = TextEditingController();
state.nickNameCodeController = TextEditingController();
super.initState(); super.initState();
} }
@ -39,7 +44,7 @@ class _LoginPageState extends State<LoginPage> {
decoration: const BoxDecoration( decoration: const BoxDecoration(
image: DecorationImage( image: DecorationImage(
image: AssetImage('assets/images/login_bg.png'), image: AssetImage('assets/images/login_bg.png'),
fit: BoxFit.fill, fit: BoxFit.cover,
), ),
), ),
child: Column( child: Column(
@ -195,12 +200,13 @@ class _LoginPageState extends State<LoginPage> {
color: ColorUtil.Color_153_153_153), color: ColorUtil.Color_153_153_153),
), ),
child: TextField( child: TextField(
obscureText: true,
controller: state.passwordController, controller: state.passwordController,
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
), ),
inputFormatters: <TextInputFormatter>[ inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly, FilteringTextInputFormatter.allow(RegExp("^[a-z0-9A-Z]+")),//
LengthLimitingTextInputFormatter(20) LengthLimitingTextInputFormatter(20)
// //
], ],
@ -375,6 +381,7 @@ class _LoginPageState extends State<LoginPage> {
), ),
), ),
onTap: () { onTap: () {
ALDownloader.cancelAll();
if (state.pageState.value == 0) { if (state.pageState.value == 0) {
logic.doHttpLogin(); logic.doHttpLogin();
} else { } else {
@ -391,4 +398,13 @@ class _LoginPageState extends State<LoginPage> {
), ),
); );
} }
@override
void dispose() {
state.passwordController?.dispose();
state.userNameController?.dispose();
state.meetingCodeController?.dispose();
state.nickNameCodeController?.dispose();
super.dispose();
}
} }

View File

@ -7,7 +7,9 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:signalr_core/signalr_core.dart'; import 'package:signalr_core/signalr_core.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import 'package:wgshare/common/store/user_store.dart'; import 'package:wgshare/common/store/user_store.dart';
import 'package:wgshare/utils/count_microphone_volume.dart'; import 'package:wgshare/utils/count_microphone_volume.dart';
import '../../common/config/request_config.dart'; import '../../common/config/request_config.dart';
@ -16,6 +18,8 @@ import '../../common/models/common/base_structure_result.dart';
import '../../common/models/meeting_room_info.dart'; import '../../common/models/meeting_room_info.dart';
import '../../common/models/meeting_room_msg.dart'; import '../../common/models/meeting_room_msg.dart';
import '../../common/models/meeting_room_user.dart'; import '../../common/models/meeting_room_user.dart';
import '../../common/store/business_store.dart';
import '../../routes/app_routes.dart';
import '../../utils/agora/AgoraUtil.dart'; import '../../utils/agora/AgoraUtil.dart';
import '../../utils/permission/PermissionService.dart'; import '../../utils/permission/PermissionService.dart';
import '../../utils/toast_utils.dart'; import '../../utils/toast_utils.dart';
@ -23,15 +27,258 @@ import 'meeting_main_state.dart';
class MeetingMainLogic extends GetxController with RequestToolMixin { class MeetingMainLogic extends GetxController with RequestToolMixin {
final MeetingMainState state = MeetingMainState(); final MeetingMainState state = MeetingMainState();
late RtcEngineEventHandler _rtcEngineEventHandler;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
_rtcEngineEventHandler = RtcEngineEventHandler(
//
onError: (ErrorCodeType err, String msg){
debugPrint("wgs输出===RTC-错误回调:${err}---${msg}");
},
//
onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
state.isJoinSuccess.value = true;
debugPrint("meeting流程====》3加入频道" );
debugPrint("wgs输出===RTC-自己加入会议室ID${connection.localUid}");
},
onRejoinChannelSuccess: (RtcConnection connection, int elapsed) {
state.isJoinSuccess.value = true;
debugPrint("meeting流程====》4加入频道" );
debugPrint("wgs输出===RTC-自己加入会议室ID${connection.localUid}");
},
//
onLeaveChannel: (RtcConnection connection, RtcStats stats) {
debugPrint("wgs输出===RTC-自己离开会议室ID${connection.localUid}");
},
// -
onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) {
debugPrint("wgs输出===RTC-远端用户或主播加入会议室用户或主机的ID$remoteUid");
},
// -
onUserOffline: (RtcConnection connection, int remoteUid,
UserOfflineReasonType reason) async {
//
if (remoteUid.toString().length == 9) {
for (var i = 0; i < state.cacheUsers.value.length; i++) {
if (remoteUid.toString() ==
state.cacheUsers.value[i].screenShareId) {
state.cacheUsers.value[i].enableShare = false;
}
}
}
update();
debugPrint("wgs输出===RTC-远端用户或主播离开会议室用户或主机的ID$remoteUid");
},
//
onAudioRoutingChanged: (int routing) {
debugPrint("wgs输出===RTC-音频路由切换:$routing");
state.communicationMode.value = routing;
if (routing == 1) {
debugPrint("wgs输出===RTC-音频路由切换为听筒");
} else if (routing == 3) {
debugPrint("wgs输出===RTC-音频路由切换为扬声器");
} else {
debugPrint("wgs输出===RTC-音频路由切换为外接设备");
}
},
//
onLocalAudioStateChanged: (RtcConnection connection,
LocalAudioStreamState state, LocalAudioStreamReason reason) {
debugPrint("wgs输出===RTC-音频采集开关:$state");
},
//
onRemoteVideoStateChanged: (RtcConnection connection,
int remoteUid,
RemoteVideoState remoteVideoState,
RemoteVideoStateReason remoteVideoStateReason,
int elapsed) {
debugPrint(
"wgs输出===RTC-远端视频状态发生改变ID-$remoteUid-状态-$remoteVideoStateReason");
if (remoteVideoStateReason ==
RemoteVideoStateReason.remoteVideoStateReasonRemoteMuted) {
//
if (remoteUid.toString().length != 9) {
//
if (remoteUid.toString() == state.remoteUid.value) {
//
doHttpGetTvAnchor();
}
} else {
//
}
}
},
//
onAudioVolumeIndication: (RtcConnection connection,
List<AudioVolumeInfo> speakers,
int speakerNumber,
int totalVolume) {
if (speakers.isNotEmpty) {
for (AudioVolumeInfo avi in speakers) {
if (avi.uid == 0) {
// debugPrint("wgs输出===RTC-本地用户音量提示:${avi.uid}--${avi.volume}");
for (MeetingRoomUser mru in state.cacheUsers.value) {
if (UserStore.to.userInfoEntity.value!.uid == mru.uid) {
mru.volume = CountMicrophoneVolume.getVolume(avi.volume!);
state.microphoneVolume.value =
CountMicrophoneVolume.getVolume(avi.volume!);
}
}
} else {
// debugPrint("wgs输出===RTC-远端用户音量提示:${avi.uid}--${avi.volume}");
for (MeetingRoomUser mru in state.cacheUsers.value) {
mru.volume = CountMicrophoneVolume.getVolume(avi.volume!);
if (avi.volume != 0) {
state.spokesman.value = mru.userName;
state.spokesmanVolume.value =
CountMicrophoneVolume.getVolume(avi.volume!);
} else {
state.spokesman.value = "";
state.spokesmanVolume.value = 0;
}
}
}
/*for (MeetingRoomUser mru in state.cacheUsers.value) {
//
if (avi.uid == 0) {
// debugPrint("wgs输出===RTC-用户音量提示(自己):${CountMicrophoneVolume.getVolume(avi.volume!)}");
mru.volume = CountMicrophoneVolume.getVolume(avi.volume!);
state.microphoneVolume.value =
CountMicrophoneVolume.getVolume(avi.volume!);
} else {
debugPrint("wgs输出===RTC-用户音量提示:${avi.uid}--${mru.uid}");
if (avi.uid.toString() == mru.uid) {
debugPrint("wgs输出===RTC-用户音量提示(远端用户):${speakers[0].uid}--${speakers[0].volume}");
mru.volume = CountMicrophoneVolume.getVolume(avi.volume!);
if (avi.volume != 0) {
state.spokesman.value = mru.userName;
state.spokesmanVolume.value =
CountMicrophoneVolume.getVolume(avi.volume!);
} else {
state.spokesman.value = "";
state.spokesmanVolume.value = 0;
}
}
}
}*/
}
}
},
//
onClientRoleChanged: (RtcConnection connection,
ClientRoleType oldRole,
ClientRoleType newRole,
ClientRoleOptions newRoleOptions) {
debugPrint(
"wgs输出===RTC-切换用户角色为:${newRole == ClientRoleType.clientRoleBroadcaster ? "主播" : "观众"}");
},
// token即将在30秒内过期回调
onTokenPrivilegeWillExpire: (RtcConnection connection, String token) {
doHttpGetMeetingToken(false);
},
// token已过期
onRequestToken: (RtcConnection connection){
doHttpGetMeetingToken(false);
},
//
onLocalVideoStateChanged: (VideoSourceType source,
LocalVideoStreamState state, LocalVideoStreamReason reason) {
debugPrint("wgs输出===RTC-本地视频状态发生改变:$source--$state--$reason");
},
//
onConnectionStateChanged: (RtcConnection connection,
ConnectionStateType stateType,
ConnectionChangedReasonType reason) {
debugPrint("wgs输出===RTC-网络连接状态发生改变:"
"会议室编号(${connection.channelId}"
"网络状态($stateType-${AgoraUtil.getConnectionStateChangedType(stateType)}"
"网络改变原因($reason-${AgoraUtil.getConnectionChangedReasonType(reason)}");
if(reason == ConnectionChangedReasonType.connectionChangedTokenExpired){
// 使token已过期
doHttpGetMeetingToken(false);
}else{
if (stateType == ConnectionStateType.connectionStateReconnecting) {
if (EasyLoading.isShow == false) {
ToastUtils.showLoadingToMask(
"网络故障,正在重连...", EasyLoadingMaskType.black);
}
} else if (stateType ==
ConnectionStateType.connectionStateConnected &&
reason ==
ConnectionChangedReasonType
.connectionChangedRejoinSuccess) {
ToastUtils.dismiss();
if (EasyLoading.isShow == false) {
ToastUtils.showSuccessToMask(
"重连成功!", EasyLoadingMaskType.black);
}
} else if (reason ==
ConnectionChangedReasonType.connectionChangedLost) {
// 15signalR Socket一致SDK继续重连
Future.delayed(const Duration(milliseconds: 15000), () {
ToastUtils.dismiss();
if (state.isShowOkAlertDialog.value == false) {
showOkAlertDialog(
context: Get.context!,
title: "提示",
message: "网络错误,请重新加入会议室",
okLabel: "确定",
barrierDismissible: false,
).then((OkCancelResult value) {
Get.back();
Get.back();
});
}
});
}
}
},
//
onFirstRemoteVideoFrame: (RtcConnection connection, int remoteUid,
int width, int height, int elapsed) async {
debugPrint("wgs输出===RTC-渲染器已接收首帧远端视频回调:${remoteUid}--${width}--${height}--${elapsed}");
},
//
onFirstRemoteVideoDecoded: (RtcConnection connection, int remoteUid,
int width, int height, int elapsed) {
debugPrint("wgs输出===RTC-已接收到远端视频并完成解码回调:${remoteUid}--${width}--${height}--${elapsed}");
}
//
/*onPermissionError: (PermissionType permissionType){
debugPrint("wgs输出===RTC-获取设备权限出错:$permissionType");
if(permissionType == PermissionType.screenCapture){
//
state.isOpenShare.value = false;
stopScreenCapture();
}
}*/
);
// //
var data = Get.arguments; var data = Get.arguments;
state.roomNumber.value = data["roomNumber"]; state.roomNumber.value = data["roomNumber"];
//
WakelockPlus.enable();
doHttpGetMeetingToken(true); doHttpGetMeetingToken(true);
} }
@ -43,6 +290,10 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
state.sendMsgController.dispose(); state.sendMsgController.dispose();
state.pageController.dispose(); state.pageController.dispose();
stopTime(); stopTime();
//
WakelockPlus.disable();
leaveMeetingToRtc(); leaveMeetingToRtc();
leaveMeetingToSocket(); leaveMeetingToSocket();
} }
@ -53,10 +304,13 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
await getClient().getMeetingToken(state.roomNumber.value); await getClient().getMeetingToken(state.roomNumber.value);
state.meetingToken.value = res.data!; state.meetingToken.value = res.data!;
if (isInit == true) { debugPrint("meeting流程====》1获取token成功" );
if (isInit) {
signalRSocket(); signalRSocket();
}else{ } else {
initRtc(); renewToken(state.meetingToken.value);
// initRtc();
} }
} }
@ -78,6 +332,7 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
getMeetingRoomAllUser(results[1].data as List<MeetingRoomUser>); getMeetingRoomAllUser(results[1].data as List<MeetingRoomUser>);
} finally { } finally {
ToastUtils.dismiss(); ToastUtils.dismiss();
ToastUtils.showSuccess("${state.isJoinSuccess}--${state.rctEngine.value}--${state.users.isNotEmpty}");
} }
} }
@ -150,6 +405,26 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
update(); update();
} }
/// grid切换大屏
void checkLargeScreen(String uid) {
ToastUtils.showSuccess("切换用户成功");
if (uid == UserStore.to.userInfoEntity.value!.uid) {
state.remoteUid.value = "0";
state.remoteAssistantUid.value = "0";
} else {
if (state.isSpeak.value == true && state.isOpenCamera.value == true) {
state.floating.value?.open(Get.context!);
}
state.remoteUid.value = uid;
state.remoteAssistantUid.value = uid;
}
changePageState(0);
Future.delayed(const Duration(milliseconds: 200), () {
changePageState(1);
});
update();
}
/// ------------------------------------------------------------------------------ /// ------------------------------------------------------------------------------
/// ///
Future<void> doHttpApplySpeak() async { Future<void> doHttpApplySpeak() async {
@ -179,6 +454,10 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
/// ------------------------------------------------------------------------------ /// ------------------------------------------------------------------------------
/// ///
Future<void> doHttpSetMicr(bool isOpenMicrophone) async { Future<void> doHttpSetMicr(bool isOpenMicrophone) async {
if (isOpenMicrophone == false) {
state.spokesman.value = "";
state.spokesmanVolume.value = 0;
}
await getClient().setMicr(state.roomNumber.value, isOpenMicrophone, await getClient().setMicr(state.roomNumber.value, isOpenMicrophone,
UserStore.to.userInfoEntity.value!.uid); UserStore.to.userInfoEntity.value!.uid);
} }
@ -200,6 +479,7 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
BaseStructureResult res = BaseStructureResult res =
await getClient().getTvAnchor(state.roomNumber.value); await getClient().getTvAnchor(state.roomNumber.value);
state.remoteUid.value = res.data!.toString(); state.remoteUid.value = res.data!.toString();
state.remoteAssistantUid.value = res.data!.toString();
debugPrint("wgs输出===:获取当前全员观看主播${res.data}"); debugPrint("wgs输出===:获取当前全员观看主播${res.data}");
if (res.data!.toString().length != 9) { if (res.data!.toString().length != 9) {
@ -223,9 +503,9 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
isOtherOpenCamera = true; isOtherOpenCamera = true;
} }
} }
if(isOtherOpenCamera == false){ if (isOtherOpenCamera == false) {
changePageState(0); changePageState(0);
}else{ } else {
state.remoteUid.value = ""; state.remoteUid.value = "";
changePageState(1); changePageState(1);
} }
@ -252,11 +532,13 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
} else { } else {
// ID为空并切换页面到视频状态 // ID为空并切换页面到视频状态
debugPrint("wgs输出===当前会议室不存在全员观看主播时设置主播ID为空并切换页面到视频状态"); debugPrint("wgs输出===当前会议室不存在全员观看主播时设置主播ID为空并切换页面到视频状态");
state.remoteUid.value = ""; // state.remoteUid.value = "";
state.remoteAssistantUid.value = "";
changePageState(1); changePageState(1);
} }
} }
} else { } else {
debugPrint("wgs输出===:当前全员观看是共享类型");
if (state.remoteUid.value == if (state.remoteUid.value ==
UserStore.to.userInfoEntity.value!.screenShareId) { UserStore.to.userInfoEntity.value!.screenShareId) {
// //
@ -268,6 +550,7 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
state.cacheUsers.value[i].enableShare = true; state.cacheUsers.value[i].enableShare = true;
} }
} }
changePageState(1);
} }
} }
update(); update();
@ -339,6 +622,8 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
await state.hubConnection.value?.start(); await state.hubConnection.value?.start();
debugPrint("meeting流程====》2socket链接成功" );
// //
state.hubConnection.value?.onreconnecting((error) { state.hubConnection.value?.onreconnecting((error) {
debugPrint("wgs输出===SignalR Socket-重连$error"); debugPrint("wgs输出===SignalR Socket-重连$error");
@ -362,12 +647,21 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
debugPrint("wgs输出===SignalR Socket-重连失败$error"); debugPrint("wgs输出===SignalR Socket-重连失败$error");
ToastUtils.dismiss(); ToastUtils.dismiss();
if (state.isNormaExit.value == false && debugPrint(
state.isShowOkAlertDialog.value == false) { "wgs输出===SignalR Socket-重连打印1${state.isNormaExit.value == false && state.isShowOkAlertDialog.value == false}");
debugPrint("wgs输出===SignalR Socket-重连打印2${null != UserStore.to.token}");
debugPrint(
"wgs输出===SignalR Socket-重连打印3${UserStore.to.token!.isNotEmpty}");
debugPrint(
"wgs输出===SignalR Socket-重连打印4${(state.isNormaExit.value == false && state.isShowOkAlertDialog.value == false) || null != UserStore.to.token || UserStore.to.token!.isNotEmpty}");
if ((state.isNormaExit.value == false &&
state.isShowOkAlertDialog.value == false) ||
(state.isNormaExit.value == false && null != UserStore.to.token)) {
showOkAlertDialog( showOkAlertDialog(
context: Get.context!, context: Get.context!,
title: "提示", title: "提示",
message: "网络错误,请重新加入会议室", message: "网络错误,请重新加入会议室1",
okLabel: "确定", okLabel: "确定",
barrierDismissible: false, barrierDismissible: false,
).then((OkCancelResult value) { ).then((OkCancelResult value) {
@ -391,7 +685,7 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
for (MeetingRoomUser mru in state.cacheUsers.value) { for (MeetingRoomUser mru in state.cacheUsers.value) {
if (mru.uid == meetingRoomUser.uid) { if (mru.uid == meetingRoomUser.uid) {
mru.roleId = meetingRoomUser.roleId; mru.roleId = meetingRoomUser.roleId;
if(state.defaulOpenState.value == 1){ if (state.defaulOpenState.value == 1) {
mru.enableMicr = true; mru.enableMicr = true;
} }
mru.isRoomManager = meetingRoomUser.isRoomManager; mru.isRoomManager = meetingRoomUser.isRoomManager;
@ -536,7 +830,9 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
} }
state.users.value = state.cacheUsers.value; state.users.value = state.cacheUsers.value;
update(); update();
doHttpGetTvAnchor(); if (listDynamic[0] != UserStore.to.userInfoEntity.value!.uid) {
doHttpGetTvAnchor();
}
debugPrint("wgs输出===Socket-远端用户或主播离开会议室:$jsonStr"); debugPrint("wgs输出===Socket-远端用户或主播离开会议室:$jsonStr");
}); });
@ -562,6 +858,20 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
}); });
}); });
/// 退
state.hubConnection.value?.on("ForceLogout", (e) {
// var jsonStr = const Utf8Decoder().convert(json.encode(uid).runes.toList());
var jsonStr = json.encode(e);
List listDynamic = jsonDecode(jsonStr);
debugPrint("wgs输出===Socket-强制退出:${listDynamic[0]}");
state.isNormaExit.value = true;
ToastUtils.showSuccess("${listDynamic[0]}");
Future.delayed(const Duration(seconds: 1), () {
UserStore.to.erase();
Get.offAllNamed(Routes.loginPage);
});
});
/// ------------------------------------------------------------------------------ /// ------------------------------------------------------------------------------
/// ///
state.hubConnection.value?.on("ReceiveMessage", (e) { state.hubConnection.value?.on("ReceiveMessage", (e) {
@ -572,10 +882,12 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
MeetingRoomMsg meetingRoomMsg = MeetingRoomMsg(list[0], list[1], list[2], MeetingRoomMsg meetingRoomMsg = MeetingRoomMsg(list[0], list[1], list[2],
0, formatDate(dateTime, [HH, ':', nn, ':', ss])); 0, formatDate(dateTime, [HH, ':', nn, ':', ss]));
state.meetingRoomMsgs.value.add(meetingRoomMsg); state.meetingRoomMsgs.value.add(meetingRoomMsg);
state.msgNum.value = state.msgNum.value += 1;
update(); update();
Future.delayed(const Duration(milliseconds: 100), () { Future.delayed(const Duration(milliseconds: 100), () {
state.chatController state.chatController
.jumpTo(state.chatController.position.maxScrollExtent); .jumpTo(state.chatController.position.maxScrollExtent);
state.msgNum.value = 0;
}); });
debugPrint("wgs输出===Socket-会议室接收消息:$jsonStr"); debugPrint("wgs输出===Socket-会议室接收消息:$jsonStr");
}); });
@ -588,13 +900,18 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
var listDynamic = jsonDecode(jsonStr); var listDynamic = jsonDecode(jsonStr);
MeetingRoomUser meetingRoomUser = MeetingRoomUser.fromJson(listDynamic); MeetingRoomUser meetingRoomUser = MeetingRoomUser.fromJson(listDynamic);
if (meetingRoomUser.enableCamera == true) { if (meetingRoomUser.enableCamera == true) {
debugPrint("wgs输出===Socket-用户单独开摄像头"); debugPrint("wgs输出===Socket-用户单独开摄像头${jsonStr}");
// //
for (MeetingRoomUser mru in state.cacheUsers.value) { for (MeetingRoomUser mru in state.cacheUsers.value) {
if (mru.uid == meetingRoomUser.uid) { if (mru.uid == meetingRoomUser.uid) {
mru.enableCamera = true; mru.enableCamera = true;
} }
debugPrint(
"wgs输出===Socket-用户单独开摄像头---${state.remoteUid.value}--${meetingRoomUser.uid}");
if (state.remoteUid.value == meetingRoomUser.uid) {
doHttpGetTvAnchor();
}
} }
if (meetingRoomUser.uid == UserStore.to.userInfoEntity.value!.uid) { if (meetingRoomUser.uid == UserStore.to.userInfoEntity.value!.uid) {
@ -630,14 +947,15 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
muteLocalVideoStream(true); muteLocalVideoStream(true);
// //
stopPreview(); stopPreview();
//
if (state.remoteUid.value == "0") {
state.remoteUid.value = "";
}
// //
if (state.floating.value?.isShowing == true) { if (state.floating.value?.isShowing == true) {
state.floating.value?.close(); state.floating.value?.close();
} }
//
if (state.remoteUid.value == "0") {
state.remoteUid.value = "";
doHttpGetTvAnchor();
}
} }
} }
update(); update();
@ -656,8 +974,12 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
/// ///
/// isAgain /// isAgain
Future<void> joinMeetingToSocket(bool isAgain) async { Future<void> joinMeetingToSocket(bool isAgain) async {
await state.hubConnection.value?.invoke("joinChannel", await state.hubConnection.value?.invoke("joinChannel", args: [
args: [state.roomNumber.value, false, false, false]); state.roomNumber.value,
state.isOpenMicrophone.value,
state.isOpenCamera.value,
state.isSpeak.value
]);
mergeFetch(isAgain); mergeFetch(isAgain);
/*if(isAgain == false){ /*if(isAgain == false){
mergeFetch(isAgain); mergeFetch(isAgain);
@ -696,11 +1018,10 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
/// ------------------------------------------------------------------------------SDK相关 /// ------------------------------------------------------------------------------SDK相关
/// SDK /// SDK
Future<void> initRtc() async { Future<void> initRtc() async {
//
PermissionService.requestPermissions();
// RtcEngine // RtcEngine
state.rctEngine.value = createAgoraRtcEngineEx(); await leaveMeetingToRtc();
state.rctEngine.value = createAgoraRtcEngine();
// RtcEngine channelProfileLiveBroadcasting // RtcEngine channelProfileLiveBroadcasting
await state.rctEngine.value?.initialize(RtcEngineContext( await state.rctEngine.value?.initialize(RtcEngineContext(
@ -708,10 +1029,12 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
channelProfile: ChannelProfileType.channelProfileLiveBroadcasting, channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
// logConfig:const LogConfig() // logConfig:const LogConfig()
)); ));
state.rctEngine.value?.registerEventHandler(_rtcEngineEventHandler);
// //
// //
enableVideo(); await enableVideo();
// //
await state.rctEngine.value?.setDefaultAudioRouteToSpeakerphone(false); await state.rctEngine.value?.setDefaultAudioRouteToSpeakerphone(false);
// //
@ -721,191 +1044,12 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
await state.rctEngine.value await state.rctEngine.value
?.setDualStreamMode(mode: SimulcastStreamMode.enableSimulcastStream); ?.setDualStreamMode(mode: SimulcastStreamMode.enableSimulcastStream);
joinMeetingToRtc(); WidgetsBinding.instance.addPostFrameCallback((_)=>joinMeetingToRtc());
//
state.rctEngine.value?.registerEventHandler(
RtcEngineEventHandler(
//
onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
state.isJoinSuccess.value = true;
debugPrint("wgs输出===RTC-自己加入会议室ID${connection.localUid}");
},
// // await joinMeetingToRtc();
onLeaveChannel: (RtcConnection connection, RtcStats stats) {
debugPrint("wgs输出===RTC-自己离开会议室ID${connection.localUid}");
},
// -
onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) {
debugPrint("wgs输出===RTC-远端用户或主播加入会议室用户或主机的ID$remoteUid");
},
// -
onUserOffline: (RtcConnection connection, int remoteUid,
UserOfflineReasonType reason) async {
//
if (remoteUid.toString().length == 9) {
for (var i = 0; i < state.cacheUsers.value.length; i++) {
if (remoteUid.toString() ==
state.cacheUsers.value[i].screenShareId) {
state.cacheUsers.value[i].enableShare = false;
}
}
}
update();
debugPrint("wgs输出===RTC-远端用户或主播离开会议室用户或主机的ID$remoteUid");
},
//
onAudioRoutingChanged: (int routing) {
debugPrint("wgs输出===RTC-音频路由切换:$routing");
state.communicationMode.value = routing;
if (routing == 1) {
debugPrint("wgs输出===RTC-音频路由切换为听筒");
} else if (routing == 3) {
debugPrint("wgs输出===RTC-音频路由切换为扬声器");
} else {
debugPrint("wgs输出===RTC-音频路由切换为外接设备");
}
},
//
onLocalAudioStateChanged: (RtcConnection connection,
LocalAudioStreamState state, LocalAudioStreamReason reason) {
debugPrint("wgs输出===RTC-音频采集开关:$state");
},
//
onRemoteVideoStateChanged: (RtcConnection connection,
int remoteUid,
RemoteVideoState remoteVideoState,
RemoteVideoStateReason remoteVideoStateReason,
int elapsed) {
debugPrint(
"wgs输出===RTC-远端视频状态发生改变ID-$remoteUid-状态-$remoteVideoStateReason");
if (remoteVideoStateReason ==
RemoteVideoStateReason.remoteVideoStateReasonRemoteMuted) {
//
if (remoteUid.toString().length != 9) {
//
if (remoteUid.toString() == state.remoteUid.value) {
//
doHttpGetTvAnchor();
}
} else {
//
}
}
},
//
onAudioVolumeIndication: (RtcConnection connection,
List<AudioVolumeInfo> speakers,
int speakerNumber,
int totalVolume) {
if (speakers.isNotEmpty) {
for (AudioVolumeInfo avi in speakers) {
for (MeetingRoomUser mru in state.cacheUsers.value) {
//
if (avi.uid == 0) {
//debugPrint("wgs输出===RTC-用户音量提示(自己):${CountMicrophoneVolume.getVolume(avi.volume!)}");
mru.volume = CountMicrophoneVolume.getVolume(avi.volume!);
state.microphoneVolume.value =
CountMicrophoneVolume.getVolume(avi.volume!);
} else {
if (avi.uid.toString() == mru.uid) {
//debugPrint("wgs输出===RTC-用户音量提示(远端用户):${speakers[0].uid}--${speakers[0].volume}");
mru.volume = CountMicrophoneVolume.getVolume(avi.volume!);
if (avi.volume != 0) {
state.spokesman.value = mru.userName;
state.spokesmanVolume.value =
CountMicrophoneVolume.getVolume(avi.volume!);
} else {
state.spokesman.value = "";
state.spokesmanVolume.value = 0;
}
}
}
}
}
}
},
//
onClientRoleChanged: (RtcConnection connection,
ClientRoleType oldRole,
ClientRoleType newRole,
ClientRoleOptions newRoleOptions) {
debugPrint(
"wgs输出===RTC-切换用户角色为:${newRole == ClientRoleType.clientRoleBroadcaster ? "主播" : "观众"}");
},
// token即将在30秒内过期回调
onTokenPrivilegeWillExpire: (RtcConnection connection, String token) {
doHttpGetMeetingToken(false);
},
//
onLocalVideoStateChanged: (VideoSourceType source,
LocalVideoStreamState state, LocalVideoStreamReason reason) {
debugPrint("wgs输出===RTC-本地视频状态发生改变:$source--$state--$reason");
},
//
onConnectionStateChanged: (RtcConnection connection,
ConnectionStateType stateType,
ConnectionChangedReasonType reason) {
debugPrint("wgs输出===RTC-网络连接状态发生改变:"
"会议室编号(${connection.channelId}"
"网络状态($stateType-${AgoraUtil.getConnectionStateChangedType(stateType)}"
"网络改变原因($reason-${AgoraUtil.getConnectionChangedReasonType(reason)}");
if (stateType == ConnectionStateType.connectionStateReconnecting) {
if (EasyLoading.isShow == false) {
ToastUtils.showLoadingToMask(
"网络故障,正在重连...", EasyLoadingMaskType.black);
}
} else if (stateType == ConnectionStateType.connectionStateConnected &&
reason ==
ConnectionChangedReasonType.connectionChangedRejoinSuccess) {
ToastUtils.dismiss();
if (EasyLoading.isShow == false) {
ToastUtils.showSuccessToMask("重连成功!", EasyLoadingMaskType.black);
}
} else if (reason ==
ConnectionChangedReasonType.connectionChangedLost) {
// 15signalR Socket一致SDK继续重连
Future.delayed(const Duration(milliseconds: 15000), () {
ToastUtils.dismiss();
if (state.isShowOkAlertDialog.value == false) {
showOkAlertDialog(
context: Get.context!,
title: "提示",
message: "网络错误,请重新加入会议室",
okLabel: "确定",
barrierDismissible: false,
).then((OkCancelResult value) {
Get.back();
Get.back();
});
}
});
}
}
//
/*onPermissionError: (PermissionType permissionType){
debugPrint("wgs输出===RTC-获取设备权限出错:$permissionType");
if(permissionType == PermissionType.screenCapture){
//
state.isOpenShare.value = false;
stopScreenCapture();
}
}*/
),
);
} }
/// ///
@ -933,6 +1077,7 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
/// ///
Future<void> leaveMeetingToRtc() async { Future<void> leaveMeetingToRtc() async {
state.rctEngine.value?.unregisterEventHandler(_rtcEngineEventHandler);
// //
await state.rctEngine.value?.leaveChannel(); await state.rctEngine.value?.leaveChannel();
// //
@ -986,4 +1131,9 @@ class MeetingMainLogic extends GetxController with RequestToolMixin {
Future<void> switchCamera() async { Future<void> switchCamera() async {
await state.rctEngine.value?.switchCamera(); await state.rctEngine.value?.switchCamera();
} }
/// Token
Future<void> renewToken(String token) async {
await state.rctEngine.value?.renewToken(token);
}
} }

View File

@ -44,6 +44,9 @@ class MeetingMainState {
/// token /// token
late RxString meetingToken = "".obs; late RxString meetingToken = "".obs;
///
late RxBool isCameraToMicrophonePermissions = false.obs;
/// ///
late Rx<MeetingRoomInfo?> meetingRoomInfo = Rx(null); late Rx<MeetingRoomInfo?> meetingRoomInfo = Rx(null);
/// showOkAlertDialog /// showOkAlertDialog
@ -76,6 +79,8 @@ class MeetingMainState {
late RxBool isOpenShare = false.obs; late RxBool isOpenShare = false.obs;
/// ID /// ID
late RxString remoteUid = "".obs; late RxString remoteUid = "".obs;
/// ID-
late RxString remoteAssistantUid = "".obs;
/// ///
late RxBool isJoinSuccess = false.obs; late RxBool isJoinSuccess = false.obs;
@ -87,13 +92,16 @@ class MeetingMainState {
/// ///
late RxList<MeetingRoomMsg> meetingRoomMsgs = RxList([]); late RxList<MeetingRoomMsg> meetingRoomMsgs = RxList([]);
///
late RxInt msgNum = 0.obs;
/// signalR /// signalR
late RxString serviceUrl = "http://192.168.2.9:5192/session-manage".obs; late RxString serviceUrl = "http://192.168.2.9:5192/session-manage".obs;
late Rx<HubConnection?> hubConnection = Rx(null); late Rx<HubConnection?> hubConnection = Rx(null);
/// ///
final String appId = "4a4f7be64fa1404ebda74784fe9ac381"; final String appId = "4a4f7be64fa1404ebda74784fe9ac381";
late Rx<RtcEngineEx?> rctEngine = Rx(null); Rx<RtcEngine?> rctEngine = Rx<RtcEngine?>(null);
/// ///
late RxBool isAutoSubscribeVideo = false.obs; late RxBool isAutoSubscribeVideo = false.obs;
/// ///

File diff suppressed because it is too large Load Diff

View File

@ -57,56 +57,58 @@ class MeetingMainVoiceComponent extends StatelessWidget {
), ),
SizedBox(height: 6.h), SizedBox(height: 6.h),
users[index].enableMicr == true users[index].enableMicr == true
? Row( ? Text.rich(
mainAxisAlignment: MainAxisAlignment.center, maxLines: 1,
crossAxisAlignment: CrossAxisAlignment.center, overflow: TextOverflow.ellipsis,
children: [ TextSpan(
Container( children: [
width: 20.w, WidgetSpan(
height: 20.h, child: Container(
child: LiquidCustomProgressIndicator( width: 20.w,
value: users[index].volume ?? 0.0, height: 20.h,
valueColor: const AlwaysStoppedAnimation(ColorUtil.Color_2_177_136), child: LiquidCustomProgressIndicator(
backgroundColor: ColorUtil.Color_255_255_255, value: users[index].volume ?? 0.0,
direction: Axis.vertical, valueColor: const AlwaysStoppedAnimation(ColorUtil.Color_2_177_136),
shapePath: ViewSvgPath.getMicrpphonePath() backgroundColor: ColorUtil.Color_255_255_255,
direction: Axis.vertical,
shapePath: ViewSvgPath.getMicrpphonePath()
),
)
), ),
), TextSpan(
SizedBox( text: users[index].userName,
width: 70,
child: Text(
users[index].userName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: ColorUtil.Color_255_255_255), color: ColorUtil.Color_255_255_255),
), ),
)
],
)
: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.asset(
'assets/images/meeting_main_microphone_open.png',
width: 20.w,
height: 20.h,
),
SizedBox(
width: 70,
child: Text(
users[index].userName,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 12.sp,
color: ColorUtil.Color_255_255_255),
),
)
], ],
), ),
textAlign: TextAlign.left,
)
: Text.rich(
maxLines: 1,
overflow: TextOverflow.ellipsis,
TextSpan(
children: [
WidgetSpan(
child: Image.asset(
'assets/images/meeting_main_microphone_open.png',
width: 20.w,
height: 20.h,
)
),
TextSpan(
text: users[index].userName,
style: TextStyle(
fontSize: 12.sp,
color: ColorUtil.Color_255_255_255),
),
],
),
textAlign: TextAlign.left,
)
], ],
); );
})), })),

View File

@ -7,10 +7,4 @@ import 'user_state.dart';
class UserLogic extends GetxController with RequestToolMixin { class UserLogic extends GetxController with RequestToolMixin {
final UserState state = UserState(); final UserState state = UserState();
/// 退
void logout(){
UserStore.to.erase();
Get.toNamed(Routes.loginPage);
}
} }

View File

@ -96,7 +96,7 @@ class UserPageState extends State<UserPage> {
), ),
Container( Container(
padding: const EdgeInsets.only(left: 16, right: 16), padding: const EdgeInsets.only(left: 16, right: 16),
margin: const EdgeInsets.only(top: 20), margin: const EdgeInsets.only(top: 30),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [

View File

@ -73,6 +73,8 @@ class ColorUtil {
static const Color_255_69_69 = Color.fromRGBO(255, 69, 69, 1); static const Color_255_69_69 = Color.fromRGBO(255, 69, 69, 1);
static const Color_84_84_84 = Color.fromRGBO(84, 84, 84, 1);
/// ///
/// hex, 0xffffff, /// hex, 0xffffff,
/// alpha, [0.0,1.0] /// alpha, [0.0,1.0]

View File

@ -17,9 +17,16 @@ class AndroidPermissionHandler {
await requestMicrophonePermission(); await requestMicrophonePermission();
} }
Future<void> requestCameraToMicrophonePermissions() async { Future<bool> requestCameraToMicrophonePermissions() async {
await requestCameraPermission(); var isCameraPermission = false;
await requestMicrophonePermission(); var isMicrophonePermissio = false;
if(await requestCameraPermission() == true){
isCameraPermission = true;
}
if(await requestMicrophonePermission() == true){
isMicrophonePermissio = true;
}
return isCameraPermission == true && isMicrophonePermissio == true;
} }
Future<void> requestLocationPermission() async { Future<void> requestLocationPermission() async {
@ -58,10 +65,12 @@ class AndroidPermissionHandler {
} }
} }
Future<void> requestCameraPermission() async { Future<bool> requestCameraPermission() async {
var isCameraPermission = false;
PermissionStatus status = await Permission.camera.request(); PermissionStatus status = await Permission.camera.request();
if (status.isGranted) { if (status.isGranted) {
print("Android: 摄像头权限已授予"); print("Android: 摄像头权限已授予");
isCameraPermission = true;
} else if (status.isPermanentlyDenied) { } else if (status.isPermanentlyDenied) {
print("Android: 摄像头权限被永久拒绝,请前往设置开启"); print("Android: 摄像头权限被永久拒绝,请前往设置开启");
ToastUtils.showError("权限被永久拒绝,请前往设置开启!"); ToastUtils.showError("权限被永久拒绝,请前往设置开启!");
@ -70,17 +79,21 @@ class AndroidPermissionHandler {
print("Android: 摄像头权限被拒绝"); print("Android: 摄像头权限被拒绝");
ToastUtils.showError("权限被拒绝,可能会导致相关功能不可用!"); ToastUtils.showError("权限被拒绝,可能会导致相关功能不可用!");
} }
return isCameraPermission;
} }
Future<void> requestStoragePermission() async { Future<bool> requestStoragePermission() async {
PermissionStatus status = await Permission.storage.request(); var isStoragePermission = false;
PermissionStatus status = await Permission.manageExternalStorage.request();
if (status.isGranted) { if (status.isGranted) {
print("Android: 存储权限已授予"); print("Android: 存储权限已授予");
isStoragePermission = true;
} else if (status.isPermanentlyDenied) { } else if (status.isPermanentlyDenied) {
print("Android: 存储权限被永久拒绝,请前往设置开启"); print("Android: 存储权限被永久拒绝,请前往设置开启");
} else { } else {
print("Android: 存储权限被拒绝"); print("Android: 存储权限被拒绝");
} }
return isStoragePermission;
} }
Future<void> requestNotificationPermission() async { Future<void> requestNotificationPermission() async {
@ -105,10 +118,12 @@ class AndroidPermissionHandler {
} }
} }
static Future<void> requestMicrophonePermission() async { static Future<bool> requestMicrophonePermission() async {
var isMicrophonePermission = false;
PermissionStatus status = await Permission.microphone.request(); PermissionStatus status = await Permission.microphone.request();
if (status.isGranted) { if (status.isGranted) {
print("Android: 麦克风权限已授予"); print("Android: 麦克风权限已授予");
isMicrophonePermission = true;
} else if (status.isPermanentlyDenied) { } else if (status.isPermanentlyDenied) {
print("Android: 麦克风权限被永久拒绝,请前往设置开启"); print("Android: 麦克风权限被永久拒绝,请前往设置开启");
ToastUtils.showError("权限被永久拒绝,请前往设置开启!"); ToastUtils.showError("权限被永久拒绝,请前往设置开启!");
@ -117,5 +132,6 @@ class AndroidPermissionHandler {
print("Android: 麦克风限被拒绝"); print("Android: 麦克风限被拒绝");
ToastUtils.showError("权限被拒绝,可能会导致相关功能不可用!"); ToastUtils.showError("权限被拒绝,可能会导致相关功能不可用!");
} }
return isMicrophonePermission;
} }
} }

View File

@ -13,9 +13,16 @@ class IosPermissionHandler {
await requestMicrophonePermission(); await requestMicrophonePermission();
} }
Future<void> requestCameraToMicrophonePermissions() async { Future<bool> requestCameraToMicrophonePermissions() async {
await requestCameraPermission(); var isCameraPermission = false;
await requestMicrophonePermission(); var isMicrophonePermissio = false;
if(await requestCameraPermission() == true){
isCameraPermission = true;
}
if(await requestMicrophonePermission() == true){
isMicrophonePermissio = true;
}
return isCameraPermission == true && isMicrophonePermissio == true;
} }
Future<void> requestLocationPermission() async { Future<void> requestLocationPermission() async {
@ -38,10 +45,12 @@ class IosPermissionHandler {
} }
} }
Future<void> requestCameraPermission() async { Future<bool> requestCameraPermission() async {
var isCameraPermission = false;
PermissionStatus status = await Permission.camera.request(); PermissionStatus status = await Permission.camera.request();
if (status.isGranted) { if (status.isGranted) {
print("iOS: 摄像头权限已授予"); print("iOS: 摄像头权限已授予");
isCameraPermission = true;
} else if (status.isPermanentlyDenied) { } else if (status.isPermanentlyDenied) {
print("iOS: 摄像头权限被永久拒绝,请前往设置开启"); print("iOS: 摄像头权限被永久拒绝,请前往设置开启");
ToastUtils.showError("权限被永久拒绝,请前往设置开启!"); ToastUtils.showError("权限被永久拒绝,请前往设置开启!");
@ -50,17 +59,21 @@ class IosPermissionHandler {
print("iOS: 摄像头权限被拒绝"); print("iOS: 摄像头权限被拒绝");
ToastUtils.showError("权限被拒绝,可能会导致相关功能不可用!"); ToastUtils.showError("权限被拒绝,可能会导致相关功能不可用!");
} }
return isCameraPermission;
} }
Future<void> requestStoragePermission() async { Future<bool> requestStoragePermission() async {
var isStoragePermission = false;
PermissionStatus status = await Permission.photos.request(); PermissionStatus status = await Permission.photos.request();
if (status.isGranted) { if (status.isGranted) {
print("iOS: 存储权限已授予"); print("iOS: 存储权限已授予");
isStoragePermission = true;
} else if (status.isPermanentlyDenied) { } else if (status.isPermanentlyDenied) {
print("iOS: 存储权限被永久拒绝,请前往设置开启"); print("iOS: 存储权限被永久拒绝,请前往设置开启");
} else { } else {
print("iOS: 存储权限被拒绝"); print("iOS: 存储权限被拒绝");
} }
return isStoragePermission;
} }
Future<void> requestNotificationPermission() async { Future<void> requestNotificationPermission() async {
@ -83,10 +96,12 @@ class IosPermissionHandler {
} }
} }
static Future<void> requestMicrophonePermission() async { static Future<bool> requestMicrophonePermission() async {
var isMicrophonePermission = false;
PermissionStatus status = await Permission.microphone.request(); PermissionStatus status = await Permission.microphone.request();
if (status.isGranted) { if (status.isGranted) {
print("iOS: 麦克风权限已授予"); print("iOS: 麦克风权限已授予");
isMicrophonePermission = true;
} else if (status.isPermanentlyDenied) { } else if (status.isPermanentlyDenied) {
print("iOS: 麦克风权限被永久拒绝,请前往设置开启"); print("iOS: 麦克风权限被永久拒绝,请前往设置开启");
ToastUtils.showError("权限被永久拒绝,请前往设置开启!"); ToastUtils.showError("权限被永久拒绝,请前往设置开启!");
@ -95,5 +110,6 @@ class IosPermissionHandler {
print("iOS: 麦克风限被拒绝"); print("iOS: 麦克风限被拒绝");
ToastUtils.showError("权限被拒绝,可能会导致相关功能不可用!"); ToastUtils.showError("权限被拒绝,可能会导致相关功能不可用!");
} }
return isMicrophonePermission;
} }
} }

View File

@ -1,16 +1,47 @@
import 'dart:io'; import 'dart:io';
import 'dart:ui';
import 'package:permission_handler/permission_handler.dart';
import 'AndroidPermissionHandler.dart'; import 'AndroidPermissionHandler.dart';
import 'IosPermissionHandler.dart'; import 'IosPermissionHandler.dart';
class PermissionService { class PermissionService {
static Future<void> requestPermissions() async { static Future<bool> requestPermissions() async {
var isRequestPermissions = false;
if (Platform.isIOS) { if (Platform.isIOS) {
// iOS权限处理逻辑 // iOS权限处理逻辑
await IosPermissionHandler().requestCameraToMicrophonePermissions(); isRequestPermissions = await IosPermissionHandler().requestCameraToMicrophonePermissions();
} else if (Platform.isAndroid) { } else if (Platform.isAndroid) {
// Android权限处理逻辑 // Android权限处理逻辑
await AndroidPermissionHandler().requestCameraToMicrophonePermissions(); isRequestPermissions = await AndroidPermissionHandler().requestCameraToMicrophonePermissions();
} }
return isRequestPermissions;
}
static Future<bool> requestStoragePermissions() async {
var isRequestPermissions = false;
if (Platform.isIOS) {
// iOS权限处理逻辑
isRequestPermissions = await IosPermissionHandler().requestStoragePermission();
} else if (Platform.isAndroid) {
// Android权限处理逻辑
isRequestPermissions = await AndroidPermissionHandler().requestStoragePermission();
}
return isRequestPermissions;
}
///
static Future<bool> checkPermission({required List<Permission> permissionList}) async {
var isRequestPermissions = true;
///
for (Permission permission in permissionList) {
PermissionStatus status = await permission.status;
///
if (!status.isGranted) {
isRequestPermissions = false;
}
}
return isRequestPermissions;
} }
} }

View File

@ -0,0 +1,23 @@
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import 'package:get/get_core/src/get_main.dart';
class RouteUtil {
/**
*
*/
static String getRoute(){
var routePath = Get.currentRoute;
return routePath;
}
/**
* context
*/
static BuildContext getContext(){
var context = Get.context!;
return context;
}
}

View File

@ -0,0 +1,175 @@
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/src/size_extension.dart';
import 'package:get/get.dart';
import 'package:get/get_core/src/get_main.dart';
import 'package:install_plugin/install_plugin.dart';
import 'package:path_provider/path_provider.dart';
import 'package:wgshare/utils/color_util.dart';
import 'package:wgshare/utils/toast_utils.dart';
import 'package:wgshare/view/upgrade/util/load_file_cancel_request.dart';
publicDialog(BuildContext context,
{String title = "提示",
required String describe,
btnWidth = 116.0,
btnHeight = 42.0,
required String leftBtnStr,
required String rightBtnStr,
bool hideCancelBtn = false,
required Function leftBtnCallback,
required Function rightBtnCallback}) {
showDialog(
context: context,
barrierDismissible: hideCancelBtn,
builder: (_) => PopScope(
canPop: hideCancelBtn,
child: HintDialog(title, describe, btnWidth, btnHeight, leftBtnStr,
rightBtnStr, hideCancelBtn,leftBtnCallback,rightBtnCallback),
));
}
class HintDialog extends StatefulWidget {
final String title; //
final String describe; //
final double btnWidth; //
final double btnHeight; //
final String leftBtnStr; //
final String rightBtnStr; //
final bool hideCancelBtn; //btn
final Function leftBtnCallback;
final Function rightBtnCallback;
HintDialog(
this.title,
this.describe,
this.btnWidth,
this.btnHeight,
this.leftBtnStr,
this.rightBtnStr,
this.hideCancelBtn,
this.leftBtnCallback,
this.rightBtnCallback);
@override
State<StatefulWidget> createState() {
return HintDialogState();
}
}
class HintDialogState extends State<HintDialog> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Center(
child: Container(
height: 250.h,
margin: const EdgeInsets.only(left: 40, right: 40),
padding:
const EdgeInsets.only(top: 20, bottom: 20, left: 12, right: 12),
decoration: BoxDecoration(
color: const Color.fromRGBO(255, 255, 255, 1),
borderRadius: BorderRadius.circular(8),
),
child: _contentStyle(),
),
);
}
///
_contentStyle() {
return Column(
children: <Widget>[
_title(),
Expanded(
child: _hintMsg(),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
child: Container(
width: widget.btnWidth,
height: widget.btnHeight,
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(99)),
color: ColorUtil.Color_230_230_230,
),
margin: const EdgeInsets.only(top: 12, left: 12),
alignment: Alignment.center,
child: Text(
widget.leftBtnStr,
style: TextStyle(
fontSize: 14.sp, color: ColorUtil.Color_255_255_255),
),
),
onTap: () {
Get.back();
widget.leftBtnCallback();
},
),
GestureDetector(
child: Container(
width: widget.btnWidth,
height: widget.btnHeight,
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(99)),
color: ColorUtil.Color_85_117_242,
),
margin: const EdgeInsets.only(top: 12, right: 12),
alignment: Alignment.center,
child: Text(
widget.rightBtnStr,
style: TextStyle(
fontSize: 14.sp, color: ColorUtil.Color_255_255_255),
),
),
onTap: () {
Get.back();
widget.rightBtnCallback();
},
)
],
)
],
);
}
///
_title() {
return Container(
alignment: Alignment.center,
margin: const EdgeInsets.only(bottom: 12),
child: Text(widget.title,
style: TextStyle(
fontSize: 16.sp,
color: ColorUtil.Color_85_117_242,
fontWeight: FontWeight.w600,
decoration: TextDecoration.none)),
);
}
///
_hintMsg() {
return SingleChildScrollView(
child: Container(
alignment: Alignment.topLeft,
margin: const EdgeInsets.only(left: 8, right: 8),
child: Text(widget.describe,
style: const TextStyle(
fontSize: 14,
color: ColorUtil.Color_89_88_88,
decoration: TextDecoration.none,
height: 1.4)),
),
);
}
}

View File

@ -0,0 +1,218 @@
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/src/size_extension.dart';
import 'package:get/get.dart';
import 'package:install_plugin/install_plugin.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:wgshare/utils/color_util.dart';
import 'package:wgshare/utils/toast_utils.dart';
import 'package:wgshare/view/upgrade/util/load_file_cancel_request.dart';
import '../../common/store/business_store.dart';
import '../../utils/permission/PermissionService.dart';
import '../public_dialog.dart';
hideUpgradeDialog(
BuildContext context,
String describe,
String apkSavePath,
String appStoreUrl,
int versionCode,{
btnWidth = 116.0,
btnHeight = 42.0,
bool hideCancelBtn = false,
}) {
showDialog(
context: context,
barrierDismissible: hideCancelBtn,
builder: (_) => PopScope(
canPop: hideCancelBtn,
child: HintDialog(describe, apkSavePath, appStoreUrl, versionCode, btnWidth,
btnHeight, hideCancelBtn),
));
}
class HintDialog extends StatefulWidget {
final String describe; //
final String apkSavePath;
final String appStoreUrl;
final int versionCode;
final double btnWidth; //
final double btnHeight; //
final bool hideCancelBtn; //btn
HintDialog(this.describe, this.apkSavePath, this.appStoreUrl, this.versionCode, this.btnWidth,
this.btnHeight, this.hideCancelBtn);
@override
State<StatefulWidget> createState() {
return HintDialogState();
}
}
class HintDialogState extends State<HintDialog> {
// 012
int confirmBtnType = 0;
//
String btnStr = "立即安装";
// apk路径
late String apkPath;
late loadFileCancelRequest loadRequest;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Center(
child: Container(
height: 400.h,
margin: const EdgeInsets.only(left: 40, right: 40),
padding:
const EdgeInsets.only(top: 20, bottom: 20, left: 12, right: 12),
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/upappbg.png'),
fit: BoxFit.fill,
),
),
child: _contentStyle(),
),
);
}
///
_contentStyle() {
return Column(
children: <Widget>[
// _title(),
Expanded(
child: Container(),
),
_hintMsg(),
GestureDetector(
child: Container(
width: double.infinity,
height: 50.h,
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(6)),
color: ColorUtil.Color_85_117_242,
),
margin: const EdgeInsets.only(top: 12),
alignment: Alignment.center,
child: Text(
btnStr,
style: TextStyle(
fontSize: 14.sp, color: ColorUtil.Color_255_255_255),
),
),
onTap: () {
if (Platform.isIOS) {
_gotoAppStore();
} else {
if (confirmBtnType == 0) {
PermissionService.checkPermission(permissionList: [Permission.manageExternalStorage]).then((value){
if(value == true){
_installApk();
}else{
publicDialog(Get.context!,
hideCancelBtn: true,
title: '请求权限说明',
describe:
'APP需要获取您的文件访问权限用以确保新版本下载后可以正常安装。',
leftBtnStr: '拒绝',
rightBtnStr: '同意',
leftBtnCallback: () {
BusinessStore.to.setIsRefuseHomeCheckPermission(true);
}, rightBtnCallback: () {
PermissionService.requestStoragePermissions().then((value){
if(value == true){
_installApk();
}
});
});
}
});
}
/*if (confirmBtnType == 1) {
SystemNavigator.pop();
}
if (confirmBtnType == 2) {
Navigator.of(context).pop();
}*/
}
},
),
Visibility(
visible: widget.hideCancelBtn,
child: GestureDetector(
child: Container(
margin: const EdgeInsets.only(top: 20),
child: Text(
"跳过此版本",
style:
TextStyle(fontSize: 14.sp, color: ColorUtil.Color_84_84_84),
),
),
onTap: () {
BusinessStore.to.setSkipUpVersion(widget.versionCode);
Get.back();
},
),
)
],
);
}
///
_hintMsg() {
return SingleChildScrollView(
child: Container(
alignment: Alignment.topLeft,
margin: const EdgeInsets.only(),
child: Text(widget.describe,
style: const TextStyle(
fontSize: 14,
color: ColorUtil.Color_244_244_244,
decoration: TextDecoration.none,
height: 1.4)),
),
);
}
/// APK
_installApk() async {
await InstallPlugin.installApk(widget.apkSavePath, appId: "com.yuanxuan.wgshare").then((result) {
debugPrint("检查更新-安卓安装成功:$result");
/*setState(() {
confirmBtnType = 2;
btnStr = "关闭APP重新打开";
});*/
BusinessStore.to.erase();
}).catchError((error) {
debugPrint("检查更新-安卓安装失败:$error");
/*setState(() {
confirmBtnType = 2;
btnStr = "跳过";
});*/
});
}
///
_gotoAppStore() async {
await InstallPlugin.install(widget.appStoreUrl).then((result) {
debugPrint("检查更新-苹果安装成功:$result");
}).catchError((error) {
debugPrint("检查更新-苹果安装失败:$error");
});
}
}

View File

@ -0,0 +1,56 @@
import 'package:json_annotation/json_annotation.dart';
part 'upgrade_entity.g.dart';
@JsonSerializable()
class UpgradeEntity extends Object {
@JsonKey(name: 'code')
int code;
@JsonKey(name: 'message')
String message;
@JsonKey(name: 'success')
bool success;
@JsonKey(name: 'data')
UpgradeData data;
UpgradeEntity(this.code,this.message,this.success,this.data,);
factory UpgradeEntity.fromJson(Map<String, dynamic> srcJson) => _$UpgradeEntityFromJson(srcJson);
Map<String, dynamic> toJson() => _$UpgradeEntityToJson(this);
}
@JsonSerializable()
class UpgradeData extends Object {
@JsonKey(name: 'versionCode')
int versionCode;
@JsonKey(name: 'versionName')
String versionName;
@JsonKey(name: 'updateType')
int updateType;
@JsonKey(name: 'versionDescribe')
String versionDescribe;
@JsonKey(name: 'androidurl')
String androidurl;
@JsonKey(name: 'appStoreUrl')
String appStoreUrl;
UpgradeData(this.versionCode,this.versionName,this.updateType,this.versionDescribe,this.androidurl,this.appStoreUrl);
factory UpgradeData.fromJson(Map<String, dynamic> srcJson) => _$UpgradeDataFromJson(srcJson);
Map<String, dynamic> toJson() => _$UpgradeDataToJson(this);
}

View File

@ -0,0 +1,149 @@
import 'dart:convert';
import 'dart:io';
import 'package:al_downloader/al_downloader.dart';
import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:path_provider/path_provider.dart';
import 'package:wgshare/utils/toast_utils.dart';
import 'package:wgshare/view/upgrade/hide_upgrade_dialog.dart';
import '../../../common/models/app_updata_info_entity.dart';
import '../../../common/store/business_store.dart';
import '../../../utils/routeUtil.dart';
import 'entity/upgrade_entity.dart';
class HideCheckVersion{
Future<Map> request(String url) async{
Response response = await Dio().get(url);
return response.data;
}
Future<void> doHttpHideCheckVersion() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String versionCode = packageInfo.buildNumber;
if(null != BusinessStore.to.appUpdataInfoEntity.value){
ToastUtils.showSuccess('00000');
if(int.tryParse(versionCode)! < BusinessStore.to.appUpdataInfoEntity.value!.versionCode){
ToastUtils.showSuccess('11111--缓存跳过版本${BusinessStore.to.skipUpVersion}-缓存安装版本${BusinessStore.to.appUpdataInfoEntity.value!.versionCode}');
if(null != BusinessStore.to.skipUpVersion){
if(BusinessStore.to.skipUpVersion! < BusinessStore.to.appUpdataInfoEntity.value!.versionCode){
hideUpgradeDialog(
RouteUtil.getContext(),
BusinessStore.to.appUpdataInfoEntity.value!.versionDescribe,
hideCancelBtn: BusinessStore.to.appUpdataInfoEntity.value!.updateType == 1 ? false : true,
BusinessStore.to.appUpdataInfoEntity.value!.apkSavePath,
BusinessStore.to.appUpdataInfoEntity.value!.appStoreUrl,
BusinessStore.to.appUpdataInfoEntity.value!.versionCode,
);
}else{
ToastUtils.showSuccess('22222');
doHttpUp();
}
}else{
doHttpUp();
}
}else{
ToastUtils.showSuccess('33333');
BusinessStore.to.erase();
}
}else{
ToastUtils.showSuccess('44444');
doHttpUp();
}
}
Future<void> doHttpUp() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String versionCode = packageInfo.buildNumber;
Future<Map> map = request("http://192.168.2.9:8827/latest.json");
map.then((result) async {
var jsonStr = json.encode(result);
var listDynamic = jsonDecode(jsonStr);
UpgradeEntity upgradeEntity = UpgradeEntity.fromJson(listDynamic);
debugPrint('55555${upgradeEntity.code}-${upgradeEntity.data.versionCode}-${upgradeEntity.data.androidurl}');
ToastUtils.showSuccess('55555${upgradeEntity.code}-${upgradeEntity.data.versionCode}-${upgradeEntity.data.androidurl}');
if(upgradeEntity.code == 200){
debugPrint("检查更新-服务器版本信息:$jsonStr");
debugPrint("检查更新-当前版本号:$versionCode");
if(int.tryParse(versionCode)! < upgradeEntity.data.versionCode){
if((BusinessStore.to.skipUpVersion ?? 0) < upgradeEntity.data.versionCode){
if (Platform.isAndroid) {
/// APK
try {
var appDocDir = await getTemporaryDirectory();
var apkSavePath = "${appDocDir.path}/wgshare_up.apk";
// String url = "https://www.onlinedown.net/iopdfbhjl/632869?module=download&t=website&v=20241229163449";
// String url = "https://s3.cn-north-1.amazonaws.com.cn/mtab.kezaihui.com/apk/takeaway_phone_release_1.apk";
String url = upgradeEntity.data.androidurl;
ALDownloader.download(url,
directoryPath: "${appDocDir.path}/",
fileName: "wgshare_up.apk",
handlerInterface:
ALDownloaderHandlerInterface(progressHandler: (progress) {
debugPrint('检查更新 | 下载进度 = $progress, url = $url');
ToastUtils.showSuccess('下载进度$progress');
}, succeededHandler: () {
debugPrint('检查更新 | 下载成功, url = $url');
debugPrint('检查更新,当前路由是:${RouteUtil.getRoute()}');
debugPrint('检查更新APK保存路径$apkSavePath');
BusinessStore.to.setAppUpdataInfo(AppUpdataInfoEntity(
upgradeEntity.data.versionCode,
upgradeEntity.data.versionName,
upgradeEntity.data.updateType,
upgradeEntity.data.versionDescribe,
upgradeEntity.data.androidurl,
upgradeEntity.data.appStoreUrl,
apkSavePath
));
if (RouteUtil.getRoute() == '/meetingMainPage') {
ToastUtils.showInfo(
"新版本静默下载完毕\n将择机通知安装",
duration: const Duration(milliseconds: 3000));
} else {
hideUpgradeDialog(
RouteUtil.getContext(),
upgradeEntity.data.versionDescribe,
hideCancelBtn: upgradeEntity.data.updateType == 1 ? false : true,
apkSavePath,
'',
upgradeEntity.data.versionCode,
);
}
}, failedHandler: () {
debugPrint('检查更新 | 下载失败, url = $url');
}, pausedHandler: () {
debugPrint('检查更新 | 下载暂停, url = $url');
}));
} catch (e) {
debugPrint("检查更新-安卓下载失败:$e");
}
}else{
hideUpgradeDialog(
RouteUtil.getContext(),
upgradeEntity.data.versionDescribe,
hideCancelBtn: upgradeEntity.data.updateType == 1 ? false : true,
'',
upgradeEntity.data.appStoreUrl,
upgradeEntity.data.versionCode,
);
}
}
}else{
return;
}
}else{
return;
}
});
}
}

View File

@ -0,0 +1,42 @@
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:package_info_plus/package_info_plus.dart';
import '../upgrade_dialog.dart';
import 'entity/upgrade_entity.dart';
Future<Map> request(String url) async{
Response response = await Dio().get(url);
return response.data;
}
void doHttpCheckVersion(BuildContext context){
Future<Map> map = request("https://meeting-api.23544.com/meeting/mobile/latest.json");
map.then((result) async {
var jsonStr = json.encode(result);
var listDynamic = jsonDecode(jsonStr);
UpgradeEntity upgradeEntity = UpgradeEntity.fromJson(listDynamic);
if(upgradeEntity.code == 200){
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String versionCode = packageInfo.buildNumber;
if(int.tryParse(versionCode)! < 2){
debugPrint("检查更新-服务器版本信息:$jsonStr");
debugPrint("检查更新-当前版本号:$versionCode");
upgradeDialog(
context,
upgradeEntity.data.versionDescribe,
hideCancelBtn: /*upgradeEntity.data.updateType == 1 ? false : true*/true,
title: '版本更新 v${upgradeEntity.data.versionName}',
/*upgradeEntity.data.url*/'https://s3.cn-north-1.amazonaws.com.cn/mtab.kezaihui.com/apk/takeaway_phone_release_1.apk',
upgradeEntity.data.appStoreUrl,
upgradeEntity.data.versionName
);
}else{
return;
}
}else{
return;
}
});
}

View File

@ -0,0 +1,290 @@
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/src/size_extension.dart';
import 'package:get/get.dart';
import 'package:install_plugin/install_plugin.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:wgshare/utils/color_util.dart';
import 'package:wgshare/utils/toast_utils.dart';
import 'package:wgshare/view/upgrade/util/load_file_cancel_request.dart';
import '../../utils/permission/PermissionService.dart';
import '../public_dialog.dart';
upgradeDialog(
BuildContext context,
String describe,
String url,
String appStoreUrl,
String versionName, {
String title = "提示",
btnWidth = 116.0,
btnHeight = 42.0,
bool hideCancelBtn = false,
}) {
showDialog(
context: context,
barrierDismissible: hideCancelBtn,
builder: (_) => PopScope(
canPop: hideCancelBtn,
child: HintDialog(title, describe, url, appStoreUrl, versionName,
btnWidth, btnHeight, hideCancelBtn),
));
}
class HintDialog extends StatefulWidget {
final String title; //
final String describe; //
final String url;
final String appStoreUrl;
final String versionName;
final double btnWidth; //
final double btnHeight; //
final bool hideCancelBtn; //btn
HintDialog(this.title, this.describe, this.url, this.appStoreUrl,
this.versionName, this.btnWidth, this.btnHeight, this.hideCancelBtn);
@override
State<StatefulWidget> createState() {
return HintDialogState();
}
}
class HintDialogState extends State<HintDialog> {
// 01234
int confirmBtnType = 0;
//
String btnStr = "立即更新";
//
double progressValue = 0.0;
// apk路径
late String apkPath;
late loadFileCancelRequest loadRequest;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Center(
child: Container(
height: 400.h,
margin: const EdgeInsets.only(left: 40, right: 40),
padding:
const EdgeInsets.only(top: 20, bottom: 20, left: 12, right: 12),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/upappbg.png'),
fit: BoxFit.fill,
),
),
child: _contentStyle(),
),
);
}
///
_contentStyle() {
return Column(
children: <Widget>[
// _title(),
Expanded(
child: Container(),
),
_hintMsg(),
Visibility(
visible: confirmBtnType == 1 ? true : false,
child: Container(
width: double.infinity,
height: 4.h,
color: ColorUtil.Color_85_117_242,
margin: const EdgeInsets.only(top: 12, left: 2, right: 2),
child: LinearProgressIndicator(
value: progressValue,
valueColor:
const AlwaysStoppedAnimation(ColorUtil.Color_85_117_242),
backgroundColor: ColorUtil.Color_185_184_184,
),
),
),
GestureDetector(
child: Container(
width: double.infinity,
height: 50.h,
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(6)),
color: ColorUtil.Color_85_117_242,
),
margin: const EdgeInsets.only(top: 12),
alignment: Alignment.center,
child: Text(
btnStr,
style: TextStyle(
fontSize: 14.sp, color: ColorUtil.Color_255_255_255),
),
),
onTap: () {
if (Platform.isIOS) {
_gotoAppStore();
} else {
if (confirmBtnType == 0) {
_downloadApk();
}
if (confirmBtnType == 1) {
Navigator.of(context).pop();
}
if (confirmBtnType == 2) {
PermissionService.checkPermission(permissionList: [Permission.manageExternalStorage]).then((value){
if(value == true){
// APK函数
_installApk(apkPath);
}else{
publicDialog(context,
hideCancelBtn: true,
title: '请求权限说明',
describe:
'APP需要获取您的文件访问权限用以确保新版本下载后可以正常安装。',
leftBtnStr: '拒绝',
rightBtnStr: '同意',
leftBtnCallback: () {
}, rightBtnCallback: () {
PermissionService.requestStoragePermissions();
});
}
});
}
if (confirmBtnType == 3) {
SystemNavigator.pop();
}
if (confirmBtnType == 4) {
Navigator.of(context).pop();
}
}
},
),
Visibility(
visible: widget.hideCancelBtn,
child: GestureDetector(
child: Container(
margin: const EdgeInsets.only(top: 20),
child: Text(
"跳过此版本",
style: TextStyle(
fontSize: 14.sp,
color: ColorUtil.Color_84_84_84
),
),
),
onTap: (){
Get.back();
},
),
)
],
);
}
///
_title() {
return Container(
alignment: Alignment.center,
margin: const EdgeInsets.only(bottom: 12),
child: Text(widget.title,
style: TextStyle(
fontSize: 16.sp,
color: ColorUtil.Color_85_117_242,
fontWeight: FontWeight.w600,
decoration: TextDecoration.none)),
);
}
///
_hintMsg() {
return SingleChildScrollView(
child: Container(
alignment: Alignment.topLeft,
margin: const EdgeInsets.only(),
child: Text(widget.describe,
style: const TextStyle(
fontSize: 14,
color: ColorUtil.Color_244_244_244,
decoration: TextDecoration.none,
height: 1.4)),
),
);
}
/// APK
_downloadApk() async {
loadRequest = loadFileCancelRequest();
try {
// apk下载路径
var appDocDir = await getTemporaryDirectory();
apkPath = "${appDocDir.path}/wgshare_${widget.versionName}.apk";
loadRequest.load(widget.url, apkPath);
loadRequest.setProgressCallbackListener((progress) {
setState(() {
progressValue = progress;
});
});
loadRequest.setSuccessCallbackListener(() {
setState(() {
confirmBtnType = 2;
btnStr = "安装";
});
});
setState(() {
confirmBtnType = 1;
btnStr = "取消";
});
} catch (e) {
debugPrint("检查更新-安卓下载失败:$e");
setState(() {
confirmBtnType = 0;
btnStr = "更新";
});
ToastUtils.showError("下载失败");
}
}
/// APK
_installApk(String path) async {
await InstallPlugin.installApk(path, appId: "com.yuanxuan.wgshare").then((result) {
debugPrint("检查更新-安卓安装成功:$result");
setState(() {
confirmBtnType = 3;
btnStr = "关闭APP重新打开";
});
}).catchError((error) {
debugPrint("检查更新-安卓安装失败:$error");
setState(() {
confirmBtnType = 4;
btnStr = "跳过";
});
});
}
///
_gotoAppStore() async {
await InstallPlugin.install(widget.appStoreUrl).then((result){
debugPrint("检查更新-苹果安装成功:$result");
}).catchError((error){
debugPrint("检查更新-苹果安装失败:$error");
});
}
}

View File

@ -0,0 +1,36 @@
import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
class loadFileCancelRequest{
late Function progressCallback;
late Function successCallback;
CancelToken token = CancelToken();
load(String url, String path) async {
Dio dio = Dio();
await dio.download(url, path, cancelToken: token, onReceiveProgress: (received, total) {
if (total != -1) {
//
progressCallback((received / total));
debugPrint("检查更新-下载进度:${(received / total)}");
}
}).then((onValue){
//
successCallback();
});
}
cancel() {
token.cancel('取消请求');
}
setProgressCallbackListener(Function progressCallback){
this.progressCallback = progressCallback;
}
setSuccessCallbackListener(Function successCallback){
this.successCallback = successCallback;
}
}

View File

@ -17,6 +17,7 @@ import macos_window_utils
import package_info_plus import package_info_plus
import path_provider_foundation import path_provider_foundation
import sqflite_darwin import sqflite_darwin
import wakelock_plus
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AgoraRtcNgPlugin.register(with: registry.registrar(forPlugin: "AgoraRtcNgPlugin")) AgoraRtcNgPlugin.register(with: registry.registrar(forPlugin: "AgoraRtcNgPlugin"))
@ -31,4 +32,5 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
} }

View File

@ -30,6 +30,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "6.5.0" version: "6.5.0"
al_downloader:
dependency: "direct main"
description:
name: al_downloader
sha256: "00b96b113da7e013f532527238e2f8e5219834f6b4e52df6ff8b12f26eda7101"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.8.4"
analyzer: analyzer:
dependency: transitive dependency: transitive
description: description:
@ -246,6 +254,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.0.9" version: "2.0.9"
dbus:
dependency: transitive
description:
name: dbus
sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.7.10"
device_info_plus: device_info_plus:
dependency: "direct main" dependency: "direct main"
description: description:
@ -347,6 +363,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "3.3.1" version: "3.3.1"
flutter_downloader:
dependency: transitive
description:
name: flutter_downloader
sha256: "2b126083d2e6b7c09755bca12012c4c734bcf7666cf07ba00c508fcb83e8d0d7"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.11.1"
flutter_easyloading: flutter_easyloading:
dependency: "direct main" dependency: "direct main"
description: description:
@ -634,6 +658,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "4.0.2" version: "4.0.2"
install_plugin:
dependency: "direct main"
description:
name: install_plugin
sha256: "6fb67ba0781e75de4f2f2266ed25e835bfd277c5bfc2ed034af52774355857c6"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
intersperse: intersperse:
dependency: transitive dependency: transitive
description: description:
@ -670,10 +702,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: js name: js
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.7.1" version: "0.6.7"
json_annotation: json_annotation:
dependency: "direct main" dependency: "direct main"
description: description:
@ -843,7 +875,7 @@ packages:
source: hosted source: hosted
version: "1.9.0" version: "1.9.0"
path_provider: path_provider:
dependency: transitive dependency: "direct main"
description: description:
name: path_provider name: path_provider
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
@ -938,6 +970,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.2.1" version: "0.2.1"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.0.2"
photo_view: photo_view:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1010,6 +1050,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.0.0" version: "2.0.0"
queue:
dependency: transitive
description:
name: queue
sha256: "9a41ecadc15db79010108c06eae229a45c56b18db699760f34e8c9ac9b831ff9"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.0+2"
retrofit: retrofit:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1263,6 +1311,22 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "14.2.5" version: "14.2.5"
wakelock_plus:
dependency: "direct main"
description:
name: wakelock_plus
sha256: "36c88af0b930121941345306d259ec4cc4ecca3b151c02e3a9e71aede83c615e"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.10"
wakelock_plus_platform_interface:
dependency: transitive
description:
name: wakelock_plus_platform_interface
sha256: "70e780bc99796e1db82fe764b1e7dcb89a86f1e5b3afb1db354de50f2e41eb7a"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.2"
watcher: watcher:
dependency: transitive dependency: transitive
description: description:
@ -1311,6 +1375,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.1.0" version: "1.1.0"
xml:
dependency: transitive
description:
name: xml
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.5.0"
yaml: yaml:
dependency: transitive dependency: transitive
description: description:

View File

@ -92,6 +92,18 @@ dependencies:
# 弹窗 # 弹窗
adaptive_dialog: ^2.3.0 adaptive_dialog: ^2.3.0
# 安装apk
install_plugin: ^2.1.0
# 文件目录
path_provider: ^2.0.2
# 后台下载
al_downloader: ^1.8.2
# 屏幕常亮
wakelock_plus: ^1.2.10
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter