Browse Source

2024-12-27 修改

修改:
1. 团组操作 - 团组基本信息 客户级别 OP提成联动
2. 部分权限声明逻辑修改
3. TCPService 逻辑优化
zhaiy 1 week ago
parent
commit
2b9a4d2ec1

+ 1 - 4
app/build.gradle

@@ -99,11 +99,8 @@ dependencies {
     //EventBus
     implementation 'org.greenrobot:eventbus:3.3.1'
 
-    //权限获取组件
-    implementation 'pub.devrel:easypermissions:3.0.0'
-
     //SignalR
-    implementation 'com.microsoft.signalr:signalr:1.0.0'
+    implementation 'com.microsoft.signalr:signalr:6.0.1'
 
     //表格组件
     implementation 'com.github.huangyanbin:SmartTable:2.2.0'

+ 1 - 1
app/src/main/AndroidManifest.xml

@@ -53,7 +53,7 @@
         android:theme="@style/AppTheme"
         tools:targetApi="31">
         <activity
-            android:name=".ui.customer_resource.customer_visit.CustomerDistributionMap"
+            android:name=".ui.customer_resource.customer_distribution.CustomerDistributionMap"
             android:exported="false"
             android:launchMode="singleTop"/>
         <activity

+ 1 - 7
app/src/main/java/com/pan_american/android/OASystem.kt

@@ -348,14 +348,8 @@ class OASystem : Application() {
         //拍照权限code
         const val CAMERA_REQUEST_CODE = 1001
 
-        //通知权限code
-        const val NOTIFICATION_REQUEST_CODE = 1002
-
         //相册权限code
-        const val GALLERY_REQUEST_CODE = 1003
-
-        //定位权限code
-        const val LOCATION_REQUEST_CODE = 1004
+        const val GALLERY_REQUEST_CODE = 1002
 
         //刷新主页消息和公告
         const val REFRESH_NAVIGATION = 2001

+ 119 - 131
app/src/main/java/com/pan_american/android/data/network/TCPService.kt

@@ -3,8 +3,7 @@ package com.pan_american.android.data.network
 import android.app.Service
 import android.content.Intent
 import android.os.Binder
-import android.os.Handler
-import android.os.Looper
+import android.os.IBinder
 import android.util.Log
 import com.microsoft.signalr.HubConnection
 import com.microsoft.signalr.HubConnectionBuilder
@@ -12,170 +11,159 @@ import com.pan_american.android.OASystem
 import com.pan_american.android.data.model.message.entity.MessageGetter
 import com.pan_american.android.data.model.message.network.MessageUnReadCountRequest
 import com.pan_american.android.data.model.message.network.MessageUnReadTotalResponse
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.cancelChildren
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
 import org.greenrobot.eventbus.EventBus
 import retrofit2.Call
 import retrofit2.Callback
 import retrofit2.Response
-import java.util.concurrent.ExecutorService
-import java.util.concurrent.Executors
+import java.util.concurrent.atomic.AtomicBoolean
 
-class TCPService: Service() {
+class TCPService : Service() {
 
     companion object {
-
-        val TAG: String = TCPService::class.java.simpleName
-
+        private const val TAG = "TCPService"
+        private const val HUB_URL = "http://132.232.92.186:8888/chatHub"
+        private const val HEARTBEAT_INTERVAL = 30_000L
+        private const val RECONNECT_DELAY = 5_000L
+        const val ACTION_START_CONNECTION = "com.pan_american.android.START_CONNECTION" // 定义 Action
         val apiService = ServiceCreator.create<APIService>()
-
-        const val HEART_SPACE_TIME: Long = 1000
-
-        //消息系统 MS HubConnection
-        val hubConnection: HubConnection = HubConnectionBuilder.create("http://132.232.92.186:8888/chatHub")
-            .withHeader("Authorization", OASystem.token)
-            .build().apply {
-                serverTimeout = 60 * HEART_SPACE_TIME
-                keepAliveInterval = HEART_SPACE_TIME
-            }
-    }
-
-    override fun onBind(p0: Intent?): ClientBinder {
-        return ClientBinder()
     }
 
-    class ClientBinder : Binder() {
-        //心跳间隔时间
+    private var hubConnection: HubConnection? = null // Hub连接,可空
+    private val isConnected = AtomicBoolean(false) // 连接状态标志
+    private val binder = TCPBinder() // Binder对象
+    private val serviceScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) // 服务协程作用域
 
-        private lateinit var readThread: ReadThread
-        private val handler = Handler(Looper.getMainLooper())
-        private lateinit var executorService: ExecutorService
-        private var connectionTryCount = 0
-        private var heartSpaceTryCount = 0
+    inner class TCPBinder : Binder()
 
-        fun startConnect() {
-            //在子线程进行网络操作
-            //Service 运行在主线程
-
-            executorService = Executors.newCachedThreadPool()
-            executorService.execute(connectRunnable)
-        }
+    override fun onBind(intent: Intent?): IBinder {
+        return binder
+    }
 
-        private fun restartConnect() {
-            executorService.execute(connectRunnable)
-        }
+    override fun onCreate() {
+        super.onCreate()
+        Log.d(TAG, "服务已创建")
+    }
 
-        private val connectRunnable = Runnable {
+    private fun startConnection() {
+        if (isConnected.get()) return // 如果已连接,则返回
 
+        serviceScope.launch { // 在IO线程启动协程
             try {
+                val connection = HubConnectionBuilder.create(HUB_URL)
+                    .withHeader("Authorization", OASystem.token)
+                    .build()
 
-                hubConnection.start().blockingAwait()
-
-                //创建读取服务器心跳的线程
-                readThread = ReadThread()
-                readThread.start()
+                hubConnection = connection
 
-                //开启心跳,每隔30秒钟发送一次心跳
-                handler.post(heartRunnable)
+                connection.start().blockingAwait() // 开始连接并等待
+                isConnected.set(true)
+                Log.d(TAG, "已连接到SignalR Hub")
 
-                connectionTryCount = 0
+                registerHubListeners(connection) // 注册Hub监听器
+                startHeartbeat(connection) // 启动心跳
 
             } catch (e: Exception) {
-
-                connectionTryCount ++
-
-//                Log.e(TAG, e.stackTraceToString())
-
-                Log.e(TAG, "Connection failed, try reconnect. Count: $connectionTryCount")
-
-                handler.postDelayed({ restartConnect() }, HEART_SPACE_TIME)
+                Log.e(TAG, "连接错误: ${e.message}", e) // 记录错误,包含异常信息
+                reconnect() // 重连
             }
         }
+    }
 
-        inner class ReadThread: Thread() {
-
-            override fun run() {
-                super.run()
-
-                try {
-                    hubConnection.on("ReceiveMessage", { message ->
-
-//                        Log.e(TAG,"Get message from server. $message")
-
-                        if (!message.contains("已上线")) {
-                            when {
-                                message.contains("操作") -> {
-                                    val messageGetter = MessageGetter(message, 1)
-                                    getUnReadTotalCount(messageGetter)
-                                }
-                                message.contains("任务") -> {
-                                    val messageGetter = MessageGetter(message, 2)
-                                    getUnReadTotalCount(messageGetter)
-                                }
-                            }
-
-                        }
-
-                    }, String::class.java)
-
-                } catch (e: Exception) {
-                    e.printStackTrace()
+    private fun registerHubListeners(connection: HubConnection) {
+        connection.on("ReceiveMessage", { message: String ->
+            // 你现有的消息处理逻辑
+            if (!message.contains("已上线")) {
+                val messageType = when {
+                    message.contains("操作") -> 1
+                    message.contains("任务") -> 2
+                    else -> return@on
                 }
+                val messageGetter = MessageGetter(message, messageType)
+                getUnReadTotalCount(messageGetter)
             }
-        }
 
-        private val heartRunnable = Runnable {
-            sendData()
-        }
-
-        private fun sendData() {
-
-            executorService.execute {
-                try {
-
-                    hubConnection.send("SignalRLogin", OASystem.userInfo.userId)
-
-                    //发送成功以后,重新建立一个心跳消息
-                    handler.postDelayed(heartRunnable, HEART_SPACE_TIME * 30)
-
-//                    Log.e(TAG, "Send message to server.")
-
-                    heartSpaceTryCount = 0
-
-                } catch (e: Exception) {
+        }, String::class.java)
+    }
 
-                    heartSpaceTryCount ++
+    private fun startHeartbeat(connection: HubConnection) = serviceScope.launch {
+        while (isConnected.get() && !serviceScope.isActive) { // 检查连接状态和服务是否活动
+            try {
+                connection.send("SignalRLogin", OASystem.userInfo.userId) // 发送心跳,处理userId为空的情况
+                Log.d(TAG, "心跳已发送")
+                delay(HEARTBEAT_INTERVAL) // 延迟
+            } catch (e: Exception) {
+                Log.e(TAG, "心跳错误: ${e.message}", e) // 记录错误,包含异常信息
+                reconnect() // 重连
+                break // 退出循环
+            }
+        }
+    }
 
-//                    Log.e(TAG, e.stackTraceToString())
+    private fun reconnect() = serviceScope.launch {
+        isConnected.set(false)
+        delay(RECONNECT_DELAY) // 延迟重连
+        startConnection() // 开始连接
+    }
 
-                    Log.e(TAG, "Get heartSpase failed,try to reconnect. Count: $heartSpaceTryCount")
+    private fun stopConnection() {
+        serviceScope.coroutineContext.cancelChildren() // 取消所有子协程
+
+        try {
+            hubConnection?.stop() // 停止连接,处理异常
+        } catch (e: Exception) {
+            Log.e(TAG, "停止连接时出错: ${e.message}", e) // 记录错误
+        } finally {
+            hubConnection = null
+            isConnected.set(false)
+            Log.d(TAG, "连接已停止")
+        }
+    }
 
-                    executorService.execute(connectRunnable)
-                }
-            }
+    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+        when (intent?.action) {
+            ACTION_START_CONNECTION -> startConnection()
         }
+        return START_STICKY // 或 START_REDELIVER_INTENT  根据你的需求选择
+    }
 
-        private fun getUnReadTotalCount(messageGetter: MessageGetter) {
-            apiService.getMessageUnReadTotal(MessageUnReadCountRequest()).enqueue(object :
-                Callback<MessageUnReadTotalResponse> {
-                override fun onResponse(
-                    call: Call<MessageUnReadTotalResponse>,
-                    response: Response<MessageUnReadTotalResponse>
-                ) {
-                    val unreadCountResponse = response.body()
-
-                    if (unreadCountResponse != null) {
-                        if (unreadCountResponse.code == 200) {
-                            OASystem.unReadCount = unreadCountResponse.data.messageUnReadCount - unreadCountResponse.data.announcementUnReadCount
-                            OASystem.announcementUnReadCount = unreadCountResponse.data.announcementUnReadCount
-                            EventBus.getDefault().post(messageGetter)
-                        }
-                    }
+    private fun getUnReadTotalCount(messageGetter: MessageGetter) {
+        apiService.getMessageUnReadTotal(MessageUnReadCountRequest()).enqueue(object :
+            Callback<MessageUnReadTotalResponse> {
+            override fun onResponse(
+                call: Call<MessageUnReadTotalResponse>,
+                response: Response<MessageUnReadTotalResponse>
+            ) {
+                val unreadCountResponse = response.body()
+
+                if (unreadCountResponse != null && unreadCountResponse.code == 200) {
+                    // 使用Elvis运算符(?:)安全地处理空值.  并进行正确计算。
+                    val messageUnReadCount = unreadCountResponse.data.messageUnReadCount
+                    val announcementUnReadCount = unreadCountResponse.data.announcementUnReadCount
+
+                    OASystem.unReadCount = messageUnReadCount - announcementUnReadCount
+                    OASystem.announcementUnReadCount = announcementUnReadCount
+                    EventBus.getDefault().post(messageGetter)
                 }
+            }
 
-                override fun onFailure(call: Call<MessageUnReadTotalResponse>, t: Throwable) {
-
-                }
-            })
-        }
+            override fun onFailure(call: Call<MessageUnReadTotalResponse>, t: Throwable) {
+                Log.e(TAG, "获取未读消息数量失败: ${t.message}", t) // 记录错误
+            }
+        })
+    }
 
+    override fun onDestroy() {
+        super.onDestroy()
+        stopConnection() // 停止连接
+        serviceScope.cancel() // 取消协程作用域
+        Log.d(TAG, "服务已销毁")
     }
 }

+ 9 - 11
app/src/main/java/com/pan_american/android/ui/customer_resource/company_customer/AddNewCustomerActivity.kt

@@ -23,7 +23,6 @@ import androidx.core.content.ContextCompat
 import androidx.core.content.FileProvider
 import androidx.core.content.res.ResourcesCompat
 import androidx.recyclerview.widget.StaggeredGridLayoutManager
-import com.google.gson.Gson
 import com.pan_american.android.OASystem
 import com.pan_american.android.R
 import com.pan_american.android.base.BaseActivity
@@ -48,7 +47,6 @@ import retrofit2.Callback
 import retrofit2.Response
 import java.io.ByteArrayOutputStream
 import java.io.File
-import java.io.FileWriter
 
 class AddNewCustomerActivity : BaseActivity<ActivityAddNewCustomerBinding>() {
 
@@ -865,15 +863,15 @@ class AddNewCustomerActivity : BaseActivity<ActivityAddNewCustomerBinding>() {
             bitmap.recycle()
         } while (!base64Adjusted)
 
-        val file = File(filesDir, "data.txt")
-        if (file.exists()) {
-            file.delete()
-        }
-        file.createNewFile()
-        val writer = FileWriter(file)
-        writer.append(Gson().toJson(BusinessCardInfoRequest(transferBase64)))
-        writer.flush()
-        writer.close()
+//        val file = File(filesDir, "data.txt")
+//        if (file.exists()) {
+//            file.delete()
+//        }
+//        file.createNewFile()
+//        val writer = FileWriter(file)
+//        writer.append(Gson().toJson(BusinessCardInfoRequest(transferBase64)))
+//        writer.flush()
+//        writer.close()
 
         apiService.getBusinessCardInfo(BusinessCardInfoRequest(transferBase64))
             .enqueue(object : Callback<BusinessCardInfoResponse> {

+ 169 - 0
app/src/main/java/com/pan_american/android/ui/customer_resource/customer_distribution/CustomerDistributionMap.kt

@@ -0,0 +1,169 @@
+package com.pan_american.android.ui.customer_resource.customer_distribution
+
+import android.Manifest
+import android.content.pm.PackageManager
+import android.graphics.Color
+import android.os.Bundle
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.core.content.ContextCompat
+import com.amap.api.location.AMapLocation
+import com.amap.api.location.AMapLocationClient
+import com.amap.api.location.AMapLocationClientOption
+import com.amap.api.location.AMapLocationListener
+import com.amap.api.maps.AMap
+import com.amap.api.maps.LocationSource
+import com.amap.api.maps.LocationSource.OnLocationChangedListener
+import com.amap.api.maps.UiSettings
+import com.amap.api.maps.model.MyLocationStyle
+import com.pan_american.android.OASystem
+import com.pan_american.android.R
+import com.pan_american.android.base.BaseActivity
+import com.pan_american.android.base.CustomAlertDialog
+import com.pan_american.android.databinding.ActivityCustomerDistributionMapBinding
+import com.pan_american.android.databinding.LayoutTitleBinding
+
+class CustomerDistributionMap : BaseActivity<ActivityCustomerDistributionMapBinding>(),
+    AMapLocationListener, LocationSource {
+
+    private lateinit var titleBinding: LayoutTitleBinding
+
+    // 高德地图定位客户端
+    private var locationClient: AMapLocationClient? = null
+
+    // 定位参数选项
+    private val clientOption = AMapLocationClientOption()
+
+    // 高德地图对象
+    private lateinit var aMap: AMap
+
+    // 定位回调监听器
+    private lateinit var locationChangedListener: OnLocationChangedListener
+
+    // 自定义定位样式
+    private val myLocationStyle = MyLocationStyle()
+
+    // 地图UI设置
+    private lateinit var uiSettings: UiSettings
+
+    // 请求位置权限的启动器,使用ActivityResultContracts简化权限请求流程
+    private val requestLocationPermissionLauncher =
+        registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
+            val allGranted = permissions.all { it.value }
+            if (allGranted) {
+                startLocationUpdates() // 所有权限已授予,开始定位更新
+            } else {
+                showMessage(resources.getString(R.string.location_permission_denied_hint))
+            }
+        }
+
+    override fun getViewBinding() = ActivityCustomerDistributionMapBinding.inflate(layoutInflater)
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        binding.mapView.onCreate(savedInstanceState)
+        initTitle()
+        // 初始化定位客户端
+        initLocation()
+        // 初始化地图
+        initMap(savedInstanceState)
+        // 请求位置权限
+        requestLocationPermissions()
+    }
+
+    override fun initTitle() {
+        titleBinding = LayoutTitleBinding.bind(binding.root).apply {
+            titleText.text = getString(R.string.customer_distribution_map)
+            backButton.setOnClickListener { back() }
+        }
+    }
+
+    override fun initViews() {}
+    override fun initEvents() {}
+
+    private fun initLocation() {
+        locationClient = AMapLocationClient(OASystem.context) // 创建定位客户端
+        locationClient?.setLocationListener(this) // 设置定位监听器
+    }
+
+    private fun initMap(savedInstanceState: Bundle?) {
+        binding.mapView.onCreate(savedInstanceState) // 创建地图View
+        aMap = binding.mapView.map // 获取地图对象
+        aMap.minZoomLevel = 3.0f // 设置最小缩放级别
+        myLocationStyle.strokeColor(Color.argb(0, 0, 0, 0)) // 设置定位圆圈边框颜色(透明)
+        myLocationStyle.strokeWidth(0f) // 设置定位圆圈边框宽度(无)
+        myLocationStyle.radiusFillColor(Color.argb(0, 0, 0, 0)) // 设置定位圆圈填充颜色(透明)
+        aMap.myLocationStyle = myLocationStyle // 设置自定义定位样式
+        aMap.setLocationSource(this) // 设置定位数据源
+        aMap.isMyLocationEnabled = true // 开启定位
+        uiSettings = aMap.uiSettings // 获取地图UI设置对象
+        uiSettings.isZoomControlsEnabled = false // 隐藏缩放按钮
+        uiSettings.isScaleControlsEnabled = true // 显示比例尺
+        //  可以在这里设置地图初始位置和缩放级别
+        //  aMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(39.90403, 116.407526), 10f))
+    }
+
+
+    /**
+     * 请求位置权限,并处理权限请求结果。
+     */
+    private fun requestLocationPermissions() {
+        val permissions = arrayOf(
+            Manifest.permission.ACCESS_COARSE_LOCATION,
+            Manifest.permission.ACCESS_FINE_LOCATION
+        )
+        if (permissions.any { ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED }) {
+            if (permissions.any { shouldShowRequestPermissionRationale(it) }) {
+                showLocationPermissionRationaleDialog() // 显示权限理由说明对话框
+            }
+            requestLocationPermissionLauncher.launch(permissions) // 启动权限请求
+        } else {
+            startLocationUpdates() // 权限已授予,开始定位更新
+        }
+    }
+
+    /**
+     * 开始定位更新
+     */
+    private fun startLocationUpdates() {
+        clientOption.locationMode = AMapLocationClientOption.AMapLocationMode.Hight_Accuracy // 设置高精度定位模式
+        clientOption.isOnceLocation = false // 设置为false,持续定位
+        clientOption.interval = 2000 // 定位间隔2秒,可调整
+        clientOption.isNeedAddress = true // 是否需要地址信息
+        locationClient?.setLocationOption(clientOption) // 设置定位参数
+        locationClient?.startLocation() // 开始定位
+    }
+
+    override fun onLocationChanged(aMapLocation: AMapLocation) {
+        if (aMapLocation.errorCode == AMapLocation.LOCATION_SUCCESS) {
+            locationChangedListener.onLocationChanged(aMapLocation) // 定位成功,回调定位结果
+        } else {
+//            Log.e("AmapError", "定位失败: ${aMapLocation.errorCode} - ${aMapLocation.errorInfo}")
+            showErrorInfo(R.string.location_error)
+        }
+    }
+
+    private fun showLocationPermissionRationaleDialog() {
+        CustomAlertDialog.Builder(this)
+            .setTitle(R.string.need_permission)
+            .setMessage(R.string.location_permission_hint)
+            .setPositiveButton(R.string.confirm) { _, _ ->
+                requestLocationPermissionLauncher.launch(arrayOf(
+                    Manifest.permission.ACCESS_COARSE_LOCATION,
+                    Manifest.permission.ACCESS_FINE_LOCATION
+                ))
+            }
+            .setNegativeButton(R.string.cancel) { dialog, _ ->
+                dialog.dismiss()
+            }
+            .show()
+    }
+
+    override fun activate(listener: OnLocationChangedListener) {
+        locationChangedListener = listener
+        locationClient?.startLocation()
+    }
+
+    override fun deactivate() {
+        locationClient?.stopLocation()
+    }
+}

+ 0 - 289
app/src/main/java/com/pan_american/android/ui/customer_resource/customer_visit/CustomerDistributionMap.kt

@@ -1,289 +0,0 @@
-package com.pan_american.android.ui.customer_resource.customer_visit
-
-import android.Manifest
-import android.graphics.Color
-import android.os.Build
-import android.os.Bundle
-import android.util.Log
-import com.amap.api.location.AMapLocation
-import com.amap.api.location.AMapLocationClient
-import com.amap.api.location.AMapLocationClientOption
-import com.amap.api.location.AMapLocationListener
-import com.amap.api.maps.AMap
-import com.amap.api.maps.LocationSource
-import com.amap.api.maps.LocationSource.OnLocationChangedListener
-import com.amap.api.maps.UiSettings
-import com.amap.api.maps.model.MyLocationStyle
-import com.pan_american.android.OASystem
-import com.pan_american.android.R
-import com.pan_american.android.base.BaseActivity
-import com.pan_american.android.databinding.ActivityCustomerDistributionMapBinding
-import com.pan_american.android.databinding.LayoutTitleBinding
-import pub.devrel.easypermissions.AfterPermissionGranted
-import pub.devrel.easypermissions.EasyPermissions
-
-class CustomerDistributionMap : BaseActivity<ActivityCustomerDistributionMapBinding>(), AMapLocationListener, LocationSource, EasyPermissions.PermissionCallbacks {
-
-    private lateinit var titleBinding: LayoutTitleBinding
-
-    private lateinit var locationClient: AMapLocationClient
-
-    private lateinit var clientOption: AMapLocationClientOption
-
-    private lateinit var aMap: AMap
-
-    private lateinit var locationChangedListener: OnLocationChangedListener
-
-    private val myLocationStyle = MyLocationStyle()
-
-    private lateinit var uiSettings: UiSettings
-
-    override fun getViewBinding() = ActivityCustomerDistributionMapBinding.inflate(layoutInflater)
-
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-
-        binding.mapView.onCreate(savedInstanceState)
-
-        initTitle()
-
-        //初始化定位
-        initLocation()
-
-        //初始化地图
-        initMap(savedInstanceState)
-
-        //检查Android 版本
-        checkingAndroidVersion()
-
-        initEvents()
-    }
-
-    override fun initTitle() {
-        titleBinding = LayoutTitleBinding.bind(binding.root).apply {
-            titleText.text = resources.getString(R.string.customer_distribution_map)
-
-            backButton.setOnClickListener {
-                back()
-            }
-        }
-    }
-
-    override fun initViews() {
-
-    }
-
-    override fun initEvents() {
-
-    }
-
-    /**
-     * 检查Android版本
-     */
-    private fun checkingAndroidVersion() {
-        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
-            //Android6.0及以上先获取权限再定位
-            requestPermission()
-        }else {
-            //Android6.0以下直接定位
-            locationClient.startLocation()
-        }
-    }
-
-    /**
-     * 动态请求权限
-     */
-    @AfterPermissionGranted(OASystem.LOCATION_REQUEST_CODE)
-    private fun requestPermission() {
-        val permissions = arrayOf(
-            Manifest.permission.ACCESS_COARSE_LOCATION,
-            Manifest.permission.ACCESS_FINE_LOCATION,
-            Manifest.permission.READ_PHONE_STATE,
-            Manifest.permission.WRITE_EXTERNAL_STORAGE
-        )
-
-        if (EasyPermissions.hasPermissions(this, *permissions)) {
-            //true 有权限 开始定位
-            //  这里不需要调用 startLocation(),移到 onPermissionsGranted 中
-            // 停止之前的定位,为 onPermissionsGranted 中的定位做准备
-            locationClient.stopLocation()
-        } else {
-            //false 无权限
-            EasyPermissions.requestPermissions(this, "使用该界面需要您开启定位权限", OASystem.LOCATION_REQUEST_CODE, *permissions)
-        }
-    }
-
-    /**
-     * 请求权限结果
-     */
-    override fun onRequestPermissionsResult(
-        requestCode: Int,
-        permissions: Array<out String>,
-        grantResults: IntArray
-    ) {
-        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
-        //设置权限请求结果
-        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this)
-    }
-
-    override fun onPermissionsGranted(requestCode: Int, perms: MutableList<String>) {
-        if (requestCode == OASystem.LOCATION_REQUEST_CODE) {
-            locationClient.stopLocation() // 停止之前的定位请求 (非常重要!)
-            clientOption.setOnceLocation(true) // 只定位一次
-            locationClient.setLocationOption(clientOption) // 重新设置 option
-            locationClient.startLocation() // 启动定位
-        }
-    }
-
-    override fun onPermissionsDenied(requestCode: Int, perms: MutableList<String>) {
-
-    }
-
-    /**
-     * 初始化定位
-     */
-    private fun initLocation() {
-        //初始化定位
-        try {
-            locationClient = AMapLocationClient(OASystem.context)
-        } catch (e: Exception) {
-            e.printStackTrace()
-        }
-
-        //设置定位回调监听
-        locationClient.setLocationListener(this)
-        //初始化AMapLocationClientOption对象
-        clientOption = AMapLocationClientOption()
-        //设置定位模式为AMapLocationMode.Height_Accuracy,高精度模式。
-        clientOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy)
-        //获取最近3s内精度最高的一次定位结果:
-        //设置setOnceLocationLatest(boolean b)接口为true,启动定位时SDK会返回最近3s内精度最高的一次定位结果。
-        // 如果设置其为true,setOnceLocation(boolean b)接口也会被设置为true,反之不会,默认为false。
-        clientOption.setOnceLocationLatest(true)
-        //设置是否返回地址信息(默认返回地址信息)
-        clientOption.setNeedAddress(true)
-        //设置定位请求超时时间,单位是毫秒,默认30000毫秒,建议超时时间不要低于8000毫秒。
-        clientOption.setHttpTimeOut(20000)
-        //关闭缓存机制,高精度定位会产生缓存。
-        clientOption.setLocationCacheEnable(false)
-        //给定位客户端对象设置定位参数
-        locationClient.setLocationOption(clientOption)
-    }
-
-    /**
-     * 接收异步返回的定位结果
-     */
-    @Override
-    override fun onLocationChanged(aMapLocation: AMapLocation) {
-        if (aMapLocation.errorCode == 0) {
-            //地址
-//            val address = aMapLocation.address
-//
-//            val latitude = aMapLocation.latitude
-//
-//            val longitude = aMapLocation.longitude
-
-//            Log.e("location", "纬度: $latitude\n经度: $longitude\n地址: $address")
-
-            //停止定位后,本地服务并不会被销毁
-            // 不需要手动停止定位了,因为已经设置为单次定位
-            //locationClient.stopLocation()
-
-            //显示地图定位结果
-            locationChangedListener.onLocationChanged(aMapLocation)
-
-        } else {
-            //定位失败时,可通过ErrCode(错误码)信息来确定失败的原因,errInfo是错误信息,详见错误码表。
-            Log.e("AmapError", "location Error, ErrCode:"
-                    + aMapLocation.errorCode + ", errInfo:"
-                    + aMapLocation.errorInfo
-            )
-        }
-    }
-
-    /**
-     * 初始化地图
-     */
-    private fun initMap(savedInstanceState: Bundle?) {
-
-        //在activity执行onCreate时执行mMapView.onCreate(savedInstanceState),创建地图
-
-        binding.mapView.onCreate(savedInstanceState)
-
-        aMap = binding.mapView.map
-
-        //设置最小缩放级别
-        aMap.minZoomLevel = 3.0f
-
-        // 自定义精度范围的圆形边框颜色  都为0则透明
-        myLocationStyle.strokeColor(Color.argb(0, 0, 0, 0))
-
-        // 自定义精度范围的圆形边框宽度  0 无宽度
-        myLocationStyle.strokeWidth(0.0f)
-
-        // 设置圆形的填充颜色  都为0则透明
-        myLocationStyle.radiusFillColor(Color.argb(0, 0, 0, 0))
-
-        //设置定位蓝点的Style
-        aMap.myLocationStyle = myLocationStyle
-
-        // 设置定位监听
-        aMap.setLocationSource(this)
-
-        // 设置为true表示显示定位层并可触发定位,false表示隐藏定位层并不可触发定位,默认是false
-        aMap.isMyLocationEnabled = true
-
-        //实例化UiSettings类对象
-        uiSettings = aMap.uiSettings
-
-        //隐藏缩放按钮
-        uiSettings.isZoomControlsEnabled = false
-
-        //显示比例尺 默认不显示
-        uiSettings.isScaleControlsEnabled = true
-    }
-
-    /**
-     * 激活定位
-     */
-    override fun activate(listener: OnLocationChangedListener) {
-        locationChangedListener = listener
-
-        locationClient.startLocation()
-    }
-
-    /**
-     * 停止定位
-     */
-    override fun deactivate() {
-        locationClient.stopLocation()
-
-        locationClient.onDestroy()
-    }
-
-    override fun onResume() {
-        super.onResume()
-
-        binding.mapView.onResume()
-    }
-
-    override fun onPause() {
-        super.onPause()
-
-        binding.mapView.onPause()
-    }
-
-    override fun onSaveInstanceState(outState: Bundle) {
-        super.onSaveInstanceState(outState)
-
-        binding.mapView.onSaveInstanceState(outState)
-    }
-
-    override fun onDestroy() {
-        super.onDestroy()
-
-        locationClient.onDestroy()
-
-        binding.mapView.onDestroy()
-    }
-}

+ 18 - 20
app/src/main/java/com/pan_american/android/ui/group_management/group_info/AddGroupInfoActivity.kt

@@ -15,7 +15,6 @@ import androidx.activity.result.contract.ActivityResultContracts
 import androidx.core.app.ActivityCompat
 import androidx.core.content.ContextCompat
 import androidx.core.content.FileProvider
-import com.google.gson.Gson
 import com.pan_american.android.OASystem
 import com.pan_american.android.R
 import com.pan_american.android.base.BaseActivity
@@ -41,7 +40,6 @@ import retrofit2.Callback
 import retrofit2.Response
 import java.io.ByteArrayOutputStream
 import java.io.File
-import java.io.FileWriter
 
 class AddGroupInfoActivity : BaseActivity<ActivityAddGroupInfoBinding>() {
 
@@ -324,15 +322,15 @@ class AddGroupInfoActivity : BaseActivity<ActivityAddGroupInfoBinding>() {
 
         } while (!base64Adjusted)
 
-        val file = File(filesDir, "data.txt")
-        if (file.exists()) {
-            file.delete()
-        }
-        file.createNewFile()
-        val writer = FileWriter(file)
-        writer.append(Gson().toJson(GroupCustomerListRequest(transferBase64)))
-        writer.flush()
-        writer.close()
+//        val file = File(filesDir, "data.txt")
+//        if (file.exists()) {
+//            file.delete()
+//        }
+//        file.createNewFile()
+//        val writer = FileWriter(file)
+//        writer.append(Gson().toJson(GroupCustomerListRequest(transferBase64)))
+//        writer.flush()
+//        writer.close()
 
         apiService.getGroupCustomerList(GroupCustomerListRequest(transferBase64))
             .enqueue(object : Callback<GroupCustomerListResponse> {
@@ -373,15 +371,15 @@ class AddGroupInfoActivity : BaseActivity<ActivityAddGroupInfoBinding>() {
     @Subscribe(threadMode = ThreadMode.MAIN)
     fun groupOperate(groupOperationRequest: GroupOperationRequest) {
 
-        val file = File(filesDir, "data.txt")
-        if (file.exists()) {
-            file.delete()
-        }
-        file.createNewFile()
-        val writer = FileWriter(file)
-        writer.append(Gson().toJson(groupOperationRequest))
-        writer.flush()
-        writer.close()
+//        val file = File(filesDir, "data.txt")
+//        if (file.exists()) {
+//            file.delete()
+//        }
+//        file.createNewFile()
+//        val writer = FileWriter(file)
+//        writer.append(Gson().toJson(groupOperationRequest))
+//        writer.flush()
+//        writer.close()
 
         apiService.groupOperation(groupOperationRequest).enqueue(object : Callback<BaseResponse> {
             override fun onResponse(call: Call<BaseResponse>, response: Response<BaseResponse>) {

+ 50 - 0
app/src/main/java/com/pan_american/android/ui/group_management/group_info/GroupInfoBaseFragment.kt

@@ -202,6 +202,56 @@ class GroupInfoBaseFragment : BaseFragment<FragmentGroupInfoBaseBinding>() {
                                 OASystem.groupOperationRequest.teamLevSId = id
                             }
 
+                            when(OASystem.groupOperationRequest.teamLevSId) {
+                                770 -> {
+                                    for (item in OASystem.groupInfoOpPercentageLevel) {
+                                        if (item.name == "300") {
+                                            OASystem.groupOperationRequest.opRoyaltyLv = item.id
+
+                                            binding.opPercentageLevel.text = item.name
+
+                                            opPercentageSelected = true
+
+                                            opPercentageSubLevel = item.remark.split("&")
+
+                                            binding.levelIllustrate.text = opPercentageSubLevel.first()
+                                        }
+                                    }
+                                }
+
+                                771 -> {
+                                    for (item in OASystem.groupInfoOpPercentageLevel) {
+                                        if (item.name == "500") {
+                                            OASystem.groupOperationRequest.opRoyaltyLv = item.id
+
+                                            binding.opPercentageLevel.text = item.name
+
+                                            opPercentageSelected = true
+
+                                            opPercentageSubLevel = item.remark.split("&")
+
+                                            binding.levelIllustrate.text = opPercentageSubLevel.first()
+                                        }
+                                    }
+                                }
+
+                                772 -> {
+                                    for (item in OASystem.groupInfoOpPercentageLevel) {
+                                        if (item.name == "1000") {
+                                            OASystem.groupOperationRequest.opRoyaltyLv = item.id
+
+                                            binding.opPercentageLevel.text = item.name
+
+                                            opPercentageSelected = true
+
+                                            opPercentageSubLevel = item.remark.split("&")
+
+                                            binding.levelIllustrate.text = opPercentageSubLevel.first()
+                                        }
+                                    }
+                                }
+                            }
+
                             popupWindow.dismiss()
                         }
                     }

+ 138 - 157
app/src/main/java/com/pan_american/android/ui/workspace/WorkspaceActivity.kt

@@ -1,32 +1,29 @@
 package com.pan_american.android.ui.workspace
 
-import android.Manifest.permission.POST_NOTIFICATIONS
+import android.Manifest
 import android.annotation.SuppressLint
 import android.app.NotificationChannel
 import android.app.NotificationManager
 import android.app.PendingIntent
-import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
-import android.content.ServiceConnection
 import android.content.pm.PackageManager
 import android.graphics.BitmapFactory
 import android.media.RingtoneManager
 import android.net.Uri
 import android.os.Build
 import android.os.Bundle
-import android.os.IBinder
 import android.os.PowerManager
 import android.os.Process
 import android.provider.Settings
 import androidx.activity.OnBackPressedCallback
 import androidx.activity.result.ActivityResultLauncher
 import androidx.activity.result.contract.ActivityResultContracts
-import androidx.core.app.ActivityCompat
 import androidx.core.app.NotificationCompat
 import androidx.core.app.NotificationManagerCompat
 import androidx.core.content.res.ResourcesCompat
 import androidx.fragment.app.Fragment
+import androidx.lifecycle.lifecycleScope
 import androidx.viewpager2.widget.ViewPager2
 import com.pan_american.android.OASystem
 import com.pan_american.android.R
@@ -40,11 +37,11 @@ import com.pan_american.android.databinding.ActivityWorkspaceBinding
 import com.pan_american.android.ui.announcement.AnnouncementFragment
 import com.pan_american.android.ui.document.DocumentFragment
 import com.pan_american.android.ui.message.MessageFragment
+import kotlinx.coroutines.launch
 import org.greenrobot.eventbus.EventBus
 import org.greenrobot.eventbus.Subscribe
 import org.greenrobot.eventbus.ThreadMode
 
-
 class WorkspaceActivity : BaseActivity<ActivityWorkspaceBinding>() {
 
     private var exitTime: Long = 0
@@ -57,19 +54,37 @@ class WorkspaceActivity : BaseActivity<ActivityWorkspaceBinding>() {
 
     private var announcementNotification = false
 
-    //监听通知事件回调
-    private lateinit var result: ActivityResultLauncher<Intent>
+    private val requestPermissionLauncher =
+        registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
+            if (isGranted) {
+                startTCPService()
+
+                if (!isIgnoringBatteryOptimizations()) {
+                    showIgnoreBatteryDialog()
+                }
 
-    private val connection = object : ServiceConnection {
-        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
-            val clientBinder = service as TCPService.ClientBinder
-            clientBinder.startConnect()
+            } else {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
+                    !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)
+                ) {
+                    showNotificationPermissionDialog() // 只在永久拒绝时显示对话框
+                } else {
+                    // 可以在这里添加其他处理
+                    showMessage(resources.getString(R.string.notification_permission_denied_hint))
+                }
+            }
         }
 
-        override fun onServiceDisconnected(p0: ComponentName?) {
-//            Log.e("TCPService", "disconnected")
+    // 用于启动忽略电池优化设置页面的 ActivityResultLauncher
+    private val requestIgnoreBatteryOptimizationsLauncher: ActivityResultLauncher<Intent> =
+        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
+            // 在用户从设置页面返回后检查是否已启用忽略电池优化
+            if (isIgnoringBatteryOptimizations()) {
+                //do something
+            } else {
+                showMessage(getString(R.string.background_execute_alert_message))
+            }
         }
-    }
 
     override fun getViewBinding() = ActivityWorkspaceBinding.inflate(layoutInflater)
 
@@ -90,20 +105,13 @@ class WorkspaceActivity : BaseActivity<ActivityWorkspaceBinding>() {
 
         onBackPressedDispatcher.addCallback(this, callback)
 
-        result = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
-            if (NotificationManagerCompat.from(this).areNotificationsEnabled()) {
-                val intent = Intent(OASystem.context, TCPService::class.java)
-                bindService(intent, connection, BIND_AUTO_CREATE)
-
-                showIgnoreBatteryDialog()
-            }
-        }
-
         if (!EventBus.getDefault().isRegistered(this)) {
             EventBus.getDefault().register(this)
         }
 
         initBottomNavigation()
+
+        createNotificationChannel()
     }
 
     override fun onStart() {
@@ -133,36 +141,111 @@ class WorkspaceActivity : BaseActivity<ActivityWorkspaceBinding>() {
 
     override fun onResume() {
         super.onResume()
+        requestPermissionsIfNeeded()
+        initViews()
+    }
+
+    private fun showNotificationPermissionDialog() {
+        CustomAlertDialog.Builder(this)
+            .setTitle(R.string.alert) // 使用资源 ID
+            .setMessage(R.string.notification_permission_denied_hint) // 使用资源 ID
+            .setPositiveButton(R.string.go_to_open) { _, _ ->
+                openNotificationSettings()
+            }
+            .setNegativeButton(R.string.cancel, null)
+            .show()
+    }
+
+    private fun openNotificationSettings() {
+        val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
+            data = Uri.fromParts("package", packageName, null)
+        }
+        startActivity(intent)
+    }
+
+    private fun requestPermissionsIfNeeded() {
+        if (!hasNotificationPermission()) {
+            requestNotificationPermission()
+        } else if (!isIgnoringBatteryOptimizations()) {
+            showIgnoreBatteryDialog()
+        }
+    }
 
-        //通知权限判断,如果在没杀app的情况下重新打开通知权限,则开启TCPService
+    private fun hasNotificationPermission(): Boolean {
+        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+            checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED
+        } else {
+            true
+        }
+    }
+
+    private fun requestNotificationPermission() {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
-            if (ActivityCompat.checkSelfPermission(
-                    this, POST_NOTIFICATIONS
-                ) == PackageManager.PERMISSION_DENIED
-            ) {
-                if (!ActivityCompat.shouldShowRequestPermissionRationale(
-                        this, POST_NOTIFICATIONS
-                    )
-                ) {
-                    showNotificationPermissionDialog()
-                } else {
-                    val intent = Intent(OASystem.context, TCPService::class.java)
-                    bindService(intent, connection, BIND_AUTO_CREATE)
-                }
-            } else {
+            requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
+        } else {
+            startTCPService()
+        }
+    }
+
+    private fun startTCPService() {
+        lifecycleScope.launch {
+            if (areNotificationsEnabled()) {
                 val intent = Intent(OASystem.context, TCPService::class.java)
-                bindService(intent, connection, BIND_AUTO_CREATE)
+                intent.action = TCPService.ACTION_START_CONNECTION
+                OASystem.context.startService(intent)
+            } else {
+                showNotificationSettings()
             }
-        } else {
-            if (!NotificationManagerCompat.from(this).areNotificationsEnabled()) {
-                showNotificationPermissionDialog()
+        }
+    }
+
+    private fun areNotificationsEnabled(): Boolean {
+        return NotificationManagerCompat.from(this).areNotificationsEnabled()
+    }
+
+    private fun showNotificationSettings() { //  跳转到通知设置页面
+        val intent = Intent().apply {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+                action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
+                putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
             } else {
-                val intent = Intent(OASystem.context, TCPService::class.java)
-                bindService(intent, connection, BIND_AUTO_CREATE)
+                action = "android.settings.APP_NOTIFICATION_SETTINGS"
+                putExtra("app_package", packageName)
+                putExtra("app_uid", applicationInfo.uid)
             }
         }
+        startActivity(intent)
+    }
 
-        initViews()
+    private fun isIgnoringBatteryOptimizations(): Boolean {
+        val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
+        return powerManager.isIgnoringBatteryOptimizations(packageName)
+    }
+
+    @SuppressLint("BatteryLife")
+    private fun showIgnoreBatteryDialog() {
+        if (!isIgnoringBatteryOptimizations()) {
+            CustomAlertDialog.Builder(this).apply {
+                setTitle(resources.getString(R.string.alert))
+                setMessage(resources.getString(R.string.background_execute_alert_message))
+                setNegativeButtonAndListener(resources.getString(R.string.cancel)) { dialog, _ -> dialog.dismiss() }
+                setPositiveButtonAndListener(resources.getString(R.string.go_to_open)) { dialog, _ ->
+                    dialog.dismiss()
+                    requestIgnoreBatteryOptimizations()
+                }
+            }.show()
+        }
+    }
+
+    @SuppressLint("BatteryLife")
+    private fun requestIgnoreBatteryOptimizations() {
+        // 创建 Intent 跳转到忽略电池优化设置页面
+        val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
+            data = Uri.parse("package:$packageName")
+        }
+
+        // 使用 ActivityResultLauncher 启动 Intent
+        requestIgnoreBatteryOptimizationsLauncher.launch(intent)
     }
 
     override fun onPause() {
@@ -264,79 +347,6 @@ class WorkspaceActivity : BaseActivity<ActivityWorkspaceBinding>() {
         }
     }
 
-    private fun showNotificationPermissionDialog() {
-
-        CustomAlertDialog.Builder(this).apply {
-            setTitle(resources.getString(R.string.alert))
-            setMessage(resources.getString(R.string.notification_permission_denied_hint))
-            setNegativeButtonAndListener(resources.getString(R.string.cancel)) { dialog, _ ->
-                dialog.dismiss()
-            }
-            setPositiveButtonAndListener(resources.getString(R.string.go_to_open)) { dialog, _ ->
-                dialog.dismiss()
-                openNotificationSettingsForApp()
-            }
-        }.show()
-    }
-
-    private fun openNotificationSettingsForApp() {
-
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
-            if (ActivityCompat.checkSelfPermission(
-                    this, POST_NOTIFICATIONS
-                ) == PackageManager.PERMISSION_DENIED
-            ) {
-                if (!ActivityCompat.shouldShowRequestPermissionRationale(
-                        this, POST_NOTIFICATIONS
-                    )
-                ) {
-                    ActivityCompat.requestPermissions(
-                        this, arrayOf(POST_NOTIFICATIONS), OASystem.NOTIFICATION_REQUEST_CODE
-                    )
-                } else {
-                    val intent = Intent(OASystem.context, TCPService::class.java)
-                    bindService(intent, connection, BIND_AUTO_CREATE)
-                }
-            } else {
-                val intent = Intent(OASystem.context, TCPService::class.java)
-                bindService(intent, connection, BIND_AUTO_CREATE)
-            }
-        } else {
-            if (!NotificationManagerCompat.from(this).areNotificationsEnabled()) {
-                val intent = Intent()
-                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-                    intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
-                    intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
-                } else {
-                    intent.action = "android.settings.APP_NOTIFICATION_SETTINGS"
-                    intent.putExtra("app_package", packageName)
-                    intent.putExtra("app_uid", applicationInfo.uid)
-                }
-                result.launch(intent)
-            } else {
-                val intent = Intent(OASystem.context, TCPService::class.java)
-                bindService(intent, connection, BIND_AUTO_CREATE)
-            }
-        }
-    }
-
-    override fun onRequestPermissionsResult(
-        requestCode: Int, permissions: Array<out String>, grantResults: IntArray
-    ) {
-        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
-        if (requestCode == OASystem.NOTIFICATION_REQUEST_CODE) {
-            if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
-                val intent = Intent(OASystem.context, TCPService::class.java)
-                bindService(intent, connection, BIND_AUTO_CREATE)
-
-                showIgnoreBatteryDialog()
-
-            } else {
-                showMessage(resources.getString(R.string.notification_permission_denied_hint))
-            }
-        }
-    }
-
     private fun createNotificationChannel() {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
             val name = "PanAmerica_Notification_Channel"
@@ -368,16 +378,11 @@ class WorkspaceActivity : BaseActivity<ActivityWorkspaceBinding>() {
             priority = NotificationCompat.PRIORITY_HIGH
             val intent = Intent(OASystem.context, WorkspaceActivity::class.java).apply {
                 when (messageGetter.messageType) {
-                    1 -> {
-                        putExtra("operation_notification", true)
-                        flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
-                    }
-
-                    2 -> {
-                        putExtra("announcement_notification", true)
-                        flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
-                    }
+                    1 -> putExtra("operation_notification", true)
+                    2 -> putExtra("announcement_notification", true)
                 }
+// 使用 singleTop 避免重复创建 Activity
+                flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
             }
             val pendingIntent: PendingIntent = PendingIntent.getActivity(
                 OASystem.context,
@@ -422,44 +427,20 @@ class WorkspaceActivity : BaseActivity<ActivityWorkspaceBinding>() {
     fun showNotificationInService(messageGetter: MessageGetter) {
 
         if (messageGetter.message.isNotBlank()) {
-            manager = this.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
+            // 仅在需要时创建 NotificationManager
+            if (!::manager.isInitialized) {  // 使用 isInitialized 检查是否已初始化
+                manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
+            }
 
             if (isActive) {
                 initUnReadCount()
             }
 
-            createNotificationChannel()
+            createNotificationChannel() // 确保只创建一次 Channel
             showNotification(messageGetter)
         }
     }
 
-    private fun isIgnoringBatteryOptimizations(): Boolean {
-        val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
-        return powerManager.isIgnoringBatteryOptimizations(packageName)
-    }
-
-    @SuppressLint("BatteryLife")
-    private fun showIgnoreBatteryDialog() {
-        if (!isIgnoringBatteryOptimizations()) {
-
-            CustomAlertDialog.Builder().apply {
-                setTitle(resources.getString(R.string.alert))
-                setMessage(resources.getString(R.string.background_execute_alert_message))
-                setNegativeButtonAndListener(resources.getString(R.string.cancel)) { dialog, _ ->
-                    dialog.dismiss()
-                }
-                setPositiveButtonAndListener(resources.getString(R.string.go_to_open)) { dialog, _ ->
-
-                    val ignoreBatteryIntent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
-                    ignoreBatteryIntent.data = Uri.parse("package:$packageName")
-                    startActivity(ignoreBatteryIntent)
-
-                    dialog.dismiss()
-                }
-            }.show()
-        }
-    }
-
     @Subscribe(threadMode = ThreadMode.MAIN)
     fun refreshNavigationBride(requestCodeBean: RequestCodeBean) {
         if (requestCodeBean.code == OASystem.REFRESH_NAVIGATION) {

+ 8 - 4
app/src/main/java/com/pan_american/android/ui/workspace/WorkspaceFragment.kt

@@ -15,7 +15,7 @@ import com.pan_american.android.base.BaseFragment
 import com.pan_american.android.base.CustomAlertDialog
 import com.pan_american.android.databinding.FragmentWorkspaceBinding
 import com.pan_american.android.ui.customer_resource.company_customer.MarketCustomerActivity
-import com.pan_american.android.ui.customer_resource.customer_visit.CustomerDistributionMap
+import com.pan_american.android.ui.customer_resource.customer_distribution.CustomerDistributionMap
 import com.pan_american.android.ui.customer_resource.market_sales_revenue.MarketSalesRevenueActivity
 import com.pan_american.android.ui.customer_resource.related_invitee.RelatedInviteeActivity
 import com.pan_american.android.ui.efficiency_tools.address_book.AddressBookActivity
@@ -47,6 +47,8 @@ import com.pan_american.android.ui.resource_management.hotel_resource.HotelResou
 
 class WorkspaceFragment : BaseFragment<FragmentWorkspaceBinding>(), OnClickListener {
 
+    private val isDebugMode = false
+
     override fun getViewBinding(
         inflater: LayoutInflater, container: ViewGroup?, bundle: Bundle?
     ) = FragmentWorkspaceBinding.inflate(inflater, container, false)
@@ -289,8 +291,10 @@ class WorkspaceFragment : BaseFragment<FragmentWorkspaceBinding>(), OnClickListe
                         binding.companyCustomerResource.visibility = View.VISIBLE
                         binding.companyCustomerResource.setOnClickListener(this)
 
-                        binding.customerVisits.visibility = View.VISIBLE
-                        binding.customerVisits.setOnClickListener(this)
+                        if (isDebugMode) {
+                            binding.customerDistribution.visibility = View.VISIBLE
+                            binding.customerDistribution.setOnClickListener(this)
+                        }
                     }
                 }
 
@@ -470,7 +474,7 @@ class WorkspaceFragment : BaseFragment<FragmentWorkspaceBinding>(), OnClickListe
                 startActivity(intent)
             }
 
-            binding.customerVisits.id -> {
+            binding.customerDistribution.id -> {
                 val intent = Intent(OASystem.context, CustomerDistributionMap::class.java)
                 startActivity(intent)
             }

+ 1 - 1
app/src/main/res/layout/fragment_workspace.xml

@@ -1047,7 +1047,7 @@
                     </LinearLayout>
 
                     <LinearLayout
-                        android:id="@+id/customer_visits"
+                        android:id="@+id/customer_distribution"
                         android:layout_width="match_parent"
                         android:layout_height="wrap_content"
                         android:orientation="vertical"

+ 15 - 2
app/src/main/res/values/strings.xml

@@ -146,10 +146,20 @@
     <string name="hint">提示</string>
     <string name="document_generated">文档已生成, 点击确认跳转至浏览器下载\n\n团组名称: %s\n\n文档类型: %s</string>
 
+    <string name="need_permission">需要权限</string>
+
     <string name="camera_permission_hint">请在设置中打开相机权限</string>
+
     <string name="file_manager_permission_hint">检测到未赋予文件管理权限,请在设置中打开文件管理权限,点击确定跳转到设置界面</string>
+
     <string name="permission_denied">未拥有该权限, 请前往设置界面打开权限</string>
 
+    <string name="location_permission_hint">此应用需要位置权限才能显示客户的分布位置, 请允许此权限</string>
+
+    <string name="gallery_permission_hint">请在设置中打开文件访问权限</string>
+
+    <string name="notification_permission_denied_hint">检测到未开启通知权限,请前往设置页面开启</string>
+
     <!-- 网络错误 -->
     <string name="network_error">%s,请检查网络设置</string>
     <string name="base_resource_data_get_failed">基础数据源获取失败</string>
@@ -903,7 +913,6 @@
     <string name="task_operation_notification">任务操作通知</string>
     <string name="group_operation_notification">团组操作通知</string>
 
-    <string name="notification_permission_denied_hint">检测到未开启通知权限,请前往设置页面开启。</string>
     <string name="go_to_open">去开启</string>
 
     <string name="message_max_count">99+</string>
@@ -1121,7 +1130,7 @@
     <string name="confirm_success">确认成功</string>
     <string name="operate_success">操作成功</string>
 
-    <string name="gallery_permission_hint">请在设置中打开文件访问权限</string>
+
 
     <string name="country_input_hint">请输入国家</string>
     <string name="area_input_hint">请输入地区</string>
@@ -1383,6 +1392,10 @@
 
     <!-- 客户分布图 -->
     <string name="customer_distribution_map">客户分布图</string>
+    
+    <string name="location_permission_denied_hint">检测到未打开定位权限,请前往设置打开定位权限</string>
+
+    <string name="location_error">定位失败</string>
 
     <!-- TODO: Remove or change this placeholder text -->
     <string name="hello_blank_fragment">Hello blank fragment</string>