优化列表更新和红点显示

This commit is contained in:
squallzhjch 2023-01-09 16:29:58 +08:00
parent 13f3516566
commit 7b5b4b08e5
8 changed files with 118 additions and 58 deletions

View File

@ -9,6 +9,8 @@ class Constant {
*/ */
const val SERVER_ADDRESS = "http://ec2-52-81-73-5.cn-north-1.compute.amazonaws.com.cn:8088/" const val SERVER_ADDRESS = "http://ec2-52-81-73-5.cn-north-1.compute.amazonaws.com.cn:8088/"
val DEBUG = Boolean.parseBoolean("true") val DEBUG = Boolean.parseBoolean("true")
const val message_status_late = "预约,待发送"
} }
} }

View File

@ -1,8 +1,39 @@
package com.navinfo.volvo package com.navinfo.volvo
import android.app.Activity
import android.app.Application import android.app.Application
import android.content.pm.ActivityInfo
import android.os.Bundle
import dagger.hilt.android.HiltAndroidApp import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp @HiltAndroidApp
open class MyApplication : Application() { open class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}
override fun onActivityStarted(activity: Activity) {
}
override fun onActivityResumed(activity: Activity) {
}
override fun onActivityPaused(activity: Activity) {
}
override fun onActivityStopped(activity: Activity) {
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
}
override fun onActivityDestroyed(activity: Activity) {
}
})
}
} }

View File

@ -1,7 +1,9 @@
package com.navinfo.volvo.database.dao package com.navinfo.volvo.database.dao
import android.util.Log
import androidx.paging.PagingSource import androidx.paging.PagingSource
import androidx.room.* import androidx.room.*
import com.navinfo.volvo.Constant
import com.navinfo.volvo.database.entity.GreetingMessage import com.navinfo.volvo.database.entity.GreetingMessage
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -20,7 +22,7 @@ interface GreetingMessageDao {
/** /**
* 未读消息统计 * 未读消息统计
*/ */
@Query("SELECT count(id) FROM GreetingMessage WHERE read = 0") @Query("SELECT count(id) FROM GreetingMessage WHERE status = '${Constant.message_status_late}'")
fun countUnreadByFlow(): Flow<Long> fun countUnreadByFlow(): Flow<Long>
/** /**
@ -32,8 +34,8 @@ interface GreetingMessageDao {
/** /**
* 检查某条数据是否存在 * 检查某条数据是否存在
*/ */
@Query("SELECT id From GreetingMessage WHERE id = :id LIMIT 1") @Query("SELECT uuid From GreetingMessage WHERE id = :id LIMIT 1")
suspend fun getMessageId(id: Long): Long suspend fun getMessageId(id: Long): Long?
/** /**
* *
@ -41,12 +43,18 @@ interface GreetingMessageDao {
@Transaction @Transaction
suspend fun insertOrUpdate(list: List<GreetingMessage>) { suspend fun insertOrUpdate(list: List<GreetingMessage>) {
for (message in list) { for (message in list) {
val id = getMessageId(message.id) Log.e("jingo", "insertOrUpdate ${message.id}")
if (id == 0L) { val uuid = getMessageId(message.id)
insert(message) Log.e("jingo", "insertOrUpdate $uuid")
if (uuid == null || uuid == 0L) {
Log.e("jingo", "insertOrUpdate start ")
val l = insert(message)
Log.e("jingo", "insertOrUpdate $l ")
} else { } else {
message.uuid = uuid
update(message) update(message)
} }
Log.e("jingo", "insertOrUpdate end")
} }
} }

View File

@ -57,7 +57,7 @@ class HomeAdapter(fragment: Fragment) :
class DiffCallback : DiffUtil.ItemCallback<GreetingMessage>() { class DiffCallback : DiffUtil.ItemCallback<GreetingMessage>() {
override fun areItemsTheSame(oldItem: GreetingMessage, newItem: GreetingMessage): Boolean { override fun areItemsTheSame(oldItem: GreetingMessage, newItem: GreetingMessage): Boolean {
return oldItem.uuid == newItem.uuid return oldItem.uuid == newItem.uuid && oldItem.status == newItem.status
} }
override fun areContentsTheSame( override fun areContentsTheSame(

View File

@ -12,6 +12,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.paging.LoadState import androidx.paging.LoadState
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.easytools.tools.ThreadPoolUtils.runOnUiThread
import com.navinfo.volvo.R import com.navinfo.volvo.R
import com.navinfo.volvo.databinding.FragmentHomeBinding import com.navinfo.volvo.databinding.FragmentHomeBinding
import com.navinfo.volvo.databinding.HomeAdapterNotingBinding import com.navinfo.volvo.databinding.HomeAdapterNotingBinding
@ -21,6 +22,7 @@ import com.navinfo.volvo.ui.fragments.BaseFragment
import com.yanzhenjie.recyclerview.* import com.yanzhenjie.recyclerview.*
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.IOException import java.io.IOException
@AndroidEntryPoint @AndroidEntryPoint
@ -55,19 +57,19 @@ class HomeFragment : BaseFragment(), OnItemClickListener, OnItemMenuClickListene
var mSwipeMenuCreator = SwipeMenuCreator { _, rightMenu, _ -> var mSwipeMenuCreator = SwipeMenuCreator { _, rightMenu, _ ->
//添加菜单自动添加至尾部 //添加菜单自动添加至尾部
var deleteItem = SwipeMenuItem(context) var deleteItem = SwipeMenuItem(context)
deleteItem.height = DisplayUtil.dip2px(context!!, 60f) deleteItem.height = DisplayUtil.dip2px(requireContext(), 60f)
deleteItem.width = DisplayUtil.dip2px(context!!, 80f) deleteItem.width = DisplayUtil.dip2px(requireContext(), 80f)
deleteItem.background = context!!.getDrawable(R.color.red) deleteItem.background = requireContext().getDrawable(R.color.red)
deleteItem.text = context!!.getString(R.string.delete) deleteItem.text = requireContext().getString(R.string.delete)
rightMenu.addMenuItem(deleteItem) rightMenu.addMenuItem(deleteItem)
//分享 //分享
var shareItem = SwipeMenuItem(context) var shareItem = SwipeMenuItem(context)
shareItem.height = DisplayUtil.dip2px(context!!, 60f) shareItem.height = DisplayUtil.dip2px(requireContext(), 60f)
shareItem.width = DisplayUtil.dip2px(context!!, 80f) shareItem.width = DisplayUtil.dip2px(requireContext(), 80f)
shareItem.background = context!!.getDrawable(R.color.gray) shareItem.background = requireContext().getDrawable(R.color.gray)
shareItem.text = context!!.getString(R.string.share) shareItem.text = requireContext().getString(R.string.share)
shareItem.setTextColor(R.color.white) shareItem.setTextColor(requireContext().getColor(R.color.white))
rightMenu.addMenuItem(shareItem) rightMenu.addMenuItem(shareItem)
} }
//侧滑按钮 //侧滑按钮
@ -123,11 +125,14 @@ class HomeFragment : BaseFragment(), OnItemClickListener, OnItemMenuClickListene
messageAdapter.addLoadStateListener { messageAdapter.addLoadStateListener {
when (it.refresh) { when (it.refresh) {
is LoadState.NotLoading -> { is LoadState.NotLoading -> {
if (messageAdapter.itemCount == 0)
binding.homeRecyclerview.addHeaderView(headBinding!!.root) binding.homeRecyclerview.addHeaderView(headBinding!!.root)
else{
binding.homeRecyclerview.removeHeaderView(headBinding!!.root)
}
Log.d("jingo", "is NotLoading") Log.d("jingo", "is NotLoading")
} }
is LoadState.Loading -> { is LoadState.Loading -> {
binding.homeRecyclerview.removeHeaderView(headBinding!!.root)
Log.d("jingo", "is Loading") Log.d("jingo", "is Loading")
} }
is LoadState.Error -> { is LoadState.Error -> {

View File

@ -7,7 +7,8 @@ import android.text.TextUtils
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.View.* import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.AdapterView import android.widget.AdapterView
import android.widget.AdapterView.OnItemSelectedListener import android.widget.AdapterView.OnItemSelectedListener
@ -18,17 +19,9 @@ import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.Navigation import androidx.navigation.Navigation
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.easytools.tools.DateUtils import com.easytools.tools.*
import com.easytools.tools.DisplayUtils
import com.easytools.tools.FileIOUtils
import com.easytools.tools.ResourceUtils
import com.easytools.tools.ThreadPoolUtils.runOnUiThread
import com.easytools.tools.ToastUtils
import com.elvishew.xlog.XLog import com.elvishew.xlog.XLog
import com.github.file_picker.FileType import com.github.file_picker.FileType
import com.github.file_picker.ListDirection import com.github.file_picker.ListDirection
@ -58,7 +51,6 @@ import com.nhaarman.supertooltips.ToolTip
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import indi.liyi.viewer.Utils import indi.liyi.viewer.Utils
import indi.liyi.viewer.ViewData import indi.liyi.viewer.ViewData
import kotlinx.coroutines.launch
import top.zibin.luban.Luban import top.zibin.luban.Luban
import top.zibin.luban.OnCompressListener import top.zibin.luban.OnCompressListener
import java.io.File import java.io.File
@ -196,8 +188,11 @@ class ObtainMessageFragment : Fragment() {
} }
val sendToArray = mutableListOf<VolvoModel>(VolvoModel("XC60", "智雅", "LYVXFEFEXNL754427")) val sendToArray = mutableListOf<VolvoModel>(VolvoModel("XC60", "智雅", "LYVXFEFEXNL754427"))
binding.edtSendTo.adapter = ArrayAdapter<String>(requireContext(), binding.edtSendTo.adapter = ArrayAdapter<String>(requireContext(),
android.R.layout.simple_dropdown_item_1line, android.R.id.text1, sendToArray.stream().map { it -> "${it.version} ${it.model} ${it.num}" }.toList()) android.R.layout.simple_dropdown_item_1line,
binding.edtSendTo.onItemSelectedListener = object: OnItemSelectedListener { android.R.id.text1,
sendToArray.stream().map { it -> "${it.version} ${it.model} ${it.num}" }.toList()
)
binding.edtSendTo.onItemSelectedListener = object : OnItemSelectedListener {
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) { override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
obtainMessageViewModel.getMessageLiveData().value?.toWho = sendToArray[p2].num obtainMessageViewModel.getMessageLiveData().value?.toWho = sendToArray[p2].num
} }
@ -287,13 +282,23 @@ class ObtainMessageFragment : Fragment() {
// Do something here with selected files // Do something here with selected files
val audioFile = files.get(0).file val audioFile = files.get(0).file
if (!audioFile.parentFile.parentFile.absolutePath.equals(SystemConstant.SoundFolder)) { if (!audioFile.parentFile.parentFile.absolutePath.equals(SystemConstant.SoundFolder)) {
val copyResult = FileIOUtils.writeFileFromIS(File(SystemConstant.SoundFolder, audioFile.name), FileInputStream(audioFile)) val copyResult = FileIOUtils.writeFileFromIS(
XLog.e("拷贝结果:"+copyResult) File(
SystemConstant.SoundFolder,
audioFile.name
), FileInputStream(audioFile)
)
XLog.e("拷贝结果:" + copyResult)
if (!copyResult) { if (!copyResult) {
ToastUtils.showToast("无法访问该文件,请重新选择其他文件") ToastUtils.showToast("无法访问该文件,请重新选择其他文件")
return return
} }
obtainMessageViewModel.updateMessageAudio(File(SystemConstant.SoundFolder, audioFile.name).absolutePath) obtainMessageViewModel.updateMessageAudio(
File(
SystemConstant.SoundFolder,
audioFile.name
).absolutePath
)
} else { } else {
obtainMessageViewModel.updateMessageAudio(audioFile.absolutePath) obtainMessageViewModel.updateMessageAudio(audioFile.absolutePath)
} }
@ -306,7 +311,7 @@ class ObtainMessageFragment : Fragment() {
ToastUtils.showToast("只能选择.m4a文件") ToastUtils.showToast("只能选择.m4a文件")
return return
} }
if (media.file.length()>2*1000*1000) { if (media.file.length() > 2 * 1000 * 1000) {
ToastUtils.showToast("文件不能超过2M") ToastUtils.showToast("文件不能超过2M")
return return
} }
@ -349,12 +354,13 @@ class ObtainMessageFragment : Fragment() {
false false
} }
MotionEvent.ACTION_UP -> { MotionEvent.ACTION_UP -> {
if (System.currentTimeMillis() - startRecordTime<2000) { if (System.currentTimeMillis() - startRecordTime < 2000) {
ToastUtils.showToast("录音时间太短!") ToastUtils.showToast("录音时间太短!")
recorderLifecycleObserver.stopAndReleaseRecorder() recorderLifecycleObserver.stopAndReleaseRecorder()
return return
} }
val recorderAudioPath = recorderLifecycleObserver.stopAndReleaseRecorder() val recorderAudioPath =
recorderLifecycleObserver.stopAndReleaseRecorder()
if (File(recorderAudioPath).exists()) { if (File(recorderAudioPath).exists()) {
obtainMessageViewModel.updateMessageAudio(recorderAudioPath) obtainMessageViewModel.updateMessageAudio(recorderAudioPath)
} }
@ -408,25 +414,29 @@ class ObtainMessageFragment : Fragment() {
} }
// 如果当前文件不在camera缓存文件夹下则移动该文件 // 如果当前文件不在camera缓存文件夹下则移动该文件
if (!file!!.parentFile.absolutePath.equals(SystemConstant.CameraFolder)) { if (!file!!.parentFile.absolutePath.equals(SystemConstant.CameraFolder)) {
try {
val copyResult = FileIOUtils.writeFileFromIS( val copyResult = FileIOUtils.writeFileFromIS(
File( File(
SystemConstant.CameraFolder, SystemConstant.CameraFolder,
fileName fileName
), FileInputStream(file) ), FileInputStream(file)
) )
XLog.e("拷贝结果:" + copyResult) XLog.e("拷贝结果:$copyResult")
// 跳转回原Fragment展示拍摄的照片 // 跳转回原Fragment展示拍摄的照片
ViewModelProvider(requireActivity()).get(ObtainMessageViewModel::class.java) obtainMessageViewModel
.updateMessagePic( .updateMessagePic(
File( File(
SystemConstant.CameraFolder, SystemConstant.CameraFolder,
fileName fileName
).absolutePath ).absolutePath
) )
} catch (e: Exception) {
XLog.e("崩溃:${e.message}")
}
} else { } else {
// 跳转回原Fragment展示拍摄的照片 // 跳转回原Fragment展示拍摄的照片
ViewModelProvider(requireActivity()).get(ObtainMessageViewModel::class.java) obtainMessageViewModel.updateMessagePic(file!!.absolutePath)
.updateMessagePic(file!!.absolutePath)
} }
} }
@ -612,7 +622,7 @@ class ObtainMessageFragment : Fragment() {
val sendDate = DateUtils.str2Date(messageData?.sendDate, dateSendFormat) val sendDate = DateUtils.str2Date(messageData?.sendDate, dateSendFormat)
val cal = Calendar.getInstance() val cal = Calendar.getInstance()
cal.time = Date() cal.time = Date()
cal.set(Calendar.MINUTE, cal.get(Calendar.MINUTE)+1) cal.set(Calendar.MINUTE, cal.get(Calendar.MINUTE) + 1)
if (sendDate.time < cal.time.time) { // 发送时间设置小于当前时间1分钟后Toast提示用户并自动设置发送时间 if (sendDate.time < cal.time.time) { // 发送时间设置小于当前时间1分钟后Toast提示用户并自动设置发送时间
messageData?.sendDate = DateUtils.date2Str(cal.time, dateSendFormat) messageData?.sendDate = DateUtils.date2Str(cal.time, dateSendFormat)
ToastUtils.showToast("自动调整发送时间为1分钟后发送") ToastUtils.showToast("自动调整发送时间为1分钟后发送")
@ -692,6 +702,7 @@ class ObtainMessageFragment : Fragment() {
}) })
.show() .show()
} }
fun onRecorderDenied() { fun onRecorderDenied() {
ToastUtils.showToast("当前操作需要您授权录音权限!") ToastUtils.showToast("当前操作需要您授权录音权限!")
} }

View File

@ -27,13 +27,13 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class ObtainMessageViewModel @Inject constructor( class ObtainMessageViewModel @Inject constructor(
private val pre: PreferencesRepository private val pre: PreferencesRepository,
) : ViewModel() { ) : ViewModel() {
private val msgLiveData: MutableLiveData<GreetingMessage> by lazy { private val msgLiveData: MutableLiveData<GreetingMessage> by lazy {
MutableLiveData<GreetingMessage>() MutableLiveData<GreetingMessage>()
} }
lateinit var username: String var username: String = ""
init { init {
viewModelScope.launch { viewModelScope.launch {

View File

@ -1,12 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:tools="http://schemas.android.com/tools">
<data> <data>
<import type="android.view.View" /> <import type="android.view.View" />
<import type="com.navinfo.volvo.Constant" />
<variable <variable
name="greetingMessage" name="greetingMessage"
type="com.navinfo.volvo.database.entity.GreetingMessage" /> type="com.navinfo.volvo.database.entity.GreetingMessage" />
@ -24,10 +26,10 @@
android:layout_marginLeft="8dp" android:layout_marginLeft="8dp"
android:scaleType="fitXY" android:scaleType="fitXY"
android:src="@mipmap/ic_launcher" android:src="@mipmap/ic_launcher"
app:roundPercent="0.4"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent"
app:roundPercent="0.4" />
<TextView <TextView
android:id="@+id/message_badge" android:id="@+id/message_badge"
@ -36,6 +38,7 @@
android:background="@drawable/shape_circular" android:background="@drawable/shape_circular"
android:gravity="center" android:gravity="center"
android:textColor="#000000" android:textColor="#000000"
android:visibility="@{Constant.message_status_late.equals(greetingMessage.status)?View.VISIBLE:View.INVISIBLE}"
app:layout_constraintCircle="@id/message_head_icon" app:layout_constraintCircle="@id/message_head_icon"
app:layout_constraintCircleAngle="45" app:layout_constraintCircleAngle="45"
app:layout_constraintCircleRadius="40dp" app:layout_constraintCircleRadius="40dp"