diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a119749..3cde424 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,8 @@ + + @@ -23,6 +25,12 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.DefenseApplication"> + + @@ -52,7 +60,7 @@ android:exported="false" /> + android:exported="false" /> diff --git a/app/src/main/java/com/example/defenseapplication/liveVideo/AlarmLogActivity.kt b/app/src/main/java/com/example/defenseapplication/liveVideo/AlarmLogActivity.kt new file mode 100644 index 0000000..dacd45e --- /dev/null +++ b/app/src/main/java/com/example/defenseapplication/liveVideo/AlarmLogActivity.kt @@ -0,0 +1,29 @@ +package com.example.defenseapplication.liveVideo + +import android.os.Bundle +import android.view.View +import androidx.appcompat.app.AppCompatActivity +import com.example.defenseapplication.R +import com.example.defenseapplication.databinding.AlarmLogActivityBinding +import com.gyf.immersionbar.ImmersionBar + +class AlarmLogActivity : AppCompatActivity() { + private lateinit var binding: AlarmLogActivityBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = AlarmLogActivityBinding.inflate(layoutInflater) + setContentView(binding.root) + ImmersionBar.with(this) + .statusBarDarkFont(true) + .titleBar(binding.topBar) + .init() + initListener() + } + + private fun initListener() { + binding.btnBack.setOnClickListener { + finish() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/defenseapplication/liveVideo/LiveVideoActivity.kt b/app/src/main/java/com/example/defenseapplication/liveVideo/LiveVideoActivity.kt index b4eb20d..4ceb1af 100644 --- a/app/src/main/java/com/example/defenseapplication/liveVideo/LiveVideoActivity.kt +++ b/app/src/main/java/com/example/defenseapplication/liveVideo/LiveVideoActivity.kt @@ -1,24 +1,38 @@ package com.example.defenseapplication.liveVideo +import android.Manifest +import android.content.Intent import android.content.pm.ActivityInfo +import android.content.pm.PackageManager import android.media.AudioManager import android.media.MediaPlayer +import android.media.MediaRecorder import android.net.Uri +import android.os.Build import android.os.Bundle +import android.os.Environment import android.view.SurfaceHolder import android.view.View import android.view.ViewGroup import android.view.WindowManager +import android.view.animation.Animation +import android.view.animation.LinearInterpolator +import android.view.animation.ScaleAnimation import android.widget.Toast import androidx.activity.OnBackPressedDispatcher +import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.compose.ui.test.isEnabled +import androidx.core.content.ContextCompat import com.example.defenseapplication.R import com.example.defenseapplication.databinding.LiveVideoActivityBinding import com.gyf.immersionbar.ImmersionBar import com.gyf.immersionbar.ktx.destroyImmersionBar +import java.io.File import java.io.IOException +private const val TAG = "LiveVideoActivity" + class LiveVideoActivity : AppCompatActivity(), SurfaceHolder.Callback { private lateinit var binding: LiveVideoActivityBinding @@ -28,11 +42,24 @@ class LiveVideoActivity : AppCompatActivity(), SurfaceHolder.Callback { private var isVoiceEnabled = false private var isLightOn = false private var isRecording = false + private var mediaRecorder: MediaRecorder? = null + private var micPulseAnimation: ScaleAnimation? = null private var videoWidth = 0 private var videoHeight = 0 private val testVideoUrl = "https://vjs.zencdn.net/v/oceans.mp4" + + private val requestPermissionLauncher = registerForActivityResult( + ActivityResultContracts.RequestPermission() + ) { isGranted -> + if (isGranted) { + startRecordingMode() + } else { + Toast.makeText(this, "需要录音权限才能使用控制功能", Toast.LENGTH_SHORT).show() + isRecording = false + } + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -140,7 +167,8 @@ class LiveVideoActivity : AppCompatActivity(), SurfaceHolder.Callback { } binding.ivMenu.setOnClickListener { - Toast.makeText(this, "菜单", Toast.LENGTH_SHORT).show() + val intent = Intent(this, SettingsActivity::class.java) + startActivity(intent) } binding.ivFullscreen.setOnClickListener { @@ -166,6 +194,12 @@ class LiveVideoActivity : AppCompatActivity(), SurfaceHolder.Callback { binding.llAlarmLog.setOnClickListener { Toast.makeText(this, "查看告警日志", Toast.LENGTH_SHORT).show() } + + // 点击xq箭头跳转到AlarmLogActivity + binding.xq.setOnClickListener { + val intent = Intent(this, AlarmLogActivity::class.java) + startActivity(intent) + } } private fun togglePlayPause() { @@ -270,14 +304,124 @@ class LiveVideoActivity : AppCompatActivity(), SurfaceHolder.Callback { } private fun toggleRecording() { - isRecording = !isRecording - - if (isRecording) { - binding.ivRecord.setImageResource(com.example.defenseapplication.R.drawable.copy_open) - Toast.makeText(this, "开始录像", Toast.LENGTH_SHORT).show() + if (!isRecording) { + if (checkRecordAudioPermission()) { + startRecordingMode() + } else { + requestRecordAudioPermission() + } } else { - binding.ivRecord.setImageResource(com.example.defenseapplication.R.drawable.copy) - Toast.makeText(this, "停止录像", Toast.LENGTH_SHORT).show() + stopRecordingMode() + } + } + + private fun checkRecordAudioPermission(): Boolean { + return ContextCompat.checkSelfPermission( + this, + Manifest.permission.RECORD_AUDIO + ) == PackageManager.PERMISSION_GRANTED + } + + private fun requestRecordAudioPermission() { + requestPermissionLauncher.launch(Manifest.permission.RECORD_AUDIO) + } + + private fun startRecordingMode() { + isRecording = true + + binding.ivRecord.setImageResource(com.example.defenseapplication.R.drawable.copy_open) + // 隐藏告警日志区域,显示控制台区域 + binding.llAlarmLog.visibility = View.GONE + binding.consoleBar.visibility = View.VISIBLE + + // 麦克风切换成开启状态 + binding.ivVoice.setImageResource(com.example.defenseapplication.R.drawable.voice_open) + isVoiceEnabled = true + + // 启动语音录制 + startVoiceRecording() + + // 启动麦克风脉冲动画 + startMicPulseAnimation() + + Toast.makeText(this, "开始控制模式", Toast.LENGTH_SHORT).show() + } + + private fun stopRecordingMode() { + isRecording = false + + binding.ivRecord.setImageResource(com.example.defenseapplication.R.drawable.copy) + // 显示告警日志区域,隐藏控制台区域 + binding.llAlarmLog.visibility = View.VISIBLE + binding.consoleBar.visibility = View.GONE + + // 麦克风切换成关闭状态 + binding.ivVoice.setImageResource(com.example.defenseapplication.R.drawable.voice) + isVoiceEnabled = false + + // 停止语音录制 + stopVoiceRecording() + + // 停止麦克风脉冲动画 + stopMicPulseAnimation() + + Toast.makeText(this, "停止控制模式", Toast.LENGTH_SHORT).show() + } + + private fun startMicPulseAnimation() { + micPulseAnimation = ScaleAnimation( + 1.0f, 1.3f, + 1.0f, 1.3f, + Animation.RELATIVE_TO_SELF, 0.5f, + Animation.RELATIVE_TO_SELF, 0.5f + ).apply { + duration = 500 + repeatCount = Animation.INFINITE + repeatMode = Animation.REVERSE + interpolator = LinearInterpolator() + } + binding.ivVoice.startAnimation(micPulseAnimation) + } + + private fun stopMicPulseAnimation() { + micPulseAnimation?.let { + binding.ivVoice.clearAnimation() + micPulseAnimation = null + } + } + + private fun startVoiceRecording() { + mediaRecorder = MediaRecorder().apply { + setAudioSource(MediaRecorder.AudioSource.MIC) + setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP) + val outputDir = getExternalFilesDir(null) ?: filesDir + val audioDir = File(outputDir, "audio") + if (!audioDir.exists()) { + audioDir.mkdirs() + } + val outputFile = File(audioDir, "voice_record_${System.currentTimeMillis()}.3gp") + setOutputFile(outputFile.absolutePath) + setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB) + + try { + prepare() + start() + } catch (e: IOException) { + e.printStackTrace() + Toast.makeText(this@LiveVideoActivity, "录音启动失败", Toast.LENGTH_SHORT).show() + } + } + } + + private fun stopVoiceRecording() { + mediaRecorder?.let { + try { + it.stop() + it.release() + } catch (e: Exception) { + e.printStackTrace() + } + mediaRecorder = null } } diff --git a/app/src/main/java/com/example/defenseapplication/liveVideo/SettingsActivity.kt b/app/src/main/java/com/example/defenseapplication/liveVideo/SettingsActivity.kt new file mode 100644 index 0000000..57d06ec --- /dev/null +++ b/app/src/main/java/com/example/defenseapplication/liveVideo/SettingsActivity.kt @@ -0,0 +1,25 @@ +package com.example.defenseapplication.liveVideo + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.example.defenseapplication.databinding.SettingsActivityBinding +import com.gyf.immersionbar.ImmersionBar + +class SettingsActivity : AppCompatActivity() { + + private lateinit var binding: SettingsActivityBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = SettingsActivityBinding.inflate(layoutInflater) + setContentView(binding.root) + // ImmersionBar核心调用,沉浸式 + ImmersionBar.with(this) + .statusBarDarkFont(true) + .titleBar(binding.topBar) + .init() + binding.ivBack.setOnClickListener { + finish() + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/back_white.png b/app/src/main/res/drawable/back_white.png new file mode 100644 index 0000000..39089f2 Binary files /dev/null and b/app/src/main/res/drawable/back_white.png differ diff --git a/app/src/main/res/drawable/console.png b/app/src/main/res/drawable/console.png new file mode 100644 index 0000000..98ac9fb Binary files /dev/null and b/app/src/main/res/drawable/console.png differ diff --git a/app/src/main/res/drawable/delete.png b/app/src/main/res/drawable/delete.png new file mode 100644 index 0000000..668b0a1 Binary files /dev/null and b/app/src/main/res/drawable/delete.png differ diff --git a/app/src/main/res/drawable/download.png b/app/src/main/res/drawable/download.png new file mode 100644 index 0000000..b6bc9d0 Binary files /dev/null and b/app/src/main/res/drawable/download.png differ diff --git a/app/src/main/res/drawable/expand.png b/app/src/main/res/drawable/expand.png new file mode 100644 index 0000000..b203b4d Binary files /dev/null and b/app/src/main/res/drawable/expand.png differ diff --git a/app/src/main/res/drawable/mute.png b/app/src/main/res/drawable/mute.png new file mode 100644 index 0000000..a9a43da Binary files /dev/null and b/app/src/main/res/drawable/mute.png differ diff --git a/app/src/main/res/drawable/start.png b/app/src/main/res/drawable/start.png new file mode 100644 index 0000000..b058f91 Binary files /dev/null and b/app/src/main/res/drawable/start.png differ diff --git a/app/src/main/res/layout/alarm_log_activity.xml b/app/src/main/res/layout/alarm_log_activity.xml new file mode 100644 index 0000000..2c05859 --- /dev/null +++ b/app/src/main/res/layout/alarm_log_activity.xml @@ -0,0 +1,364 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>--> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/live_video_activity.xml b/app/src/main/res/layout/live_video_activity.xml index 298ff10..63960b2 100644 --- a/app/src/main/res/layout/live_video_activity.xml +++ b/app/src/main/res/layout/live_video_activity.xml @@ -187,6 +187,25 @@ + + + + + + + + + + diff --git a/app/src/main/res/layout/settings_activity.xml b/app/src/main/res/layout/settings_activity.xml new file mode 100644 index 0000000..c23ce63 --- /dev/null +++ b/app/src/main/res/layout/settings_activity.xml @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 65904ee..92102b5 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -13,5 +13,6 @@ #99000000 #E7E7E7 #F9F9F9 + #03A9F4 \ No newline at end of file