diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e6b0c77..57e8f14 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,9 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/daodaoshi/chick_mood/AddActivity.kt b/app/src/main/java/com/daodaoshi/chick_mood/AddActivity.kt new file mode 100644 index 0000000..23234d9 --- /dev/null +++ b/app/src/main/java/com/daodaoshi/chick_mood/AddActivity.kt @@ -0,0 +1,416 @@ +package com.daodaoshi.chick_mood + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.hardware.Sensor +import android.hardware.SensorEvent +import android.hardware.SensorEventListener +import android.hardware.SensorManager +import android.util.Log +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.view.View +import android.view.animation.AlphaAnimation +import android.view.animation.Animation +import android.widget.Button +import android.widget.ImageView +import android.widget.ProgressBar +import android.widget.TextView +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import android.os.Vibrator +import android.os.VibrationEffect +import com.chick_mood.data.model.Emotion +import java.text.DecimalFormat + +/** + * 添加心情页面 + * 通过摇晃手机来记录心情强度的核心功能页面 + */ +class AddActivity : AppCompatActivity(), SensorEventListener { + + // UI组件 + private lateinit var btnBack: ImageView + private lateinit var tvTitle: TextView + private lateinit var tvMoodText: TextView + private lateinit var tvMoodValue: TextView + private lateinit var progressBar: ProgressBar + private lateinit var ivChick: ImageView + private lateinit var btnEnd: Button + private lateinit var viewOverlay: View + private lateinit var tvOverlayText: TextView + + // 传感器相关 + private lateinit var sensorManager: SensorManager + private var accelerometer: Sensor? = null + private var isSensorRegistered = false + + // 震动反馈 + private lateinit var vibrator: Vibrator + + // 心情数据 + private var selectedEmotion: Emotion? = null + private var currentMoodValue = 0 + private val maxMoodValue = 100 + + // 遮罩引导系统 + private val handler = Handler(Looper.getMainLooper()) + private var overlayState = OVERLAY_STATE_INITIAL + + // 摇晃检测相关 + private var lastShakeTime = 0L + private var shakeThreshold = 15f // 摇晃强度阈值 + private val moodValueFormatter = DecimalFormat("#") + + companion object { + private const val TAG = "AddActivity" + private const val EXTRA_EMOTION_TYPE = "emotion_type" + + // 遮罩状态 + private const val OVERLAY_STATE_INITIAL = 0 + private const val OVERLAY_STATE_READY = 1 + private const val OVERLAY_STATE_HIDDEN = 2 + + // 遮罩显示时间(毫秒) + private const val OVERLAY_INITIAL_DURATION = 2000L + private const val OVERLAY_READY_DURATION = 2000L + + // 心情值累计参数 + private const val SHAKE_COOLDOWN = 100L // 摇晃冷却时间(毫秒) + private const val MOOD_INCREMENT_BASE = 2 // 基础心情值增量 + + /** + * 启动AddActivity + * @param context 上下文 + * @param emotion 选择的心情类型 + */ + fun start(context: Context, emotion: Emotion) { + val intent = Intent(context, AddActivity::class.java).apply { + putExtra(EXTRA_EMOTION_TYPE, emotion.name) + } + context.startActivity(intent) + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_add) + + // 获取传递的心情类型 + selectedEmotion = intent.getStringExtra(EXTRA_EMOTION_TYPE)?.let { + Emotion.valueOf(it) + } ?: Emotion.HAPPY + + initViews() + initSensor() + startOverlaySequence() + } + + /** + * 初始化视图组件 + */ + private fun initViews() { + btnBack = findViewById(R.id.btn_back) + tvTitle = findViewById(R.id.tv_title) + tvMoodText = findViewById(R.id.tv_mood_text) + tvMoodValue = findViewById(R.id.tv_mood_value) + progressBar = findViewById(R.id.progress_bar) + ivChick = findViewById(R.id.iv_chick) + btnEnd = findViewById(R.id.btn_end) + viewOverlay = findViewById(R.id.view_overlay) + tvOverlayText = findViewById(R.id.tv_overlay_text) + + // 设置标题 + tvTitle.text = "添加心情" + + // 根据心情类型设置小鸡图标 + selectedEmotion?.let { emotion -> + val chickIcon = getEmotionIconRes(emotion) + ivChick.setImageResource(chickIcon) + } + + // 初始化UI状态 + resetUI() + + // 设置点击监听器 + btnBack.setOnClickListener { + finish() + } + + btnEnd.setOnClickListener { + onEndButtonClicked() + } + + // 遮罩点击事件(可选:允许用户手动关闭遮罩) + viewOverlay.setOnClickListener { + // 可以选择是否允许用户手动关闭遮罩 + // hideOverlay() + } + } + + /** + * 初始化传感器和震动器 + */ + private fun initSensor() { + sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager + accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) + + // 初始化震动器 + vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator + + if (accelerometer == null) { + Toast.makeText(this, "无传感器", Toast.LENGTH_SHORT).show() + } + } + + /** + * 开始遮罩引导序列 + */ + private fun startOverlaySequence() { + overlayState = OVERLAY_STATE_INITIAL + viewOverlay.visibility = View.VISIBLE + tvOverlayText.visibility = View.VISIBLE + tvOverlayText.text = "握紧手机" + + // 添加淡入动画 + val fadeIn = AlphaAnimation(0f, 1f) + fadeIn.duration = 300 + viewOverlay.startAnimation(fadeIn) + tvOverlayText.startAnimation(fadeIn) + + // 2秒后切换到准备状态 + handler.postDelayed({ + if (overlayState == OVERLAY_STATE_INITIAL) { + overlayState = OVERLAY_STATE_READY + tvOverlayText.text = "开始摇晃,记录心情" + + // 再过2秒后隐藏遮罩 + handler.postDelayed({ + hideOverlay() + }, OVERLAY_READY_DURATION) + } + }, OVERLAY_INITIAL_DURATION) + } + + /** + * 隐藏遮罩 + */ + private fun hideOverlay() { + if (overlayState != OVERLAY_STATE_HIDDEN) { + overlayState = OVERLAY_STATE_HIDDEN + + // 添加淡出动画 + val fadeOut = AlphaAnimation(1f, 0f) + fadeOut.duration = 300 + fadeOut.setAnimationListener(object : Animation.AnimationListener { + override fun onAnimationStart(animation: Animation) {} + override fun onAnimationEnd(animation: Animation) { + viewOverlay.visibility = View.GONE + tvOverlayText.visibility = View.GONE + // 开始监听摇晃 + startShakeDetection() + } + override fun onAnimationRepeat(animation: Animation) {} + }) + viewOverlay.startAnimation(fadeOut) + tvOverlayText.startAnimation(fadeOut) + } + } + + /** + * 开始摇晃检测 + */ + private fun startShakeDetection() { + if (!isSensorRegistered && accelerometer != null) { + sensorManager.registerListener( + this, + accelerometer, + SensorManager.SENSOR_DELAY_NORMAL + ) + isSensorRegistered = true + } + } + + /** + * 停止摇晃检测 + */ + private fun stopShakeDetection() { + if (isSensorRegistered) { + sensorManager.unregisterListener(this) + isSensorRegistered = false + } + } + + /** + * 重置UI状态 + */ + private fun resetUI() { + currentMoodValue = 0 + tvMoodText.text = "开始!" + tvMoodValue.text = moodValueFormatter.format(currentMoodValue) + progressBar.progress = 0 + } + + /** + * 结束按钮点击处理 + */ + private fun onEndButtonClicked() { + stopShakeDetection() + + // 创建心情记录数据 + val currentTime = System.currentTimeMillis() + + // 跳转到编辑页面 + EditActivity.startForAdd( + context = this, + emotion = selectedEmotion!!, + moodValue = currentMoodValue, + timestamp = currentTime + ) + + finish() + } + + /** + * 更新心情值和UI + */ + private fun updateMoodValue(increment: Int) { + currentMoodValue = (currentMoodValue + increment).coerceAtMost(maxMoodValue) + + // 更新UI + tvMoodValue.text = moodValueFormatter.format(currentMoodValue) + progressBar.progress = currentMoodValue + + // 更新心情文案 + updateMoodText() + } + + /** + * 更新心情文案 + */ + private fun updateMoodText() { + val moodText = generateMoodText(currentMoodValue, selectedEmotion!!) + tvMoodText.text = moodText + } + + /** + * 生成心情文案 + */ + private fun generateMoodText(value: Int, emotion: Emotion): String { + val degree = when { + value <= 20 -> "有些" + value <= 40 -> "非常" + value <= 60 -> "超级" + value <= 80 -> "这也太" + else -> "完全无法控制" + } + return "$degree ${emotion.displayName}" + } + + /** + * 获取情绪对应的图标资源 + */ + private fun getEmotionIconRes(emotion: Emotion): Int { + return when (emotion) { + Emotion.HAPPY -> R.drawable.ic_placeholder_chick_happy + Emotion.ANGRY -> R.drawable.ic_placeholder_chick_angry + Emotion.SAD -> R.drawable.ic_placeholder_chick_sad + Emotion.WORRIED -> R.drawable.ic_placeholder_chick_worried + Emotion.LONELY -> R.drawable.ic_placeholder_chick_lonely + Emotion.SCARED -> R.drawable.ic_placeholder_chick_scared + } + } + + // SensorEventListener 回调方法 + override fun onSensorChanged(event: SensorEvent?) { + event?.let { + if (it.sensor.type == Sensor.TYPE_ACCELEROMETER) { + val currentTime = System.currentTimeMillis() + + // 防止过于频繁的检测 + if (currentTime - lastShakeTime < SHAKE_COOLDOWN) { + return + } + + val x = it.values[0] + val y = it.values[1] + val z = it.values[2] + + // 计算加速度的平方和 + val accelerationSquare = x * x + y * y + z * z + + // 计算总的加速度强度 + val acceleration = Math.sqrt(accelerationSquare.toDouble()).toFloat() + + // 检测是否达到摇晃阈值 + if (acceleration > shakeThreshold) { + lastShakeTime = currentTime + + // 提供震动反馈 + provideVibrationFeedback() + + // 根据摇晃强度计算心情值增量 + val increment = calculateMoodIncrement(acceleration) + updateMoodValue(increment) + } + } + } + } + + /** + * 提供震动反馈 + */ + private fun provideVibrationFeedback() { + try { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + // Android 8.0及以上使用VibrationEffect + val vibrationEffect = VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE) + vibrator.vibrate(vibrationEffect) + } else { + // 旧版本使用传统方法 + @Suppress("DEPRECATION") + vibrator.vibrate(100) + } + } catch (e: Exception) { + // 震动功能失败不影响核心功能 + Log.d(TAG, "震动反馈失败", e) + } + } + + /** + * 根据摇晃强度计算心情值增量 + */ + private fun calculateMoodIncrement(acceleration: Float): Int { + // 基础增量 + 根据摇晃强度的额外增量 + val baseIncrement = MOOD_INCREMENT_BASE + val bonusIncrement = ((acceleration - shakeThreshold) / 10).toInt().coerceAtLeast(0) + return baseIncrement + bonusIncrement + } + + override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) { + // 不需要处理 + } + + override fun onResume() { + super.onResume() + // 如果遮罩已经隐藏,重新开始传感器监听 + if (overlayState == OVERLAY_STATE_HIDDEN) { + startShakeDetection() + } + } + + override fun onPause() { + super.onPause() + stopShakeDetection() + handler.removeCallbacksAndMessages(null) + } + + override fun onDestroy() { + super.onDestroy() + stopShakeDetection() + handler.removeCallbacksAndMessages(null) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/daodaoshi/chick_mood/EditActivity.kt b/app/src/main/java/com/daodaoshi/chick_mood/EditActivity.kt index d0316e3..5f07b35 100644 --- a/app/src/main/java/com/daodaoshi/chick_mood/EditActivity.kt +++ b/app/src/main/java/com/daodaoshi/chick_mood/EditActivity.kt @@ -1,6 +1,7 @@ package com.daodaoshi.chick_mood import android.app.Activity +import android.content.Context import android.content.Intent import android.os.Bundle import android.text.Editable @@ -49,6 +50,12 @@ class EditActivity : AppCompatActivity() { private var currentImagePath: String? = null private val databaseManager = SimpleDatabaseManager.getInstance(this) + // 新增记录模式相关变量 + private var isAddMode = false + private var addModeEmotion: Emotion? = null + private var addModeMoodValue: Int = 0 + private var addModeTimestamp: Long = 0 + // 图片选择器 private val imagePickerLauncher = registerForActivityResult( androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult() @@ -64,6 +71,43 @@ class EditActivity : AppCompatActivity() { companion object { private const val MAX_TEXT_LENGTH = 500 private const val TAG = "EditActivity" + + // Intent extra keys + private const val EXTRA_RECORD_ID = "record_id" + private const val EXTRA_IS_ADD_MODE = "is_add_mode" + private const val EXTRA_EMOTION_TYPE = "emotion_type" + private const val EXTRA_MOOD_VALUE = "mood_value" + private const val EXTRA_TIMESTAMP = "timestamp" + + /** + * 启动编辑页面(编辑模式) + * @param context 上下文 + * @param recordId 要编辑的记录ID + */ + fun start(context: Context, recordId: Long) { + val intent = Intent(context, EditActivity::class.java).apply { + putExtra(EXTRA_RECORD_ID, recordId) + putExtra(EXTRA_IS_ADD_MODE, false) + } + context.startActivity(intent) + } + + /** + * 启动编辑页面(新增模式) + * @param context 上下文 + * @param emotion 心情类型 + * @param moodValue 心情值 + * @param timestamp 时间戳 + */ + fun startForAdd(context: Context, emotion: Emotion, moodValue: Int, timestamp: Long) { + val intent = Intent(context, EditActivity::class.java).apply { + putExtra(EXTRA_IS_ADD_MODE, true) + putExtra(EXTRA_EMOTION_TYPE, emotion.name) + putExtra(EXTRA_MOOD_VALUE, moodValue) + putExtra(EXTRA_TIMESTAMP, timestamp) + } + context.startActivity(intent) + } } override fun onCreate(savedInstanceState: Bundle?) { @@ -119,13 +163,53 @@ class EditActivity : AppCompatActivity() { * 加载心情记录数据 */ private fun loadData() { - val recordId = intent.getLongExtra("record_id", -1L) - if (recordId == -1L) { - finish() - return - } + // 检查是否为新增模式 + isAddMode = intent.getBooleanExtra(EXTRA_IS_ADD_MODE, false) - loadRecordFromDatabase(recordId) + if (isAddMode) { + // 新增模式 + loadAddModeData() + } else { + // 编辑模式 + val recordId = intent.getLongExtra("record_id", -1L) + if (recordId == -1L) { + finish() + return + } + loadRecordFromDatabase(recordId) + } + } + + /** + * 加载新增模式的数据 + */ + private fun loadAddModeData() { + try { + addModeEmotion = intent.getStringExtra(EXTRA_EMOTION_TYPE)?.let { + Emotion.valueOf(it) + } ?: Emotion.HAPPY + addModeMoodValue = intent.getIntExtra(EXTRA_MOOD_VALUE, 0) + addModeTimestamp = intent.getLongExtra(EXTRA_TIMESTAMP, System.currentTimeMillis()) + + // 创建临时记录用于显示 + currentRecord = MoodRecord( + id = 0, // 临时ID,保存时会重新分配 + emotion = addModeEmotion!!, + moodIntensity = addModeMoodValue, + timestamp = addModeTimestamp, + textContent = null, + imagePath = null, + isFavorite = false + ) + + // 更新UI + displayRecord(currentRecord!!) + Log.d(TAG, "Add mode data loaded successfully") + } catch (e: Exception) { + Log.e(TAG, "Error loading add mode data", e) + showToast("加载新增模式数据失败") + finish() + } } /** @@ -354,26 +438,60 @@ class EditActivity : AppCompatActivity() { CoroutineScope(Dispatchers.IO).launch { try { - val updatedRecord = record.copy( - textContent = if (updatedText.isEmpty()) null else updatedText, - imagePath = currentImagePath, - timestamp = record.timestamp // 保持原始时间戳不变 - ) + if (isAddMode) { + // 新增模式:创建新记录 + val newRecord = MoodRecord( + id = 0, // 数据库自动生成ID + emotion = record.emotion, + moodIntensity = record.moodIntensity, + timestamp = record.timestamp, + textContent = if (updatedText.isEmpty()) null else updatedText, + imagePath = currentImagePath, + isFavorite = false + ) - databaseManager.updateMoodRecord(updatedRecord) + val newId = databaseManager.createMoodRecord( + emotion = newRecord.emotion, + intensity = newRecord.moodIntensity, + noteText = newRecord.textContent ?: "", + imagePaths = if (newRecord.imagePath != null) listOf(newRecord.imagePath) else emptyList() + ) - withContext(Dispatchers.Main) { - showToast("保存成功") + withContext(Dispatchers.Main) { + showToast("保存成功") - // 直接跳转到首页并传递编辑的记录ID用于定位 - val homeIntent = Intent(this@EditActivity, MainActivitySimple::class.java).apply { - putExtra("edited_record_id", record.id) - flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK + // 跳转到首页并传递新增记录ID用于定位 + val homeIntent = Intent(this@EditActivity, MainActivitySimple::class.java).apply { + putExtra("new_record_id", newId) + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) + } + startActivity(homeIntent) + finish() + } + } else { + // 编辑模式:更新现有记录 + val updatedRecord = record.copy( + textContent = if (updatedText.isEmpty()) null else updatedText, + imagePath = currentImagePath, + timestamp = record.timestamp // 保持原始时间戳不变 + ) + + databaseManager.updateMoodRecord(updatedRecord) + + withContext(Dispatchers.Main) { + showToast("保存成功") + + // 跳转到首页并传递编辑的记录ID用于定位 + val homeIntent = Intent(this@EditActivity, MainActivitySimple::class.java).apply { + putExtra("edited_record_id", record.id) + flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK + } + startActivity(homeIntent) + finish() } - startActivity(homeIntent) - finish() } } catch (e: Exception) { + Log.e(TAG, "Error saving record", e) withContext(Dispatchers.Main) { showToast("保存失败") } diff --git a/app/src/main/java/com/daodaoshi/chick_mood/MainActivitySimple.kt b/app/src/main/java/com/daodaoshi/chick_mood/MainActivitySimple.kt index a922f7e..a1b2536 100644 --- a/app/src/main/java/com/daodaoshi/chick_mood/MainActivitySimple.kt +++ b/app/src/main/java/com/daodaoshi/chick_mood/MainActivitySimple.kt @@ -154,12 +154,10 @@ class MainActivitySimple : AppCompatActivity() { Emotion.SCARED -> "害怕" } - showTemporaryMessage("你选择了:$emotionName") - - // 创建一条测试记录来验证功能 - createMoodRecordWithEmotion(emotion) - Log.d(TAG, "用户选择了情绪:$emotionName") + + // 跳转到添加心情页面 + AddActivity.start(this, emotion) } /** @@ -652,6 +650,12 @@ class MainActivitySimple : AppCompatActivity() { Log.d(TAG, "检测到编辑完成的记录ID: $editedRecordId") recordIdToLocate = editedRecordId } + + val newRecordId = intent.getLongExtra("new_record_id", -1L) + if (newRecordId != -1L) { + Log.d(TAG, "检测到新增完成的记录ID: $newRecordId") + recordIdToLocate = newRecordId + } } /** diff --git a/app/src/main/res/drawable/button_primary.xml b/app/src/main/res/drawable/button_primary.xml new file mode 100644 index 0000000..3823f0c --- /dev/null +++ b/app/src/main/res/drawable/button_primary.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/progress_bar_mood.xml b/app/src/main/res/drawable/progress_bar_mood.xml new file mode 100644 index 0000000..6a18abf --- /dev/null +++ b/app/src/main/res/drawable/progress_bar_mood.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_add.xml b/app/src/main/res/layout/activity_add.xml new file mode 100644 index 0000000..a4cc8e5 --- /dev/null +++ b/app/src/main/res/layout/activity_add.xml @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +