Compare commits

...

14 Commits

73 changed files with 1232 additions and 206 deletions

View File

@@ -0,0 +1,48 @@
kotlin version: 2.2.10
error message: java.nio.file.AccessDeniedException: C:\Users\Administrator\AppData\Local\kotlin\daemon\kotlin-daemon-client-tsmarker7700090853507788345.tmp
error message: Daemon compilation failed: Could not connect to Kotlin compile daemon
java.lang.RuntimeException: Could not connect to Kotlin compile daemon
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:214)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:74)
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:68)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:64)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:61)
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:102)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:61)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:58)
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:176)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
at org.gradle.internal.Factories$1.create(Factories.java:30)
at org.gradle.internal.work.DefaultWorkerLeaseService.lambda$withLocksAcquired$0(DefaultWorkerLeaseService.java:269)
at org.gradle.internal.work.ResourceLockStatistics$1.measure(ResourceLockStatistics.java:42)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocksAcquired(DefaultWorkerLeaseService.java:267)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:259)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1583)

View File

@@ -0,0 +1,48 @@
kotlin version: 2.2.10
error message: java.nio.file.AccessDeniedException: C:\Users\Administrator\AppData\Local\kotlin\daemon\kotlin-daemon-client-tsmarker12680940025563006602.tmp
error message: Daemon compilation failed: Could not connect to Kotlin compile daemon
java.lang.RuntimeException: Could not connect to Kotlin compile daemon
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:214)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:74)
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:68)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:64)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:61)
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:102)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:61)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:58)
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:176)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
at org.gradle.internal.Factories$1.create(Factories.java:30)
at org.gradle.internal.work.DefaultWorkerLeaseService.lambda$withLocksAcquired$0(DefaultWorkerLeaseService.java:269)
at org.gradle.internal.work.ResourceLockStatistics$1.measure(ResourceLockStatistics.java:42)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocksAcquired(DefaultWorkerLeaseService.java:267)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:259)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1583)

View File

@@ -0,0 +1,48 @@
kotlin version: 2.2.10
error message: java.nio.file.AccessDeniedException: C:\Users\Administrator\AppData\Local\kotlin\daemon\kotlin-daemon-client-tsmarker3894271637508823813.tmp
error message: Daemon compilation failed: Could not connect to Kotlin compile daemon
java.lang.RuntimeException: Could not connect to Kotlin compile daemon
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:214)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:74)
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:68)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:64)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:61)
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:102)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:61)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:58)
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:176)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
at org.gradle.internal.Factories$1.create(Factories.java:30)
at org.gradle.internal.work.DefaultWorkerLeaseService.lambda$withLocksAcquired$0(DefaultWorkerLeaseService.java:269)
at org.gradle.internal.work.ResourceLockStatistics$1.measure(ResourceLockStatistics.java:42)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocksAcquired(DefaultWorkerLeaseService.java:267)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:259)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1583)

View File

@@ -24,6 +24,7 @@
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/logo" android:icon="@mipmap/logo"
android:label="@string/app_name" android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.DefenseApplication"> android:theme="@style/Theme.DefenseApplication">
@@ -42,6 +43,9 @@
<activity <activity
android:name=".personal.InvitationCodeActivity" android:name=".personal.InvitationCodeActivity"
android:exported="false" /> android:exported="false" />
<activity
android:name=".personal.JoinFamilyInviteActivity"
android:exported="false" />
<activity <activity
android:name=".liveVideo.StrikePositionActivity" android:name=".liveVideo.StrikePositionActivity"
android:exported="false" /> android:exported="false" />

View File

@@ -18,6 +18,8 @@ class AddDeviceAdapter(
private val onItemClick: (Device) -> Unit private val onItemClick: (Device) -> Unit
) : RecyclerView.Adapter<AddDeviceAdapter.ViewHolder>() { ) : RecyclerView.Adapter<AddDeviceAdapter.ViewHolder>() {
private val defaultIcons = listOf(R.mipmap.img1, R.mipmap.img2, R.mipmap.img3)
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val ivIcon: ImageView = itemView.findViewById(R.id.ivDeviceIcon2) val ivIcon: ImageView = itemView.findViewById(R.id.ivDeviceIcon2)
val tvName: TextView = itemView.findViewById(R.id.tvDeviceName2) val tvName: TextView = itemView.findViewById(R.id.tvDeviceName2)
@@ -47,7 +49,9 @@ class AddDeviceAdapter(
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val device = devices[position] val device = devices[position]
holder.tvName.text = device.name holder.tvName.text = device.name
holder.ivIcon.setImageResource(R.mipmap.img)
val iconResId = defaultIcons[position % defaultIcons.size]
holder.ivIcon.setImageResource(iconResId)
// 点击事件 // 点击事件
holder.itemView.setOnClickListener { onItemClick(device) } holder.itemView.setOnClickListener { onItemClick(device) }

View File

@@ -3,25 +3,18 @@ package com.ckkj.defense_device.adapter
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.ckkj.defense_device.R import com.ckkj.defense_device.R
import com.ckkj.defense_device.bean.DeviceBean
import com.ckkj.defense_device.databinding.ItemDeviceBinding import com.ckkj.defense_device.databinding.ItemDeviceBinding
data class DeviceUi(
val name: String,
val status: String,
val deviceId: String = ""
) {
val isOnline: Boolean
get() = status == "1"
}
class DeviceAdapter( class DeviceAdapter(
private val onItemClick: (DeviceUi) -> Unit = {} private val onItemClick: (DeviceBean) -> Unit = {}
) : RecyclerView.Adapter<DeviceAdapter.DeviceViewHolder>() { ) : RecyclerView.Adapter<DeviceAdapter.DeviceViewHolder>() {
private val items = mutableListOf<DeviceUi>() private val items = mutableListOf<DeviceBean>()
fun submitList(deviceList: List<DeviceUi>) { fun submitList(deviceList: List<DeviceBean>) {
items.clear() items.clear()
items.addAll(deviceList) items.addAll(deviceList)
notifyDataSetChanged() notifyDataSetChanged()
@@ -33,15 +26,25 @@ class DeviceAdapter(
} }
override fun onBindViewHolder(holder: DeviceViewHolder, position: Int) { override fun onBindViewHolder(holder: DeviceViewHolder, position: Int) {
holder.bind(items[position]) holder.bind(items[position], position)
} }
override fun getItemCount(): Int = items.size override fun getItemCount(): Int = items.size
private val defaultIcons = listOf(R.mipmap.img1, R.mipmap.img2, R.mipmap.img3)
inner class DeviceViewHolder(private val binding: ItemDeviceBinding) : inner class DeviceViewHolder(private val binding: ItemDeviceBinding) :
RecyclerView.ViewHolder(binding.root) { RecyclerView.ViewHolder(binding.root) {
fun bind(item: DeviceUi) { fun bind(item: DeviceBean, position: Int) {
if (item.deviceImg.isNullOrEmpty()) {
val iconResId = defaultIcons[position % defaultIcons.size]
binding.ivDeviceIcon.setImageResource(iconResId)
} else {
Glide.with(binding.root.context)
.load(item.deviceImg)
.into(binding.ivDeviceIcon)
}
binding.tvDeviceType.text = item.name binding.tvDeviceType.text = item.name
when (item.status) { when (item.status) {
"1" -> { "1" -> {

View File

@@ -4,7 +4,7 @@ package com.ckkj.defense_device.bean
* 统一返回数据基类(根据你的后端实际字段调整) * 统一返回数据基类(根据你的后端实际字段调整)
*/ */
data class BaseInfo( data class BaseInfo(
val message: String? = null, val msg: String? = null,
val code: Int = 0, val code: Int = 0,
val data: Any? = null val data: Any? = null
) )

View File

@@ -27,4 +27,13 @@ data class DeviceBean(
val wifiPassword: String? = null,//WiFi密码 val wifiPassword: String? = null,//WiFi密码
val workMode: String? = null,//工作模式 0-自动 1-半自动 val workMode: String? = null,//工作模式 0-自动 1-半自动
val optionPassword: String? = null, //操作密码 val optionPassword: String? = null, //操作密码
) ) {
val deviceId: String
get() = id ?: ""
val name: String
get() = deviceName ?: ""
val isOnline: Boolean
get() = status == "1"
}

View File

@@ -0,0 +1,7 @@
package com.ckkj.defense_device.bean
data class JoinTheFamilyBean(
val parentId: String,
val inviteCode: String,
)

View File

@@ -0,0 +1,8 @@
package com.ckkj.defense_device.bean
data class RecommendationCodeBean(
val inviteCode: String? = null,
val code: String? = null,
val expireTime: Long? = null,
val expire: Long? = null
)

View File

@@ -0,0 +1,5 @@
package com.ckkj.defense_device.bean
data class SelectInviterInfoBean(
val code: String
)

View File

@@ -0,0 +1,8 @@
package com.ckkj.defense_device.bean
data class UpdateFamilyUserRequest(
val id: String,
val optionPassword: String,
val optionStatus: String,
val status: String,
)

View File

@@ -1,5 +1,6 @@
package com.ckkj.defense_device.home package com.ckkj.defense_device.home
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.ckkj.defense_device.R import com.ckkj.defense_device.R
@@ -7,6 +8,10 @@ import com.ckkj.defense_device.base.BaseActivity
import com.ckkj.defense_device.databinding.HomeActivityBinding import com.ckkj.defense_device.databinding.HomeActivityBinding
class HomeActivity : BaseActivity() { class HomeActivity : BaseActivity() {
companion object {
const val EXTRA_REFRESH = "extra_refresh"
}
// ViewBinding 核心对象 // ViewBinding 核心对象
private lateinit var binding: HomeActivityBinding private lateinit var binding: HomeActivityBinding
@@ -49,6 +54,28 @@ class HomeActivity : BaseActivity() {
binding.ivProfile.setImageResource(R.mipmap.profile_checked) binding.ivProfile.setImageResource(R.mipmap.profile_checked)
binding.tvProfile.setTextColor(resources.getColor(R.color.ic_selected)) binding.tvProfile.setTextColor(resources.getColor(R.color.ic_selected))
} }
handleRefreshIntent(intent)
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
if (intent != null) {
setIntent(intent)
handleRefreshIntent(intent)
}
}
private fun handleRefreshIntent(intent: Intent) {
if (intent.getBooleanExtra(EXTRA_REFRESH, false)) {
intent.removeExtra(EXTRA_REFRESH)
switchFragment(homeMenuFragment)
binding.ivHome.setImageResource(R.mipmap.home_checked)
binding.tvHome.setTextColor(resources.getColor(R.color.ic_selected))
binding.ivProfile.setImageResource(R.mipmap.profile)
binding.tvProfile.setTextColor(resources.getColor(R.color.ic_normal))
homeMenuFragment.refreshHomeData()
}
} }
/** /**

View File

@@ -23,13 +23,14 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.ckkj.defense_device.R import com.ckkj.defense_device.R
import com.ckkj.defense_device.adapter.DeviceAdapter import com.ckkj.defense_device.adapter.DeviceAdapter
import com.ckkj.defense_device.adapter.DeviceUi
import com.ckkj.defense_device.adapter.FamilyAdapter import com.ckkj.defense_device.adapter.FamilyAdapter
import com.ckkj.defense_device.bean.DeviceBean import com.ckkj.defense_device.bean.DeviceBean
import com.ckkj.defense_device.bean.FamilyBean import com.ckkj.defense_device.bean.FamilyBean
import com.ckkj.defense_device.bean.SwitchFamilyRequest import com.ckkj.defense_device.bean.SwitchFamilyRequest
import com.ckkj.defense_device.bean.SelectInviterInfoBean
import com.ckkj.defense_device.databinding.HomeMenuFragmentBinding import com.ckkj.defense_device.databinding.HomeMenuFragmentBinding
import com.ckkj.defense_device.liveVideo.LiveVideoActivity import com.ckkj.defense_device.liveVideo.LiveVideoActivity
import com.ckkj.defense_device.personal.JoinFamilyInviteActivity
import com.ckkj.defense_device.utils.DeviceIdEvent import com.ckkj.defense_device.utils.DeviceIdEvent
import com.ckkj.defense_device.utils.DeviceUpdateEvent import com.ckkj.defense_device.utils.DeviceUpdateEvent
import com.ckkj.defense_device.utils.FamilySwitchEvent import com.ckkj.defense_device.utils.FamilySwitchEvent
@@ -37,6 +38,7 @@ import com.ckkj.defense_device.utils.MessageStatusManager
import com.ckkj.defense_device.utils.RetrofitNetwork import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.utils.UserinfoUtil import com.ckkj.defense_device.utils.UserinfoUtil
import com.ckkj.defense_device.view.DefenseDialogs import com.ckkj.defense_device.view.DefenseDialogs
import com.ckkj.defense_device.view.LoadingDialog
import com.ckkj.defense_device.view.showDefenseDialog import com.ckkj.defense_device.view.showDefenseDialog
import com.gyf.immersionbar.ImmersionBar import com.gyf.immersionbar.ImmersionBar
import com.maning.mlkitscanner.scan.MNScanManager import com.maning.mlkitscanner.scan.MNScanManager
@@ -45,6 +47,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.json.JSONObject
//主页碎片 //主页碎片
class HomeMenuFragment : Fragment() { class HomeMenuFragment : Fragment() {
// ViewBinding // ViewBinding
@@ -122,23 +125,86 @@ class HomeMenuFragment : Fragment() {
MNScanManager.RESULT_SUCCESS -> { MNScanManager.RESULT_SUCCESS -> {
val result = data?.getStringArrayListExtra(MNScanManager.INTENT_KEY_RESULT_SUCCESS) val result = data?.getStringArrayListExtra(MNScanManager.INTENT_KEY_RESULT_SUCCESS)
if (!result.isNullOrEmpty()) { if (!result.isNullOrEmpty()) {
ToastUtils.showToast(requireContext(), "扫码成功: $result") val inviteCode = parseInviteCode(result[0])
if (inviteCode.isNullOrEmpty()) {
Toast.makeText(requireContext(), "无效的邀请码", Toast.LENGTH_SHORT).show()
} else {
fetchInviterInfo(inviteCode)
}
} else { } else {
ToastUtils.showToast(requireContext(), "扫码结果为空") Toast.makeText(requireContext(), "扫码结果为空", Toast.LENGTH_SHORT).show()
} }
} }
MNScanManager.RESULT_FAIL -> { MNScanManager.RESULT_FAIL -> {
val error = data?.getStringExtra(MNScanManager.INTENT_KEY_RESULT_ERROR) val error = data?.getStringExtra(MNScanManager.INTENT_KEY_RESULT_ERROR)
ToastUtils.showToast(requireContext(), "扫码失败: $error") Toast.makeText(requireContext(), "扫码失败: $error", Toast.LENGTH_SHORT).show()
} }
MNScanManager.RESULT_CANCLE -> { MNScanManager.RESULT_CANCLE -> {
ToastUtils.showToast(requireContext(), "扫码取消") Toast.makeText(requireContext(), "扫码取消", Toast.LENGTH_SHORT).show()
} }
} }
} }
}) })
} }
private fun parseInviteCode(scanResult: String): String? {
val trimmed = scanResult.trim()
if (trimmed.isEmpty()) return null
return try {
if (trimmed.startsWith("{")) {
JSONObject(trimmed).optString("code").takeIf { it.isNotEmpty() }
} else {
trimmed
}
} catch (e: Exception) {
trimmed
}
}
private fun fetchInviterInfo(inviteCode: String) {
val loadingDialog = LoadingDialog(requireActivity()).apply { show() }
CoroutineScope(Dispatchers.IO).launch {
try {
val response = RetrofitNetwork.getNetworkService()
.selectInviterInfo(SelectInviterInfoBean(code = inviteCode))
withContext(Dispatchers.Main) {
loadingDialog.dismiss()
if (response.code == 200 && response.data != null) {
val inviter = response.data
val parentId = inviter.resolveParentId()
if (parentId.isNullOrEmpty()) {
Toast.makeText(
requireContext(),
getString(R.string.network_error),
Toast.LENGTH_SHORT
).show()
return@withContext
}
val intent = Intent(requireContext(), JoinFamilyInviteActivity::class.java).apply {
putExtra(JoinFamilyInviteActivity.EXTRA_INVITE_CODE, inviteCode)
putExtra(JoinFamilyInviteActivity.EXTRA_NICK_NAME, inviter.nickName)
putExtra(JoinFamilyInviteActivity.EXTRA_AVATAR, inviter.avatar)
putExtra(JoinFamilyInviteActivity.EXTRA_PARENT_ID, parentId)
putExtra(JoinFamilyInviteActivity.EXTRA_USER_ID, inviter.userId)
}
startActivity(intent)
} else {
Toast.makeText(
requireContext(),
response.msg.ifBlank { getString(R.string.network_error) },
Toast.LENGTH_SHORT
).show()
}
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
loadingDialog.dismiss()
Toast.makeText(requireContext(), R.string.network_error, Toast.LENGTH_SHORT).show()
}
}
}
}
private val unreadCountChangeListener = object : MessageStatusManager.UnreadCountChangeListener { private val unreadCountChangeListener = object : MessageStatusManager.UnreadCountChangeListener {
override fun onUnreadCountChanged(count: Int) { override fun onUnreadCountChanged(count: Int) {
updateUnreadCountUI(count) updateUnreadCountUI(count)
@@ -200,8 +266,8 @@ class HomeMenuFragment : Fragment() {
// 为 LinearLayout 设置渐变背景 // 为 LinearLayout 设置渐变背景
setGradientBackground( setGradientBackground(
view = binding.gradientLayout, view = binding.gradientLayout,
startColor = Color.parseColor("#EBDCED"), startColor = resources.getColor(R.color.gradientLayout1),
endColor = Color.parseColor("#CAF4FF") endColor = resources.getColor(R.color.gradientLayout2)
) )
binding.message.setOnClickListener { binding.message.setOnClickListener {
val intent = Intent(requireContext(), MessageActivity::class.java) val intent = Intent(requireContext(), MessageActivity::class.java)
@@ -285,6 +351,10 @@ class HomeMenuFragment : Fragment() {
} }
} }
fun refreshHomeData() {
fetchFamilyList()
}
private fun fetchFamilyList() { private fun fetchFamilyList() {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
try { try {
@@ -321,18 +391,22 @@ class HomeMenuFragment : Fragment() {
Log.d("HomeMenuFragment", "canViewDevice = $canViewDevice") Log.d("HomeMenuFragment", "canViewDevice = $canViewDevice")
if (canViewDevice) { if (canViewDevice) {
binding.tvDeviceTitle.visibility = View.VISIBLE binding.tvDeviceTitle.visibility = View.VISIBLE
binding.swipeRefreshLayout.visibility = View.VISIBLE
binding.rvDeviceList.visibility = View.VISIBLE binding.rvDeviceList.visibility = View.VISIBLE
binding.message.visibility = View.VISIBLE binding.message.visibility = View.VISIBLE
binding.addFamily.visibility = View.VISIBLE binding.addFamily.visibility = View.VISIBLE
binding.add.visibility = View.VISIBLE binding.add.visibility = View.VISIBLE
binding.layoutNoPermission.visibility = View.GONE binding.layoutNoPermission.visibility = View.GONE
binding.layoutNoNetwork.visibility = View.GONE
} else { } else {
binding.tvDeviceTitle.visibility = View.GONE binding.tvDeviceTitle.visibility = View.GONE
binding.swipeRefreshLayout.visibility = View.GONE
binding.rvDeviceList.visibility = View.GONE binding.rvDeviceList.visibility = View.GONE
binding.message.visibility = View.GONE binding.message.visibility = View.GONE
binding.addFamily.visibility = View.GONE binding.addFamily.visibility = View.GONE
binding.add.visibility = View.GONE binding.add.visibility = View.GONE
binding.layoutNoPermission.visibility = View.VISIBLE binding.layoutNoPermission.visibility = View.VISIBLE
binding.layoutNoNetwork.visibility = View.GONE
} }
} }
@@ -390,9 +464,12 @@ class HomeMenuFragment : Fragment() {
hasMore = newDevices.size >= pageSize hasMore = newDevices.size >= pageSize
} }
Log.d("DeviceList", "deviceBeanList size = ${deviceBeanList.size}") Log.d("DeviceList", "deviceBeanList size = ${deviceBeanList.size}")
showDeviceContent()
updateDeviceList() updateDeviceList()
} else { } else {
Toast.makeText(requireContext(), response.msg, Toast.LENGTH_SHORT).show() noNetwork()
val errorMsg = response.msg.ifBlank { getString(R.string.network_error) }
Toast.makeText(requireContext(), errorMsg, Toast.LENGTH_SHORT).show()
if (isLoadMore) { if (isLoadMore) {
hasMore = false hasMore = false
} }
@@ -401,6 +478,7 @@ class HomeMenuFragment : Fragment() {
} }
} catch (e: Exception) { } catch (e: Exception) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
noNetwork()
Toast.makeText(requireContext(), R.string.network_error, Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), R.string.network_error, Toast.LENGTH_SHORT).show()
isLoading = false isLoading = false
if (isLoadMore) { if (isLoadMore) {
@@ -445,21 +523,31 @@ class HomeMenuFragment : Fragment() {
} }
} }
private fun updateDeviceList() { private fun showDeviceContent() {
val deviceUiList = deviceBeanList.map { binding.swipeRefreshLayout.visibility = View.VISIBLE
DeviceUi(it.deviceName ?: "", it.status ?: "", it.id ?: "") binding.rvDeviceList.visibility = View.VISIBLE
} binding.layoutNoPermission.visibility = View.GONE
binding.layoutNoNetwork.visibility = View.GONE
}
if (deviceUiList.isEmpty()) { private fun noNetwork() {
binding.swipeRefreshLayout.visibility = View.GONE
binding.rvDeviceList.visibility = View.GONE
binding.layoutNoPermission.visibility = View.GONE
binding.layoutNoNetwork.visibility = View.VISIBLE
}
private fun updateDeviceList() {
if (deviceBeanList.isEmpty()) {
deviceAdapter.submitList(listOf( deviceAdapter.submitList(listOf(
DeviceUi(getString(R.string.no_device), "0") DeviceBean(deviceName = getString(R.string.no_device), status = "0")
)) ))
} else { } else {
deviceAdapter.submitList(deviceUiList) deviceAdapter.submitList(deviceBeanList)
} }
} }
private fun onDeviceClicked(device: DeviceUi) { private fun onDeviceClicked(device: DeviceBean) {
if (device.isOnline) { if (device.isOnline) {
DeviceIdEvent.setDeviceId(device.deviceId) DeviceIdEvent.setDeviceId(device.deviceId)
val intent = Intent(requireContext(), LiveVideoActivity::class.java) val intent = Intent(requireContext(), LiveVideoActivity::class.java)

View File

@@ -125,7 +125,7 @@ class MessageActivity : AppCompatActivity() {
e.printStackTrace() e.printStackTrace()
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
if (allMessages.isEmpty()) { if (allMessages.isEmpty()) {
showEmptyState() noNetwork()
} }
} }
} finally { } finally {
@@ -152,6 +152,12 @@ class MessageActivity : AppCompatActivity() {
binding.emptyLayout.visibility = View.GONE binding.emptyLayout.visibility = View.GONE
} }
private fun noNetwork() {
binding.rvMessageList.visibility = View.GONE
binding.layoutNoNetwork.visibility = View.VISIBLE
binding.tvNoMoreData.visibility = View.GONE
}
private fun showNoMoreDataHint() { private fun showNoMoreDataHint() {
binding.tvNoMoreData.visibility = View.VISIBLE binding.tvNoMoreData.visibility = View.VISIBLE
} }

View File

@@ -21,6 +21,7 @@ import com.ckkj.defense_device.utils.LanguageUtil
import com.ckkj.defense_device.utils.RetrofitNetwork import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.utils.UserinfoUtil import com.ckkj.defense_device.utils.UserinfoUtil
import com.ckkj.defense_device.utils.ActivityManager import com.ckkj.defense_device.utils.ActivityManager
import com.ckkj.defense_device.view.CustomDialogFragmentText
import com.ckkj.defense_device.view.FamilySwitchDialog import com.ckkj.defense_device.view.FamilySwitchDialog
import com.ckkj.defense_device.view.LanguageDialog import com.ckkj.defense_device.view.LanguageDialog
import com.gyf.immersionbar.ImmersionBar import com.gyf.immersionbar.ImmersionBar
@@ -100,27 +101,36 @@ class PersonalFragment : Fragment() {
} }
// 退出登录点击事件 // 退出登录点击事件
binding.btnLogout.setOnClickListener { binding.btnLogout.setOnClickListener {
CoroutineScope(Dispatchers.IO).launch { val dialog = CustomDialogFragmentText()
try { .setTitle(getString(R.string.tip))
val response = RetrofitNetwork.getNetworkService().logout() .setMessage(getString(R.string.logout_confirm))
withContext(Dispatchers.Main) { .setShowInput(false)
if (response.code == 200) { .setNegativeText(getString(R.string.cancel))
UserinfoUtil.clearUserInfo() .setPositiveText(getString(R.string.confirm))
val intent = Intent(requireContext(), com.ckkj.defense_device.login.LoginActivity::class.java) .setOnConfirmListener {
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK CoroutineScope(Dispatchers.IO).launch {
startActivity(intent) try {
requireActivity().finish() val response = RetrofitNetwork.getNetworkService().logout()
} else { withContext(Dispatchers.Main) {
Toast.makeText(requireContext(), getString(R.string.logout_fail), Toast.LENGTH_SHORT).show() if (response.code == 200) {
UserinfoUtil.clearUserInfo()
val intent = Intent(requireContext(), com.ckkj.defense_device.login.LoginActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
requireActivity().finish()
} else {
Toast.makeText(requireContext(), getString(R.string.logout_fail), Toast.LENGTH_SHORT).show()
}
}
} catch (e: Exception) {
e.printStackTrace()
withContext(Dispatchers.Main) {
Toast.makeText(requireContext(), getString(R.string.logout_fail), Toast.LENGTH_SHORT).show()
}
} }
} }
} catch (e: Exception) {
e.printStackTrace()
withContext(Dispatchers.Main) {
Toast.makeText(requireContext(), getString(R.string.logout_fail), Toast.LENGTH_SHORT).show()
}
} }
} dialog.show(childFragmentManager, "logout_dialog")
} }
fetchCurrentFamily() fetchCurrentFamily()
return binding.root return binding.root

View File

@@ -3,6 +3,7 @@ package com.ckkj.defense_device.liveVideo
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@@ -95,19 +96,33 @@ class AddLinkedUserActivity : AppCompatActivity() {
Toast.makeText(this@AddLinkedUserActivity, "暂无家庭成员数据", Toast.LENGTH_SHORT).show() Toast.makeText(this@AddLinkedUserActivity, "暂无家庭成员数据", Toast.LENGTH_SHORT).show()
} }
} else { } else {
hide()
Toast.makeText(this@AddLinkedUserActivity, "请求失败: ${response.msg}", Toast.LENGTH_SHORT).show() Toast.makeText(this@AddLinkedUserActivity, "请求失败: ${response.msg}", Toast.LENGTH_SHORT).show()
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
android.util.Log.e("AddLinkedUserActivity", "网络请求异常", e)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
binding.swipeRefresh.isRefreshing = false binding.swipeRefresh.isRefreshing = false
noNetwork()
Toast.makeText(this@AddLinkedUserActivity, "网络请求失败: ${e.message}", Toast.LENGTH_SHORT).show() Toast.makeText(this@AddLinkedUserActivity, "网络请求失败: ${e.message}", Toast.LENGTH_SHORT).show()
} }
} }
} }
} }
private fun hide() {
binding.searchLayout.visibility = View.GONE
binding.rvAddLinkedUser.visibility = View.GONE
binding.butting?.visibility = View.GONE
binding.layoutNoPermission?.visibility = View.VISIBLE
}
private fun noNetwork() {
binding.searchLayout.visibility = View.GONE
binding.rvAddLinkedUser.visibility = View.GONE
binding.butting?.visibility = View.GONE
binding.layoutNoNetwork?.visibility = View.VISIBLE
}
private fun updateFamilyMemberList(familyMembersList: List<FamilyMembersBean>) { private fun updateFamilyMemberList(familyMembersList: List<FamilyMembersBean>) {
fullUserItemList.clear() fullUserItemList.clear()
familyMembersList.forEach { member -> familyMembersList.forEach { member ->

View File

@@ -257,6 +257,7 @@ class LinkedUserActivity : AppCompatActivity() {
updateFamilyMembersList(response.data as List<FamilyMembersBean>) updateFamilyMembersList(response.data as List<FamilyMembersBean>)
} }
} else { } else {
hide()
Toast.makeText(this@LinkedUserActivity, response.msg, Toast.LENGTH_SHORT).show() Toast.makeText(this@LinkedUserActivity, response.msg, Toast.LENGTH_SHORT).show()
} }
} }
@@ -264,11 +265,24 @@ class LinkedUserActivity : AppCompatActivity() {
e.printStackTrace() e.printStackTrace()
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
binding.swipeRefresh.isRefreshing = false binding.swipeRefresh.isRefreshing = false
Toast.makeText(this@LinkedUserActivity, "网络请求失败", Toast.LENGTH_SHORT).show() noNetwork()
Toast.makeText(this@LinkedUserActivity, R.string.network_request_failed, Toast.LENGTH_SHORT).show()
} }
} }
} }
} }
private fun hide() {
binding.searchLayout.visibility = View.GONE
binding.rvLinkedUser.visibility = View.GONE
binding.butting?.visibility = View.GONE
binding.layoutNoPermission?.visibility = View.VISIBLE
}
private fun noNetwork() {
binding.searchLayout.visibility = View.GONE
binding.rvLinkedUser.visibility = View.GONE
binding.butting?.visibility = View.GONE
binding.layoutNoNetwork?.visibility = View.VISIBLE
}
private fun updateDeviceUserList(deviceUserList: List<DeviceUserBean>) { private fun updateDeviceUserList(deviceUserList: List<DeviceUserBean>) {
fullUserList.clear() fullUserList.clear()

View File

@@ -80,7 +80,7 @@ class LiveVideoActivity : BaseActivity() {
} }
private fun initVideoPlayer() { private fun initVideoPlayer() {
val videoUrl = "https://www.w3schools.com/html/movie.mp4"//"https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" val videoUrl ="http://220.203.23.195:9090/live/test001.live.flv"
orientationUtils = OrientationUtils(this, binding.exoPlayContextId) orientationUtils = OrientationUtils(this, binding.exoPlayContextId)
orientationUtils?.isEnable = true orientationUtils?.isEnable = true

View File

@@ -7,6 +7,7 @@ import androidx.lifecycle.lifecycleScope
import com.ckkj.defense_device.R import com.ckkj.defense_device.R
import com.ckkj.defense_device.bean.DeleteDeviceUserRequest import com.ckkj.defense_device.bean.DeleteDeviceUserRequest
import com.ckkj.defense_device.bean.ObtainInviterInformationRequest import com.ckkj.defense_device.bean.ObtainInviterInformationRequest
import com.ckkj.defense_device.bean.UpdateFamilyUserRequest
import com.ckkj.defense_device.bean.VerifyOptionPasswordRequest import com.ckkj.defense_device.bean.VerifyOptionPasswordRequest
import com.ckkj.defense_device.personal.PasswordInputDialog import com.ckkj.defense_device.personal.PasswordInputDialog
import com.ckkj.defense_device.utils.DeviceIdEvent import com.ckkj.defense_device.utils.DeviceIdEvent
@@ -26,6 +27,7 @@ class UserDetailsActivity : AppCompatActivity() {
private var userId: String = "" private var userId: String = ""
private var isPasswordVerified: Boolean = false private var isPasswordVerified: Boolean = false
private lateinit var passwordDialog: PasswordInputDialog private lateinit var passwordDialog: PasswordInputDialog
private var optionPassword: String = ""
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -87,10 +89,12 @@ class UserDetailsActivity : AppCompatActivity() {
binding.swUserStatus.setOnCheckedChangeListener { _, isChecked -> binding.swUserStatus.setOnCheckedChangeListener { _, isChecked ->
if (!isPasswordVerified) { if (!isPasswordVerified) {
binding.swUserStatus.isChecked = !isChecked binding.swUserStatus.isChecked = !isChecked
showPasswordDialog { showPasswordDialog(isChecked) {
isPasswordVerified = true isPasswordVerified = true
binding.swUserStatus.isChecked = isChecked binding.swUserStatus.isChecked = isChecked
} }
} else {
updateFamilyUserStatus(isChecked)
} }
} }
@@ -99,7 +103,7 @@ class UserDetailsActivity : AppCompatActivity() {
} }
} }
private fun showPasswordDialog(onVerified: () -> Unit) { private fun showPasswordDialog(status: Boolean, onVerified: () -> Unit) {
passwordDialog = PasswordInputDialog(this).apply { passwordDialog = PasswordInputDialog(this).apply {
setOnConfirmListener { inputPassword -> setOnConfirmListener { inputPassword ->
lifecycleScope.launch { lifecycleScope.launch {
@@ -109,6 +113,7 @@ class UserDetailsActivity : AppCompatActivity() {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
if (response.code == 200) { if (response.code == 200) {
dismiss() dismiss()
updateFamilyUserStatus(status, inputPassword)
onVerified() onVerified()
} else { } else {
Toast.makeText( Toast.makeText(
@@ -135,6 +140,48 @@ class UserDetailsActivity : AppCompatActivity() {
} }
private fun updateFamilyUserStatus(status: Boolean) {
updateFamilyUserStatus(status, optionPassword)
}
private fun updateFamilyUserStatus(status: Boolean, password: String) {
if (userId.isEmpty()) {
Toast.makeText(this, "用户ID为空", Toast.LENGTH_SHORT).show()
return
}
if (password.isNotEmpty()) {
optionPassword = password
}
lifecycleScope.launch {
try {
val request = UpdateFamilyUserRequest(
id = userId,
optionPassword = optionPassword,
optionStatus = "status",
status = if (status) "1" else "0"
)
val response = RetrofitNetwork.getNetworkService().updateFamilyUser(request)
withContext(Dispatchers.Main) {
if (response.code == 200) {
Toast.makeText(this@UserDetailsActivity, "状态修改成功", Toast.LENGTH_SHORT).show()
DeviceUserUpdateEvent.notifyUpdate()
} else {
Toast.makeText(this@UserDetailsActivity, response.msg ?: "状态修改失败", Toast.LENGTH_SHORT).show()
binding.swUserStatus.isChecked = !status
}
}
} catch (e: Exception) {
e.printStackTrace()
withContext(Dispatchers.Main) {
Toast.makeText(this@UserDetailsActivity, "网络请求失败", Toast.LENGTH_SHORT).show()
binding.swUserStatus.isChecked = !status
}
}
}
}
private fun showDeleteDialog() { private fun showDeleteDialog() {
val dialog = CustomDialogFragmentText() val dialog = CustomDialogFragmentText()
dialog.setTitle(getString(R.string.tip)) dialog.setTitle(getString(R.string.tip))
@@ -146,7 +193,7 @@ class UserDetailsActivity : AppCompatActivity() {
if (isPasswordVerified) { if (isPasswordVerified) {
deleteDeviceUser() deleteDeviceUser()
} else { } else {
showPasswordDialog { showPasswordDialog(false) {
isPasswordVerified = true isPasswordVerified = true
deleteDeviceUser() deleteDeviceUser()
} }

View File

@@ -79,29 +79,31 @@ class VoiceSettingsActivity : AppCompatActivity() {
private fun updateDeviceAlertAudio() { private fun updateDeviceAlertAudio() {
deviceId?.let { id -> deviceId?.let { id ->
val newAlertAudio = if (binding.swWarningVoice.isChecked) "1" else "0" val newAlertAudio = if (binding.swWarningVoice.isChecked) "1" else "0"
// 只有状态发生变化时才请求 // 点击返回时总是触发网络请求
if (newAlertAudio != originalAlertAudio) { CoroutineScope(Dispatchers.IO).launch {
CoroutineScope(Dispatchers.IO).launch { try {
try { val deviceBean = DeviceBean(id = id, alertAudio = newAlertAudio)
val deviceBean = DeviceBean(id = id, alertAudio = newAlertAudio) val response = apiService.updateDeviceInfo(deviceBean)
val response = apiService.updateDeviceInfo(deviceBean) withContext(Dispatchers.Main) {
withContext(Dispatchers.Main) { if (response.code == 200 && response.data != null) {
if (response.code == 200 && response.data != null) { Toast.makeText(this@VoiceSettingsActivity, "修改成功", Toast.LENGTH_SHORT).show()
Toast.makeText(this@VoiceSettingsActivity, "修改成功", Toast.LENGTH_SHORT).show() } else {
} else { // 仅在状态实际发生变化时才显示错误提示
if (newAlertAudio != originalAlertAudio) {
Toast.makeText(this@VoiceSettingsActivity, response.msg, Toast.LENGTH_SHORT).show() Toast.makeText(this@VoiceSettingsActivity, response.msg, Toast.LENGTH_SHORT).show()
} }
finish()
} }
} catch (e: Exception) { finish()
withContext(Dispatchers.Main) { }
} catch (e: Exception) {
withContext(Dispatchers.Main) {
// 仅在状态实际发生变化时才显示失败提示
if (newAlertAudio != originalAlertAudio) {
Toast.makeText(this@VoiceSettingsActivity, "修改失败", Toast.LENGTH_SHORT).show() Toast.makeText(this@VoiceSettingsActivity, "修改失败", Toast.LENGTH_SHORT).show()
finish()
} }
finish()
} }
} }
} else {
finish()
} }
} }
} }

View File

@@ -10,6 +10,7 @@ import bean.ForgotRequest
import com.ckkj.defense_device.R import com.ckkj.defense_device.R
import com.ckkj.defense_device.databinding.ForgetPasswordActivityBinding import com.ckkj.defense_device.databinding.ForgetPasswordActivityBinding
import com.ckkj.defense_device.utils.RetrofitNetwork import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.view.LoadingDialog
import com.google.firebase.crashlytics.buildtools.reloc.org.apache.http.util.TextUtils import com.google.firebase.crashlytics.buildtools.reloc.org.apache.http.util.TextUtils
import com.gyf.immersionbar.ImmersionBar import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@@ -20,6 +21,7 @@ import java.util.regex.Pattern
class ForgetPasswordActivity : AppCompatActivity() { class ForgetPasswordActivity : AppCompatActivity() {
private lateinit var binding: ForgetPasswordActivityBinding private lateinit var binding: ForgetPasswordActivityBinding
private var isLoading = false
private var countDownTimer: CountDownTimer? = null private var countDownTimer: CountDownTimer? = null
private var isSendingCode = false private var isSendingCode = false
@@ -63,7 +65,9 @@ class ForgetPasswordActivity : AppCompatActivity() {
} }
binding.btnNext.setOnClickListener { binding.btnNext.setOnClickListener {
performResetPassword() if (!isLoading) {
performResetPassword()
}
} }
} }
@@ -163,8 +167,11 @@ class ForgetPasswordActivity : AppCompatActivity() {
return return
} }
isLoading = true
binding.btnNext.isEnabled = false binding.btnNext.isEnabled = false
binding.btnNext.text = "重置中..." val loadingDialog = LoadingDialog(this)
.setMessage(getString(R.string.resetting))
.apply { show() }
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
try { try {
@@ -175,8 +182,9 @@ class ForgetPasswordActivity : AppCompatActivity() {
) )
val response = RetrofitNetwork.getNetworkService().forgotPassword(forgotRequest) val response = RetrofitNetwork.getNetworkService().forgotPassword(forgotRequest)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
isLoading = false
loadingDialog.dismiss()
binding.btnNext.isEnabled = true binding.btnNext.isEnabled = true
binding.btnNext.text = "确定"
if (response.code == 200) { if (response.code == 200) {
Toast.makeText(this@ForgetPasswordActivity, R.string.reset_success, Toast.LENGTH_SHORT).show() Toast.makeText(this@ForgetPasswordActivity, R.string.reset_success, Toast.LENGTH_SHORT).show()
@@ -187,8 +195,9 @@ class ForgetPasswordActivity : AppCompatActivity() {
} }
} catch (e: Exception) { } catch (e: Exception) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
isLoading = false
loadingDialog.dismiss()
binding.btnNext.isEnabled = true binding.btnNext.isEnabled = true
binding.btnNext.text = "确定"
Toast.makeText(this@ForgetPasswordActivity, R.string.network_error, Toast.LENGTH_SHORT).show() Toast.makeText(this@ForgetPasswordActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
} }
} }

View File

@@ -5,6 +5,7 @@ import android.graphics.Color
import android.graphics.drawable.GradientDrawable import android.graphics.drawable.GradientDrawable
import android.os.Bundle import android.os.Bundle
import android.os.CountDownTimer import android.os.CountDownTimer
import android.util.Log
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@@ -194,6 +195,7 @@ class LoginActivity : AppCompatActivity() {
try { try {
val response = RetrofitNetwork.getNetworkService().getCode(phone) val response = RetrofitNetwork.getNetworkService().getCode(phone)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
Log.d("VerificationCode", "code = ${response.code}, msg = ${response.msg}")
if (response.code == 200) { if (response.code == 200) {
Toast.makeText(this@LoginActivity, getString(R.string.code_sent_to, phone), Toast.LENGTH_SHORT).show() Toast.makeText(this@LoginActivity, getString(R.string.code_sent_to, phone), Toast.LENGTH_SHORT).show()
startCountDown() startCountDown()

View File

@@ -11,6 +11,7 @@ import bean.RegisterRequest
import com.ckkj.defense_device.R import com.ckkj.defense_device.R
import com.ckkj.defense_device.databinding.RegisterActivityBinding import com.ckkj.defense_device.databinding.RegisterActivityBinding
import com.ckkj.defense_device.utils.RetrofitNetwork import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.view.LoadingDialog
import com.gyf.immersionbar.ImmersionBar import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -158,7 +159,9 @@ class RegisterActivity : AppCompatActivity() {
isLoading = true isLoading = true
binding.btnNext.isEnabled = false binding.btnNext.isEnabled = false
binding.btnNext.text = getString(R.string.registering) val loadingDialog = LoadingDialog(this)
.setMessage(getString(R.string.registering))
.apply { show() }
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
try { try {
@@ -174,8 +177,8 @@ class RegisterActivity : AppCompatActivity() {
val response = RetrofitNetwork.getNetworkService().register(registerRequest) val response = RetrofitNetwork.getNetworkService().register(registerRequest)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
isLoading = false isLoading = false
loadingDialog.dismiss()
binding.btnNext.isEnabled = true binding.btnNext.isEnabled = true
binding.btnNext.text = getString(R.string.next_step)
if (response.code == 200) { if (response.code == 200) {
Toast.makeText(this@RegisterActivity, R.string.register_success, Toast.LENGTH_SHORT).show() Toast.makeText(this@RegisterActivity, R.string.register_success, Toast.LENGTH_SHORT).show()
@@ -190,8 +193,8 @@ class RegisterActivity : AppCompatActivity() {
} catch (e: Exception) { } catch (e: Exception) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
isLoading = false isLoading = false
loadingDialog.dismiss()
binding.btnNext.isEnabled = true binding.btnNext.isEnabled = true
binding.btnNext.text = getString(R.string.next_step)
Toast.makeText(this@RegisterActivity, R.string.network_error, Toast.LENGTH_SHORT).show() Toast.makeText(this@RegisterActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
} }
} }

View File

@@ -1,32 +1,45 @@
package com.ckkj.defense_device.personal package com.ckkj.defense_device.personal
import android.Manifest
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Color import android.graphics.Color
import android.graphics.Paint import android.graphics.Paint
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Environment import android.os.Environment
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.ckkj.defense_device.R import com.ckkj.defense_device.R
import com.ckkj.defense_device.bean.RecommendationCodeBean
import com.ckkj.defense_device.bean.SelectInviterInfoBean
import com.ckkj.defense_device.bean.VerifyOptionPasswordRequest import com.ckkj.defense_device.bean.VerifyOptionPasswordRequest
import com.ckkj.defense_device.databinding.DialogExpireTimeBinding import com.ckkj.defense_device.databinding.DialogExpireTimeBinding
import com.ckkj.defense_device.databinding.InvitationCodeActivityBinding import com.ckkj.defense_device.databinding.InvitationCodeActivityBinding
import com.ckkj.defense_device.utils.RetrofitNetwork import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.view.LoadingDialog
import com.google.gson.Gson
import com.google.zxing.BarcodeFormat import com.google.zxing.BarcodeFormat
import com.google.zxing.EncodeHintType import com.google.zxing.EncodeHintType
import com.google.zxing.qrcode.QRCodeWriter import com.google.zxing.qrcode.QRCodeWriter
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
import com.gyf.immersionbar.ImmersionBar import com.gyf.immersionbar.ImmersionBar
import com.maning.mlkitscanner.scan.MNScanManager
import com.maning.mlkitscanner.scan.callback.act.MNScanCallback
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.json.JSONObject
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.util.Hashtable import java.util.Hashtable
@@ -44,7 +57,24 @@ class InvitationCodeActivity : AppCompatActivity() {
private var isLoading = false // 是否正在加载 private var isLoading = false // 是否正在加载
private var hasGeneratedQrCode = false // 是否已经生成过二维码 private var hasGeneratedQrCode = false // 是否已经生成过二维码
private val requestPermissionsLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
val cameraGranted = permissions[Manifest.permission.CAMERA] ?: false
val storageGranted = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
permissions[Manifest.permission.READ_MEDIA_IMAGES] ?: false
} else {
permissions[Manifest.permission.READ_EXTERNAL_STORAGE] ?: false
}
if (cameraGranted && storageGranted) {
startScan()
} else {
Toast.makeText(this, R.string.permission_denied, Toast.LENGTH_SHORT).show()
}
}
companion object { companion object {
private const val TAG = "InvitationCodeActivity"
const val EXPIRE_24H = 24 * 60 * 60 * 1000L const val EXPIRE_24H = 24 * 60 * 60 * 1000L
const val EXPIRE_7D = 7 * 24 * 60 * 60 * 1000L const val EXPIRE_7D = 7 * 24 * 60 * 60 * 1000L
} }
@@ -90,15 +120,136 @@ class InvitationCodeActivity : AppCompatActivity() {
// 撤销邀请按钮 // 撤销邀请按钮
binding.btnCancelInvite.setOnClickListener { binding.btnCancelInvite.setOnClickListener {
showPasswordDialog { if (generatedInviteCode.isEmpty()) {
cancelInvitation() ToastUtils.showError(this, getString(R.string.qr_code_expired))
return@setOnClickListener
}
showPasswordDialog { password, dialog ->
cancelInvitation(password, dialog)
} }
} }
// 生成/重新生成按钮 // 生成/重新生成按钮
binding.btnRegenerate.setOnClickListener { binding.btnRegenerate.setOnClickListener {
showPasswordDialog { showPasswordDialog { password, dialog ->
generateOrRegenerateInvitation() generateOrRegenerateInvitation(password, dialog)
}
}
binding.sys.setOnClickListener {
checkPermissionsAndScan()
}
}
private fun checkPermissionsAndScan() {
val cameraPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
val storagePermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES)
} else {
ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
if (cameraPermission == PackageManager.PERMISSION_GRANTED &&
storagePermission == PackageManager.PERMISSION_GRANTED
) {
startScan()
} else {
val permissionsToRequest = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.READ_MEDIA_IMAGES
)
} else {
arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
}
requestPermissionsLauncher.launch(permissionsToRequest)
}
}
private fun startScan() {
MNScanManager.startScan(this, object : MNScanCallback {
override fun onActivityResult(resultCode: Int, data: Intent?) {
when (resultCode) {
MNScanManager.RESULT_SUCCESS -> {
val result = data?.getStringArrayListExtra(MNScanManager.INTENT_KEY_RESULT_SUCCESS)
if (!result.isNullOrEmpty()) {
val inviteCode = parseInviteCode(result[0])
if (inviteCode.isNullOrEmpty()) {
Toast.makeText(this@InvitationCodeActivity, "无效的邀请码", Toast.LENGTH_SHORT).show()
} else {
fetchInviterInfo(inviteCode)
}
} else {
Toast.makeText(this@InvitationCodeActivity, "扫码结果为空", Toast.LENGTH_SHORT).show()
}
}
MNScanManager.RESULT_FAIL -> {
val error = data?.getStringExtra(MNScanManager.INTENT_KEY_RESULT_ERROR)
Toast.makeText(this@InvitationCodeActivity, "扫码失败: $error", Toast.LENGTH_SHORT).show()
}
MNScanManager.RESULT_CANCLE -> {
Toast.makeText(this@InvitationCodeActivity, "扫码取消", Toast.LENGTH_SHORT).show()
}
}
}
})
}
private fun parseInviteCode(scanResult: String): String? {
val trimmed = scanResult.trim()
if (trimmed.isEmpty()) return null
return try {
if (trimmed.startsWith("{")) {
JSONObject(trimmed).optString("code").takeIf { it.isNotEmpty() }
} else {
trimmed
}
} catch (e: Exception) {
trimmed
}
}
private fun fetchInviterInfo(inviteCode: String) {
val loadingDialog = LoadingDialog(this).apply { show() }
CoroutineScope(Dispatchers.IO).launch {
try {
val response = RetrofitNetwork.getNetworkService()
.selectInviterInfo(SelectInviterInfoBean(code = inviteCode))
withContext(Dispatchers.Main) {
loadingDialog.dismiss()
if (response.code == 200 && response.data != null) {
val inviter = response.data
val parentId = inviter.resolveParentId()
if (parentId.isNullOrEmpty()) {
Toast.makeText(
this@InvitationCodeActivity,
getString(R.string.network_error),
Toast.LENGTH_SHORT
).show()
return@withContext
}
val intent = Intent(this@InvitationCodeActivity, JoinFamilyInviteActivity::class.java).apply {
putExtra(JoinFamilyInviteActivity.EXTRA_INVITE_CODE, inviteCode)
putExtra(JoinFamilyInviteActivity.EXTRA_NICK_NAME, inviter.nickName)
putExtra(JoinFamilyInviteActivity.EXTRA_AVATAR, inviter.avatar)
putExtra(JoinFamilyInviteActivity.EXTRA_PARENT_ID, parentId)
putExtra(JoinFamilyInviteActivity.EXTRA_USER_ID, inviter.userId)
}
startActivity(intent)
} else {
Toast.makeText(
this@InvitationCodeActivity,
response.msg.ifBlank { getString(R.string.network_error) },
Toast.LENGTH_SHORT
).show()
}
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
loadingDialog.dismiss()
Toast.makeText(this@InvitationCodeActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
}
} }
} }
} }
@@ -106,14 +257,56 @@ class InvitationCodeActivity : AppCompatActivity() {
/** /**
* 生成或重新生成邀请码 * 生成或重新生成邀请码
*/ */
private fun generateOrRegenerateInvitation() { private fun generateOrRegenerateInvitation(password: String, passwordDialog: PasswordInputDialog) {
// 根据当前选择的时效生成邀请码 if (isLoading) return
generateQRCode() isLoading = true
showQrCode() val loadingDialog = LoadingDialog(this).apply { show() }
// 更新按钮文字为"重新生成"
binding.btnRegenerate.text = getString(R.string.regenerate) CoroutineScope(Dispatchers.IO).launch {
hasGeneratedQrCode = true try {
ToastUtils.showSuccess(this, getString(R.string.operation_success)) val response = RetrofitNetwork.getNetworkService()
.generateRecommendationCode(VerifyOptionPasswordRequest(password))
Log.d(TAG, "生成推荐码响应: code=${response.code}, msg=${response.msg}, data=${response.data}")
withContext(Dispatchers.Main) {
isLoading = false
loadingDialog.dismiss()
if (response.code == 200) {
val parsed = parseRecommendationCode(response.data)
if (parsed != null) {
passwordDialog.dismiss()
generatedInviteCode = parsed.first
expireTimestamp = parsed.second
qrCodeContent =
"{\"type\":\"invite\",\"code\":\"$generatedInviteCode\",\"expire\":$expireTimestamp}"
Log.d(
TAG,
"推荐码生成成功: Code=$generatedInviteCode, expireTimestamp=$expireTimestamp, qrContent=$qrCodeContent"
)
generateQRCode()
showQrCode()
binding.btnRegenerate.text = getString(R.string.regenerate)
hasGeneratedQrCode = true
ToastUtils.showSuccess(this@InvitationCodeActivity, getString(R.string.operation_success))
} else {
Log.w(TAG, "推荐码解析失败, raw data=${response.data}")
passwordDialog.showError(getString(R.string.network_error))
}
} else {
Log.w(TAG, "生成推荐码失败: code=${response.code}, msg=${response.msg}")
passwordDialog.showError(
response.msg.ifEmpty { getString(R.string.password_error) }
)
}
}
} catch (e: Exception) {
Log.e(TAG, "生成推荐码异常", e)
withContext(Dispatchers.Main) {
isLoading = false
loadingDialog.dismiss()
passwordDialog.showError(getString(R.string.network_error))
}
}
}
} }
private fun showExpireTimeDialog() { private fun showExpireTimeDialog() {
@@ -151,9 +344,6 @@ class InvitationCodeActivity : AppCompatActivity() {
binding.tvExpireTime.text = getString(R.string.expire_7d) binding.tvExpireTime.text = getString(R.string.expire_7d)
} }
} }
// 显示二维码并生成
showQrCode()
generateQRCode()
expireTimeDialog?.dismiss() expireTimeDialog?.dismiss()
} }
@@ -173,22 +363,28 @@ class InvitationCodeActivity : AppCompatActivity() {
} }
private fun generateQRCode() { private fun generateQRCode() {
// 检查二维码是否过期,如果过期则生成新的邀请码 if (qrCodeContent.isEmpty()) return
val isExpired = expireTimestamp > 0 && System.currentTimeMillis() > expireTimestamp val qrBitmap = createQRCodeWithLogo(qrCodeContent, 400, 400)
val content = if (qrCodeContent.isNotEmpty() && !isExpired) qrCodeContent else generateInviteCode()
val qrBitmap = createQRCodeWithLogo(content, 400, 400)
binding.ivQrCode.setImageBitmap(qrBitmap) binding.ivQrCode.setImageBitmap(qrBitmap)
} }
private fun generateInviteCode(): String { private fun parseRecommendationCode(data: Any?): Pair<String, Long>? {
val timestamp = System.currentTimeMillis() if (data == null) return null
expireTimestamp = timestamp + expireTimeInMillis return when (data) {
generatedInviteCode = "INV${timestamp}" is String -> {
qrCodeContent = "{\"type\":\"invite\",\"code\":\"$generatedInviteCode\",\"expire\":$expireTimestamp}" if (data.isEmpty()) null
// 输出日志,方便调试验证 else Pair(data, System.currentTimeMillis() + expireTimeInMillis)
android.util.Log.d("InvitationCode", "生成的二维码内容: $qrCodeContent") }
android.util.Log.d("InvitationCode", "推荐码: $generatedInviteCode, 过期时间戳: $expireTimestamp") else -> {
return qrCodeContent val bean = Gson().fromJson(Gson().toJson(data), RecommendationCodeBean::class.java)
val code = bean.inviteCode?.takeIf { it.isNotEmpty() }
?: bean.code?.takeIf { it.isNotEmpty() }
?: return null
val expire = bean.expireTime ?: bean.expire
?: (System.currentTimeMillis() + expireTimeInMillis)
Pair(code, expire)
}
}
} }
/* /*
* 这段代码生成带Logo的二维码 * 这段代码生成带Logo的二维码
@@ -415,56 +611,62 @@ class InvitationCodeActivity : AppCompatActivity() {
/** /**
* 撤销邀请 * 撤销邀请
*/ */
private fun cancelInvitation() { private fun cancelInvitation(password: String, passwordDialog: PasswordInputDialog) {
// 撤销邀请逻辑 if (isLoading) return
hideQrCode() if (generatedInviteCode.isEmpty()) {
generatedInviteCode = "" passwordDialog.showError(getString(R.string.qr_code_expired))
qrCodeContent = "" return
expireTimestamp = 0
hasGeneratedQrCode = false
// 恢复按钮文字为"生成邀请码"
binding.btnRegenerate.text = getString(R.string.generate_invite_code)
// 显示提示
ToastUtils.showSuccess(this, getString(R.string.operation_success))
}
/**
* 显示密码输入对话框
* @param onSuccess 验证成功后的回调
*/
private fun showPasswordDialog(onSuccess: () -> Unit) {
val dialog = PasswordInputDialog(this)
dialog.setTitle(getString(R.string.input_op_pwd))
dialog.setOnConfirmListener { password ->
verifyPassword(password, dialog, onSuccess)
} }
dialog.show() isLoading = true
} val loadingDialog = LoadingDialog(this).apply { show() }
/**
* 验证操作密码
*/
private fun verifyPassword(password: String, dialog: PasswordInputDialog, onSuccess: () -> Unit) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
try { try {
val response = RetrofitNetwork.getNetworkService() val response = RetrofitNetwork.getNetworkService()
.verifyOptionPassword(VerifyOptionPasswordRequest(password)) .revokeInvitation(VerifyOptionPasswordRequest(password))
Log.d(TAG, "撤销邀请响应: code=${response.code}, msg=${response.msg}")
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
isLoading = false
loadingDialog.dismiss()
if (response.code == 200) { if (response.code == 200) {
dialog.dismiss() passwordDialog.dismiss()
onSuccess.invoke() hideQrCode()
generatedInviteCode = ""
qrCodeContent = ""
expireTimestamp = 0
hasGeneratedQrCode = false
binding.btnRegenerate.text = getString(R.string.generate_invite_code)
ToastUtils.showSuccess(this@InvitationCodeActivity, getString(R.string.operation_success))
} else { } else {
dialog.showError(getString(R.string.password_error)) passwordDialog.showError(
response.msg.ifEmpty { getString(R.string.password_error) }
)
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "撤销邀请异常", e)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
dialog.showError(getString(R.string.network_error)) isLoading = false
loadingDialog.dismiss()
passwordDialog.showError(getString(R.string.network_error))
} }
} }
} }
} }
/**
* 显示密码输入对话框
*/
private fun showPasswordDialog(onConfirm: (String, PasswordInputDialog) -> Unit) {
val dialog = PasswordInputDialog(this)
dialog.setTitle(getString(R.string.input_op_pwd))
dialog.setOnConfirmListener { password ->
onConfirm(password, dialog)
}
dialog.show()
}
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
expireTimeDialog?.dismiss() expireTimeDialog?.dismiss()

View File

@@ -0,0 +1,123 @@
package com.ckkj.defense_device.personal
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.ckkj.defense_device.R
import com.ckkj.defense_device.bean.JoinTheFamilyBean
import com.ckkj.defense_device.databinding.JoinFamilyInviteActivityBinding
import com.ckkj.defense_device.home.HomeActivity
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.view.LoadingDialog
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
// 邀请加入家庭
class JoinFamilyInviteActivity : AppCompatActivity() {
private lateinit var binding: JoinFamilyInviteActivityBinding
private var isLoading = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = JoinFamilyInviteActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
ImmersionBar.with(this)
.statusBarDarkFont(true)
.titleBar(binding.topBar)
.init()
binding.ivBack.setOnClickListener { finish() }
binding.btnJoinNow.setOnClickListener { joinFamily() }
displayInviterInfo()
}
private fun joinFamily() {
if (isLoading) return
val parentId = intent.getStringExtra(EXTRA_USER_ID).orEmpty()
val inviteCode = intent.getStringExtra(EXTRA_INVITE_CODE).orEmpty()
if (parentId.isEmpty() || inviteCode.isEmpty()) {
Log.e(TAG, "加入家庭参数无效: parentId=$parentId, inviteCode=$inviteCode")
Toast.makeText(this, R.string.network_error, Toast.LENGTH_SHORT).show()
return
}
Log.e(TAG, "加入家庭请求: parentId=$parentId, inviteCode=$inviteCode")
isLoading = true
val loadingDialog = LoadingDialog(this).apply { show() }
CoroutineScope(Dispatchers.IO).launch {
try {
val response = RetrofitNetwork.getNetworkService()
.joinTheFamily(JoinTheFamilyBean(parentId = parentId, inviteCode = inviteCode))
Log.e(TAG, "加入家庭响应: code=${response.code}, msg=${response.msg}, data=${response.data}")
withContext(Dispatchers.Main) {
isLoading = false
loadingDialog.dismiss()
if (response.code == 200) {
Log.e(TAG, "加入家庭成功,返回首页刷新")
ToastUtils.showSuccess(this@JoinFamilyInviteActivity, getString(R.string.operation_success))
val homeIntent = Intent(this@JoinFamilyInviteActivity, HomeActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
putExtra(HomeActivity.EXTRA_REFRESH, true)
}
startActivity(homeIntent)
finish()
} else {
Log.e(TAG, "加入家庭失败: code=${response.code}, msg=${response.msg}")
Toast.makeText(
this@JoinFamilyInviteActivity,
response.msg.ifBlank { getString(R.string.network_error) },
Toast.LENGTH_SHORT
).show()
}
}
} catch (e: Exception) {
Log.e(TAG, "加入家庭请求异常", e)
withContext(Dispatchers.Main) {
isLoading = false
loadingDialog.dismiss()
Toast.makeText(this@JoinFamilyInviteActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
}
}
}
}
private fun displayInviterInfo() {
val nickName = intent.getStringExtra(EXTRA_NICK_NAME).orEmpty()
val avatar = intent.getStringExtra(EXTRA_AVATAR)
binding.tvNickname.text = nickName.ifEmpty { getString(R.string.nickname_placeholder) }
binding.tvInviteMessage.text = getString(
R.string.invite_join_family_message,
nickName.ifEmpty { getString(R.string.nickname_placeholder) }
)
val requestOptions = RequestOptions()
.placeholder(R.mipmap.img)
.error(R.mipmap.img)
Glide.with(this)
.load(avatar)
.apply(requestOptions)
.circleCrop()
.into(binding.ivAvatar)
}
companion object {
private const val TAG = "JoinFamilyInviteActivity"
const val EXTRA_INVITE_CODE = "invite_code"
const val EXTRA_NICK_NAME = "nick_name"
const val EXTRA_AVATAR = "avatar"
const val EXTRA_PARENT_ID = "parent_id"
const val EXTRA_USER_ID = "user_id"
}
}

View File

@@ -138,7 +138,7 @@ object ApiConfig {
*/ */
private fun buildFakeErrorResponse(request: Request, msg: String, code: Int = 1): Response { private fun buildFakeErrorResponse(request: Request, msg: String, code: Int = 1): Response {
val baseInfo = BaseInfo( val baseInfo = BaseInfo(
message = msg, msg = msg,
code = code, code = code,
data = null, data = null,
) )

View File

@@ -14,11 +14,15 @@ import com.ckkj.defense_device.bean.DeleteBean
import com.ckkj.defense_device.bean.DeleteDeviceUserRequest import com.ckkj.defense_device.bean.DeleteDeviceUserRequest
import com.ckkj.defense_device.bean.DeviceAlarmResponse import com.ckkj.defense_device.bean.DeviceAlarmResponse
import com.ckkj.defense_device.bean.FamilyMembersBean import com.ckkj.defense_device.bean.FamilyMembersBean
import com.ckkj.defense_device.bean.InviterInfoBean
import com.ckkj.defense_device.bean.JoinTheFamilyBean
import com.ckkj.defense_device.bean.MessageBean import com.ckkj.defense_device.bean.MessageBean
import com.ckkj.defense_device.bean.MessageListResponse import com.ckkj.defense_device.bean.MessageListResponse
import com.ckkj.defense_device.bean.ObtainInviterInformationBean import com.ckkj.defense_device.bean.ObtainInviterInformationBean
import com.ckkj.defense_device.bean.ObtainInviterInformationRequest import com.ckkj.defense_device.bean.ObtainInviterInformationRequest
import com.ckkj.defense_device.bean.SelectInviterInfoBean
import com.ckkj.defense_device.bean.SwitchFamilyRequest import com.ckkj.defense_device.bean.SwitchFamilyRequest
import com.ckkj.defense_device.bean.UpdateFamilyUserRequest
import com.ckkj.defense_device.bean.UpdatePassword import com.ckkj.defense_device.bean.UpdatePassword
import com.ckkj.defense_device.bean.UpdateUserInfo import com.ckkj.defense_device.bean.UpdateUserInfo
import com.ckkj.defense_device.bean.UploadResponse import com.ckkj.defense_device.bean.UploadResponse
@@ -191,7 +195,7 @@ interface ApiService {
/** /**
* 获取邀请人信息 * 获取家庭成员信息
*/ */
@POST("/app/obtainInviterInformation") @POST("/app/obtainInviterInformation")
suspend fun obtainInviterInformation(@Body request: ObtainInviterInformationRequest): GetCodeBean<ObtainInviterInformationBean> suspend fun obtainInviterInformation(@Body request: ObtainInviterInformationRequest): GetCodeBean<ObtainInviterInformationBean>
@@ -250,8 +254,22 @@ interface ApiService {
/** /**
* 修改家庭用户状态 * 修改家庭用户状态
*/ */
//@POST("/app/updateFamilyUser") @POST("/app/updateFamilyUser")
suspend fun updateFamilyUser(@Body request: UpdateFamilyUserRequest): GetCodeBean<Any>
/**
* 获取邀请人信息
*/
@POST("/app/selectInviterInfo")
suspend fun selectInviterInfo(@Body request: SelectInviterInfoBean): GetCodeBean<InviterInfoBean>
/**
* 加入xxx家庭
*/
@POST("/app/joinTheFamily")
suspend fun joinTheFamily(@Body request: JoinTheFamilyBean): GetCodeBean<Any>
} }

View File

@@ -13,6 +13,7 @@ object RetrofitNetwork {
private const val DEFAULT_TIMEOUT = 30L private const val DEFAULT_TIMEOUT = 30L
const val BASE_URL = "https://stem-boozy-margarine.ngrok-free.dev" const val BASE_URL = "https://stem-boozy-margarine.ngrok-free.dev"
//const val BASE_URL = "http://service.reinkun.com:8001"
private var retrofit: Retrofit? = null private var retrofit: Retrofit? = null
private val gson = Gson() private val gson = Gson()

View File

@@ -5,5 +5,5 @@
<stroke <stroke
android:width="1dp" android:width="1dp"
android:color="@color/green" /> android:color="@color/green" />
<corners android:radius="8dp" /> <corners android:radius="25dp" />
</shape> </shape>

View File

@@ -2,5 +2,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android" <shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:shape="rectangle">
<solid android:color="@color/green" /> <solid android:color="@color/green" />
<corners android:radius="8dp" /> <corners android:radius="25dp" />
</shape> </shape>

View File

@@ -164,6 +164,8 @@
android:background="@drawable/rounded_blue_bg" android:background="@drawable/rounded_blue_bg"
android:paddingHorizontal="12dp" android:paddingHorizontal="12dp"
android:maxWidth="120dp" android:maxWidth="120dp"
android:elevation="0dp"
android:stateListAnimator="@null"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
@@ -223,6 +225,8 @@
android:layout_marginTop="40dp" android:layout_marginTop="40dp"
android:text="@string/login" android:text="@string/login"
android:textSize="18sp" android:textSize="18sp"
android:elevation="0dp"
android:stateListAnimator="@null"
android:background="@drawable/rounded_blue_bg" android:background="@drawable/rounded_blue_bg"
android:textColor="@color/white" android:textColor="@color/white"
app:layout_constraintTop_toBottomOf="@+id/smsRowLayout" app:layout_constraintTop_toBottomOf="@+id/smsRowLayout"

View File

@@ -123,6 +123,8 @@
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="16dp" android:textSize="16dp"
android:textStyle="bold" android:textStyle="bold"
android:elevation="0dp"
android:stateListAnimator="@null"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"

View File

@@ -80,6 +80,8 @@
android:background="@drawable/rounded_blue_bg" android:background="@drawable/rounded_blue_bg"
android:text="@string/select_wifi" android:text="@string/select_wifi"
android:textColor="@color/white" android:textColor="@color/white"
android:elevation="0dp"
android:stateListAnimator="@null"
android:textSize="14sp" /> android:textSize="14sp" />
</LinearLayout> </LinearLayout>

View File

@@ -88,8 +88,41 @@
android:paddingBottom="16dp"/> android:paddingBottom="16dp"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<!-- 空页面 -->
<LinearLayout
android:id="@+id/layoutNoPermission"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<ImageView
android:layout_width="244dp"
android:layout_height="131dp"
android:src="@mipmap/default_page"/>
</LinearLayout>
<LinearLayout
android:id="@+id/layoutNoNetwork"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:paddingHorizontal="32dp"
android:background="@color/white"
android:visibility="gone">
<ImageView
android:layout_width="244dp"
android:layout_height="131dp"
android:src="@mipmap/no_network_img_name" />
</LinearLayout>
<!-- 底部按钮 --> <!-- 底部按钮 -->
<LinearLayout <LinearLayout
android:id="@+id/butting"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingHorizontal="16dp" android:paddingHorizontal="16dp"
@@ -102,6 +135,8 @@
android:text="@string/confirm" android:text="@string/confirm"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="14dp" android:textSize="14dp"
android:elevation="0dp"
android:stateListAnimator="@null"
android:background="@drawable/rounded_blue_bg" /> android:background="@drawable/rounded_blue_bg" />
</LinearLayout> </LinearLayout>

View File

@@ -105,6 +105,8 @@
android:textAllCaps="false" android:textAllCaps="false"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="16sp" android:textSize="16sp"
android:elevation="0dp"
android:stateListAnimator="@null"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/etConfirmPassword" /> app:layout_constraintTop_toBottomOf="@+id/etConfirmPassword" />

View File

@@ -287,6 +287,8 @@
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="14dp" android:textSize="14dp"
android:textStyle="bold" android:textStyle="bold"
android:elevation="0dp"
android:stateListAnimator="@null"
android:background="@drawable/rounded_blue_bg" android:background="@drawable/rounded_blue_bg"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"

View File

@@ -107,6 +107,8 @@
android:textSize="17sp" android:textSize="17sp"
android:textStyle="bold" android:textStyle="bold"
android:background="@drawable/bg_capture_face_button" android:background="@drawable/bg_capture_face_button"
android:elevation="0dp"
android:stateListAnimator="@null"
android:textColor="#F2FFFFFF" /> android:textColor="#F2FFFFFF" />
</LinearLayout> </LinearLayout>

View File

@@ -153,6 +153,8 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
android:elevation="0dp"
android:stateListAnimator="@null"
android:layout_marginStart="12dp" /> android:layout_marginStart="12dp" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
@@ -246,11 +248,13 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="56dp" android:layout_height="56dp"
android:layout_marginTop="32dp" android:layout_marginTop="32dp"
android:text="@string/determine" android:text="@string/determine"
android:textAllCaps="false" android:textAllCaps="false"
android:textSize="16sp" android:textSize="16sp"
app:cornerRadius="12dp" app:cornerRadius="12dp"
android:textColor="@color/white" android:textColor="@color/white"
android:elevation="0dp"
android:stateListAnimator="@null"
android:background="@drawable/rounded_blue_bg" android:background="@drawable/rounded_blue_bg"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"

View File

@@ -168,6 +168,22 @@
android:src="@mipmap/default_page" /> android:src="@mipmap/default_page" />
</LinearLayout> </LinearLayout>
<LinearLayout
android:id="@+id/layoutNoNetwork"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:paddingHorizontal="32dp"
android:background="@color/white"
android:visibility="gone">
<ImageView
android:layout_width="244dp"
android:layout_height="131dp"
android:src="@mipmap/no_network_img_name" />
</LinearLayout>
</LinearLayout> </LinearLayout>
<View <View

View File

@@ -118,6 +118,7 @@
</FrameLayout> </FrameLayout>
<!-- 使用智能文字和图标 --> <!-- 使用智能文字和图标 -->
<LinearLayout <LinearLayout
android:id="@+id/sys"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
@@ -125,16 +126,16 @@
android:gravity="center_vertical"> android:gravity="center_vertical">
<ImageView <ImageView
android:layout_width="20dp" android:layout_width="24dp"
android:layout_height="20dp" android:layout_height="24dp"
android:src="@drawable/syis" /> android:src="@mipmap/syis" />
<TextView <TextView
android:textColor="@color/green"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:text="@string/use_smart_defense_app" android:text="@string/use_smart_defense_app"
android:textColor="@color/black"
android:textSize="14sp" /> android:textSize="14sp" />
</LinearLayout> </LinearLayout>
@@ -211,6 +212,8 @@
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:text="@string/cancel_invite" android:text="@string/cancel_invite"
android:textColor="@color/green" android:textColor="@color/green"
android:elevation="0dp"
android:stateListAnimator="@null"
android:textSize="16sp" android:textSize="16sp"
android:background="@drawable/bg_blue_border_round" android:background="@drawable/bg_blue_border_round"
android:textAllCaps="false" /> android:textAllCaps="false" />
@@ -223,6 +226,8 @@
android:text="@string/regenerate" android:text="@string/regenerate"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="16sp" android:textSize="16sp"
android:elevation="0dp"
android:stateListAnimator="@null"
android:background="@drawable/bg_blue_round" android:background="@drawable/bg_blue_round"
android:textAllCaps="false" /> android:textAllCaps="false" />
</LinearLayout> </LinearLayout>

View File

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
tools:context=".personal.JoinFamilyInviteActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/topBar"
android:layout_width="match_parent"
android:layout_height="56dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/ivBack"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="16dp"
android:contentDescription="@string/back"
android:src="@mipmap/back"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:id="@+id/contentLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingHorizontal="32dp"
app:layout_constraintBottom_toTopOf="@id/btnJoinNow"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/topBar"
app:layout_constraintVertical_bias="0.35">
<ImageView
android:id="@+id/ivAvatar"
android:layout_width="88dp"
android:layout_height="88dp"
android:contentDescription="@string/avatar"
android:scaleType="centerCrop"
android:src="@mipmap/img"/>
<TextView
android:id="@+id/tvNickname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/nickname_placeholder"
android:textColor="@color/black"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvInviteMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center"
android:text="@string/invite_join_family_message_preview"
android:textColor="@color/gray"
android:textSize="14sp" />
</LinearLayout>
<android.widget.Button
android:id="@+id/btnJoinNow"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="40dp"
android:background="@drawable/rounded_blue_bg"
android:text="@string/join_now"
android:textColor="@color/white"
android:textSize="16sp"
android:textStyle="bold"
android:elevation="0dp"
android:stateListAnimator="@null"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -35,60 +35,107 @@
android:textStyle="bold" /> android:textStyle="bold" />
</LinearLayout> </LinearLayout>
<!-- 搜索区域 -->
<LinearLayout <LinearLayout
android:id="@+id/searchLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:paddingHorizontal="16dp"
android:paddingVertical="12dp"
android:layout_marginTop="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="@drawable/bg_user_manage"
android:gravity="center_vertical"
android:paddingHorizontal="16dp">
<ImageView
android:layout_width="18dp"
android:layout_height="18dp"
android:src="@mipmap/search"
android:layout_marginRight="8dp"/>
<EditText
android:id="@+id/etSearch"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="@null"
android:hint="@string/search_preset_text"
android:textColorHint="@color/gray"
android:textColor="@color/black"
android:textSize="14sp"
android:inputType="text"
android:imeOptions="actionSearch"
android:singleLine="true"/>
</LinearLayout>
</LinearLayout>
<!-- 列表区域 -->
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefresh"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1"> android:layout_weight="1"
<com.yanzhenjie.recyclerview.SwipeRecyclerView android:orientation="vertical">
android:id="@+id/rvLinkedUser"
<!-- 搜索区域 -->
<LinearLayout
android:id="@+id/searchLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:background="@color/white"
android:paddingHorizontal="16dp" android:paddingHorizontal="16dp"
android:paddingTop="8dp" android:paddingVertical="12dp"
android:paddingBottom="16dp"/> android:layout_marginTop="8dp">
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="@drawable/bg_user_manage"
android:gravity="center_vertical"
android:paddingHorizontal="16dp">
<ImageView
android:layout_width="18dp"
android:layout_height="18dp"
android:src="@mipmap/search"
android:layout_marginRight="8dp"/>
<EditText
android:id="@+id/etSearch"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="@null"
android:hint="@string/search_preset_text"
android:textColorHint="@color/gray"
android:textColor="@color/black"
android:textSize="14sp"
android:inputType="text"
android:imeOptions="actionSearch"
android:singleLine="true"/>
</LinearLayout>
</LinearLayout>
<!-- 内容区域 -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<!-- 列表 -->
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.yanzhenjie.recyclerview.SwipeRecyclerView
android:id="@+id/rvLinkedUser"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<!-- 空页面 -->
<LinearLayout
android:id="@+id/layoutNoPermission"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<ImageView
android:layout_width="244dp"
android:layout_height="131dp"
android:src="@mipmap/default_page"/>
</LinearLayout>
<LinearLayout
android:id="@+id/layoutNoNetwork"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:paddingHorizontal="32dp"
android:background="@color/white"
android:visibility="gone">
<ImageView
android:layout_width="244dp"
android:layout_height="131dp"
android:src="@mipmap/no_network_img_name" />
</LinearLayout>
</FrameLayout>
</LinearLayout>
<!-- 底部按钮 --> <!-- 底部按钮 -->
<LinearLayout <LinearLayout
android:id="@+id/butting"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -102,6 +149,8 @@
android:text="@string/add_linked_user" android:text="@string/add_linked_user"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="14dp" android:textSize="14dp"
android:elevation="0dp"
android:stateListAnimator="@null"
android:background="@drawable/rounded_blue_bg" /> android:background="@drawable/rounded_blue_bg" />
<!-- 悬浮加号按钮 --> <!-- 悬浮加号按钮 -->
<ImageView <ImageView

View File

@@ -58,7 +58,22 @@
<ImageView <ImageView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:src="@mipmap/no_message" /> android:src="@mipmap/default_page_img_name" />
</LinearLayout>
<LinearLayout
android:id="@+id/layoutNoNetwork"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:paddingHorizontal="32dp"
android:visibility="gone">
<ImageView
android:layout_width="244dp"
android:layout_height="131dp"
android:src="@mipmap/no_network_img_name" />
</LinearLayout> </LinearLayout>
<TextView <TextView

View File

@@ -148,6 +148,8 @@
android:background="@drawable/rounded_blue_bg" android:background="@drawable/rounded_blue_bg"
android:paddingHorizontal="12dp" android:paddingHorizontal="12dp"
android:maxWidth="120dp" android:maxWidth="120dp"
android:elevation="0dp"
android:stateListAnimator="@null"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
@@ -293,6 +295,8 @@
android:textSize="16sp" android:textSize="16sp"
app:cornerRadius="12dp" app:cornerRadius="12dp"
android:textColor="@color/white" android:textColor="@color/white"
android:elevation="0dp"
android:stateListAnimator="@null"
android:background="@drawable/rounded_blue_bg" android:background="@drawable/rounded_blue_bg"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"

View File

@@ -35,6 +35,7 @@ tools:context=".login.RegisterSuccessActivity">
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="16sp" android:textSize="16sp"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@id/backIcon" app:layout_constraintBottom_toBottomOf="@id/backIcon"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
@@ -80,6 +81,8 @@ tools:context=".login.RegisterSuccessActivity">
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="14dp" android:textSize="14dp"
android:textStyle="bold" android:textStyle="bold"
android:elevation="0dp"
android:stateListAnimator="@null"
android:background="@drawable/rounded_blue_bg" android:background="@drawable/rounded_blue_bg"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"

View File

@@ -91,6 +91,8 @@
android:paddingHorizontal="16dp" android:paddingHorizontal="16dp"
android:text="@string/get_verification_code" android:text="@string/get_verification_code"
android:textColor="@color/white" android:textColor="@color/white"
android:elevation="0dp"
android:stateListAnimator="@null"
android:textSize="14sp" /> android:textSize="14sp" />
</RelativeLayout> </RelativeLayout>
@@ -131,6 +133,8 @@
android:background="@drawable/rounded_blue_bg" android:background="@drawable/rounded_blue_bg"
android:text="@string/confirm_reset_password_btn" android:text="@string/confirm_reset_password_btn"
android:textColor="@color/white" android:textColor="@color/white"
android:elevation="0dp"
android:stateListAnimator="@null"
android:textSize="16sp" /> android:textSize="16sp" />
</LinearLayout> </LinearLayout>

View File

@@ -81,6 +81,8 @@
android:text="@string/complete" android:text="@string/complete"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="16dp" android:textSize="16dp"
android:elevation="0dp"
android:stateListAnimator="@null"
android:textStyle="bold" /> android:textStyle="bold" />
</LinearLayout> </LinearLayout>

View File

@@ -76,6 +76,8 @@
android:background="@drawable/rounded_blue_bg" android:background="@drawable/rounded_blue_bg"
android:text="@string/submit" android:text="@string/submit"
android:textColor="@color/white" android:textColor="@color/white"
android:elevation="0dp"
android:stateListAnimator="@null"
android:textSize="16sp" /> android:textSize="16sp" />
</LinearLayout> </LinearLayout>

View File

@@ -182,6 +182,8 @@
android:textSize="16dp" android:textSize="16dp"
android:background="@drawable/rounded_blue_bg" android:background="@drawable/rounded_blue_bg"
android:padding="0dp" android:padding="0dp"
android:elevation="0dp"
android:stateListAnimator="@null"
android:layout_margin="0dp"/> android:layout_margin="0dp"/>
</LinearLayout> </LinearLayout>

View File

@@ -72,7 +72,7 @@
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1"/> android:layout_weight="1"/>
<Button <android.widget.Button
android:id="@+id/btnSave" android:id="@+id/btnSave"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -80,7 +80,9 @@
android:layout_marginEnd="20dp" android:layout_marginEnd="20dp"
android:layout_marginStart="20dp" android:layout_marginStart="20dp"
android:layout_marginBottom="90dp" android:layout_marginBottom="90dp"
android:backgroundTint="@color/green" /> android:elevation="0dp"
android:stateListAnimator="@null"
android:background="@drawable/rounded_blue_bg" />
</LinearLayout> </LinearLayout>

View File

@@ -80,6 +80,8 @@
android:background="@drawable/rounded_blue_bg" android:background="@drawable/rounded_blue_bg"
android:text="@string/select_wifi" android:text="@string/select_wifi"
android:textColor="@color/white" android:textColor="@color/white"
android:elevation="0dp"
android:stateListAnimator="@null"
android:textSize="14sp" /> android:textSize="14sp" />
</LinearLayout> </LinearLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 956 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 B

View File

@@ -48,6 +48,7 @@
<string name="reset_password">重置密码</string> <string name="reset_password">重置密码</string>
<string name="new_password">新密码</string> <string name="new_password">新密码</string>
<string name="reset_success">密码重置成功</string> <string name="reset_success">密码重置成功</string>
<string name="resetting">重置中...</string>
<!-- Face Login/Verification --> <!-- Face Login/Verification -->
<string name="face_login">人脸登录</string> <string name="face_login">人脸登录</string>
@@ -109,6 +110,10 @@
<string name="enter_family_name">请输入家庭名称</string> <string name="enter_family_name">请输入家庭名称</string>
<string name="create_family">创建家庭</string> <string name="create_family">创建家庭</string>
<string name="join_family">加入家庭</string> <string name="join_family">加入家庭</string>
<string name="join_now">立即加入</string>
<string name="nickname_placeholder">这里是昵称</string>
<string name="invite_join_family_message">邀请你加入%s的家庭</string>
<string name="invite_join_family_message_preview">邀请你加入这里是昵称的家庭</string>
<string name="family_code">家庭码</string> <string name="family_code">家庭码</string>
<string name="enter_family_code">请输入家庭码</string> <string name="enter_family_code">请输入家庭码</string>
<string name="family_with_normal">的家庭</string> <string name="family_with_normal">的家庭</string>

View File

@@ -20,5 +20,7 @@
<color name="green_light">#E8F7F7</color> <color name="green_light">#E8F7F7</color>
<color name="gray_divider">#E5E5E5</color> <color name="gray_divider">#E5E5E5</color>
<color name="orange">#FF9800</color> <color name="orange">#FF9800</color>
<color name="gradientLayout1">#EBDCED</color>
<color name="gradientLayout2">#CAF4FF</color>
</resources> </resources>

View File

@@ -44,6 +44,7 @@
<string name="reset_password">Reset Password</string> <string name="reset_password">Reset Password</string>
<string name="new_password">New Password</string> <string name="new_password">New Password</string>
<string name="reset_success">Password reset successful</string> <string name="reset_success">Password reset successful</string>
<string name="resetting">Resetting...</string>
<!-- Face Login/Verification --> <!-- Face Login/Verification -->
<string name="face_capture_hint">Please align your face within the frame</string> <string name="face_capture_hint">Please align your face within the frame</string>
@@ -104,6 +105,10 @@
<string name="enter_family_name">Enter family name</string> <string name="enter_family_name">Enter family name</string>
<string name="create_family">Create Family</string> <string name="create_family">Create Family</string>
<string name="join_family">Join Family</string> <string name="join_family">Join Family</string>
<string name="join_now">Join Now</string>
<string name="nickname_placeholder">Nickname</string>
<string name="invite_join_family_message">Inviting you to join %s\'s family</string>
<string name="invite_join_family_message_preview">Inviting you to join Nickname\'s family</string>
<string name="family_code">Family Code</string> <string name="family_code">Family Code</string>
<string name="enter_family_code">Enter family code</string> <string name="enter_family_code">Enter family code</string>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">service.reinkun.com</domain>
</domain-config>
</network-security-config>