diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 34fb441..ea2679f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -30,7 +30,7 @@ android { } // 设置输出APK文件名格式 - setProperty("archivesBaseName", "菜品配比终端v${versionName}") + setProperty("archivesBaseName", "pbc_v${versionName}") } signingConfigs { create("debug_507") { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a7fc2ef..23ff004 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ - + + + + android:exported="true" + android:directBootAware="true"> + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/ui/MasterScaleActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/MasterScaleActivity.kt index 55a28f9..d708ecf 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/MasterScaleActivity.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/MasterScaleActivity.kt @@ -9,6 +9,7 @@ import android.widget.TextView import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.shuwei.dish.match.R import com.shuwei.dish.match.base.BaseActivity import com.shuwei.dish.match.databinding.ActivityMasterScaleBinding import com.shuwei.dish.match.databinding.ListItemScaleDataBinding @@ -16,6 +17,7 @@ import com.shuwei.dish.match.scale.ScaleData import com.shuwei.dish.match.scale.ScaleServiceManager import com.shuwei.dish.match.utils.NetworkUtil import com.shuwei.dish.match.utils.WeightUtil +import com.shuwei.dish.match.utils.ext.dp import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch @@ -138,7 +140,7 @@ class MasterScaleActivity : BaseActivity() { layoutParams = LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT - ).also { it.topMargin = 4.dpToPx() } + ).also { it.topMargin = 4.dp } } row.addView(makeTextView("秤 ${scale.address}", 22, weight = 1f)) row.addView(makeTextView("${scale.weight} g", 26, bold = true, weight = 2f)) @@ -158,15 +160,11 @@ class MasterScaleActivity : BaseActivity() { textSize = spSize.toFloat() if (bold) setTypeface(null, android.graphics.Typeface.BOLD) setTextColor( - if (bold) getColor(com.shuwei.dish.match.R.color.home_title) - else getColor(com.shuwei.dish.match.R.color.home_sub_title) + if (bold) getColor(R.color.home_title) + else getColor(R.color.home_sub_title) ) layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, weight) } } - - /** dp 转 px 工具 */ - private fun Int.dpToPx(): Int = - (this * resources.displayMetrics.density + 0.5f).toInt() } diff --git a/app/src/main/java/com/shuwei/dish/match/ui/SettingActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/SettingActivity.kt index 3768583..6e9c3d7 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/SettingActivity.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/SettingActivity.kt @@ -81,9 +81,9 @@ class SettingActivity : BaseActivity() { updateButtonColors(isDeviceConfigSelected = false) } - // 设备角色只读展示,角色由设备 ID 决定,不可手动切换 - updateRoleButtonText() - binding.btnDeviceRole.isEnabled = false +// // 设备角色只读展示,角色由设备 ID 决定,不可手动切换 +// updateRoleButtonText() +// binding.btnDeviceRole.isEnabled = false // 主设备显示「秤数据监控」入口 if (GlobalData.deviceRole == DeviceRole.MASTER) { @@ -97,11 +97,11 @@ class SettingActivity : BaseActivity() { updateButtonColors(isDeviceConfigSelected = true) } - /** 刷新角色按钮文字 */ - private fun updateRoleButtonText() { - val roleLabel = if (GlobalData.deviceRole == DeviceRole.MASTER) "主设备" else "子设备" - binding.btnDeviceRole.text = "设备角色:$roleLabel" - } +// /** 刷新角色按钮文字 */ +// private fun updateRoleButtonText() { +// val roleLabel = if (GlobalData.deviceRole == DeviceRole.MASTER) "主设备" else "子设备" +// binding.btnDeviceRole.text = "设备角色:$roleLabel" +// } /** * 更新底部菜单按钮的颜色状态 diff --git a/app/src/main/java/com/shuwei/dish/match/utils/BootReceiver.kt b/app/src/main/java/com/shuwei/dish/match/utils/BootReceiver.kt index 35f6830..a2a5ee3 100644 --- a/app/src/main/java/com/shuwei/dish/match/utils/BootReceiver.kt +++ b/app/src/main/java/com/shuwei/dish/match/utils/BootReceiver.kt @@ -3,15 +3,25 @@ package com.shuwei.dish.match.utils import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import com.shuwei.dish.match.ui.InitActivity class BootReceiver : BroadcastReceiver() { + + companion object { + /** 兼容多种设备的开机广播 action 集合 */ + private val BOOT_ACTIONS = setOf( + Intent.ACTION_BOOT_COMPLETED, + "android.intent.action.QUICKBOOT_POWERON", + "android.intent.action.LOCKED_BOOT_COMPLETED" + ) + } + override fun onReceive(context: Context, intent: Intent) { - if (Intent.ACTION_BOOT_COMPLETED == intent.action) { - // 启动服务或Activity - val launchIntent = Intent(context, InitActivity::class.java) - launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - context.startActivity(launchIntent) + val action = intent.action ?: return + if (action in BOOT_ACTIONS) { + // Android 10+ 禁止在后台直接启动 Activity + // 改为启动前台服务,由 BootService 负责拉起 InitActivity + val serviceIntent = Intent(context, BootService::class.java) + context.startForegroundService(serviceIntent) } } } diff --git a/app/src/main/java/com/shuwei/dish/match/utils/BootService.kt b/app/src/main/java/com/shuwei/dish/match/utils/BootService.kt new file mode 100644 index 0000000..7275c18 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/utils/BootService.kt @@ -0,0 +1,95 @@ +package com.shuwei.dish.match.utils + +import android.app.ActivityManager +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.app.Service +import android.content.Intent +import android.content.pm.ServiceInfo +import android.os.Build +import android.os.IBinder +import com.shuwei.dish.match.R +import com.shuwei.dish.match.ui.InitActivity + +/** + * 开机自启中转前台服务 + * 用于绕过 Android 10+ 对后台直接启动 Activity 的限制 + * 执行流程:BootReceiver → startForegroundService() → 本服务 → setFullScreenIntent → InitActivity + */ +class BootService : Service() { + + companion object { + /** 通知渠道 ID */ + private const val CHANNEL_ID = "boot_channel" + /** 前台通知 ID */ + private const val NOTIFICATION_ID = 1001 + } + + override fun onBind(intent: Intent?): IBinder? = null + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + // 若 App 进程已在前台运行(主设备系统签名场景),则不重复启动,避免闪退 + val activityManager = getSystemService(ACTIVITY_SERVICE) as ActivityManager + val isAppForeground = activityManager.runningAppProcesses?.any { + it.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND + && it.processName == packageName + } ?: false + + // App 未运行时才设置 fullScreenIntent,由系统自动启动 Activity + // 直接调用 startActivity() 在部分 Android 10 设备上会被 ActivityTaskManager 拦截 + val pendingIntent = if (!isAppForeground) createLaunchPendingIntent() else null + + // Android 14+ startForeground 需要传入 foregroundServiceType + val notification = buildNotification(pendingIntent) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC) + } else { + startForeground(NOTIFICATION_ID, notification) + } + + stopSelf() + return START_NOT_STICKY + } + + /** 创建启动 InitActivity 的 PendingIntent */ + private fun createLaunchPendingIntent(): PendingIntent { + val launchIntent = Intent(this, InitActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + return PendingIntent.getActivity( + this, 0, launchIntent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + } + + /** + * 构建前台服务通知 + * fullScreenIntent 不为空时设置全屏意图,系统会在显示通知时自动启动 Activity + * 通知渠道必须为 IMPORTANCE_HIGH,否则 fullScreenIntent 不会触发 + */ + private fun buildNotification(fullScreenIntent: PendingIntent?): Notification { + val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager + val channel = NotificationChannel( + CHANNEL_ID, + "开机自启动", + NotificationManager.IMPORTANCE_HIGH + ).apply { + description = "应用开机自动启动通知渠道" + setShowBadge(false) + } + manager.createNotificationChannel(channel) + + return Notification.Builder(this, CHANNEL_ID) + .setContentTitle("正在启动应用") + .setSmallIcon(R.mipmap.ic_logo512) + .apply { + if (fullScreenIntent != null) { + setFullScreenIntent(fullScreenIntent, true) + } + } + .build() + } +} + diff --git a/app/src/main/res/layout/activity_master_scale.xml b/app/src/main/res/layout/activity_master_scale.xml index 6373dce..86a2c62 100644 --- a/app/src/main/res/layout/activity_master_scale.xml +++ b/app/src/main/res/layout/activity_master_scale.xml @@ -11,7 +11,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" - android:layout_marginTop="20dp" + android:layout_marginTop="10dp" android:layout_marginBottom="10dp" android:text="全设备秤数据监控" android:textColor="@color/home_title" @@ -36,6 +36,7 @@ android:layout_height="0dp" android:layout_weight="1" android:overScrollMode="never" - android:padding="16dp" /> + android:padding="16dp" + tools:listitem="@layout/list_item_scale_data"/> diff --git a/app/src/main/res/layout/activity_setting.xml b/app/src/main/res/layout/activity_setting.xml index d1d36cb..c3b0d8d 100644 --- a/app/src/main/res/layout/activity_setting.xml +++ b/app/src/main/res/layout/activity_setting.xml @@ -37,14 +37,14 @@ android:textSize="26sp" android:background="@android:color/transparent" /> - + + + + + + + + + android:paddingTop="10dp"> @@ -37,4 +37,10 @@ android:orientation="vertical" android:layout_marginTop="5dp" /> + +