Skip to content

Commit

Permalink
Merge b99a12f into 6159153
Browse files Browse the repository at this point in the history
  • Loading branch information
Steve-Mr authored Jun 1, 2023
2 parents 6159153 + b99a12f commit cd260e4
Show file tree
Hide file tree
Showing 14 changed files with 340 additions and 33 deletions.
69 changes: 53 additions & 16 deletions app/src/main/java/com/maary/liveinpeace/ConnectionListAdapter.kt
Original file line number Diff line number Diff line change
@@ -1,58 +1,88 @@
package com.maary.liveinpeace

import android.annotation.SuppressLint
import android.content.Context
import android.content.res.ColorStateList
import android.media.AudioDeviceInfo
import android.media.Image
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.maary.liveinpeace.database.Connection
import java.util.concurrent.TimeUnit
import kotlin.coroutines.coroutineContext

class ConnectionListAdapter : ListAdapter<Connection, ConnectionListAdapter.ConnectionViewHolder>(ConnectionsComparator()){

private var currentItemLayout = R.layout.item_connection

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ConnectionViewHolder {
return ConnectionViewHolder.create(parent)
return ConnectionViewHolder.create(parent, currentItemLayout)
}

override fun onBindViewHolder(holder: ConnectionViewHolder, position: Int) {
val current = getItem(position)
holder.bind( current.name, current.type, current.duration)
holder.bind( current.name, current.type, current.duration, current.disconnectedTime)
}

class ConnectionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val connectionIconView: ImageView = itemView.findViewById(R.id.device_icon)
private val connectionDeviceNameView: TextView = itemView.findViewById(R.id.device_name)
private val connectionDurationView: TextView = itemView.findViewById(R.id.device_connection_time)
private val connectionIndicatorView: ImageView = itemView.findViewById(R.id.connection_time_prefix)

fun bind(deviceName: String?, type: Int?,duration: Long?) {
if (type in listOf(
AudioDeviceInfo.TYPE_BLE_BROADCAST,
AudioDeviceInfo.TYPE_BLE_HEADSET,
AudioDeviceInfo.TYPE_BLE_SPEAKER,
AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
AudioDeviceInfo.TYPE_BLUETOOTH_SCO
)){
connectionIconView.setImageResource(R.drawable.ic_bluetooth_round)
} else {
connectionIconView.setImageResource(R.drawable.ic_headphone_round)
}
fun bind(deviceName: String?, type: Int?, duration: Long?, disconnectedTime: Long?) {
connectionIconView.setImageResource(
chooseDeviceDrawable(
type = type,
drawableHeadphone = R.drawable.ic_headphone_round,
drawableBLE = R.drawable.ic_bluetooth_round))
connectionDeviceNameView.text = deviceName
connectionDurationView.text = duration?.let { formatMilliseconds(itemView.context, it) }

if (disconnectedTime == null){
// Get the desired tint color from resources
val tintColor = itemView.context.getColor(android.R.color.holo_green_light)

// Create a ColorStateList with the desired tint color
val colorStateList = ColorStateList.valueOf(tintColor)
connectionIndicatorView.imageTintList = colorStateList
connectionIconView.setImageResource(
chooseDeviceDrawable(
type = type,
drawableHeadphone = R.drawable.ic_headphone_round_alt,
drawableBLE = R.drawable.ic_bluetooth_round_alt))
}
}

companion object {
fun create(parent: ViewGroup): ConnectionViewHolder {
fun create(parent: ViewGroup, layoutId: Int): ConnectionViewHolder {
val view: View = LayoutInflater.from(parent.context)
.inflate(R.layout.item_connection, parent, false)
.inflate(layoutId, parent, false)
return ConnectionViewHolder(view)
}
}

private fun chooseDeviceDrawable(type: Int?, drawableHeadphone: Int, drawableBLE: Int): Int{
return if (type in listOf(
AudioDeviceInfo.TYPE_BLE_BROADCAST,
AudioDeviceInfo.TYPE_BLE_HEADSET,
AudioDeviceInfo.TYPE_BLE_SPEAKER,
AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
AudioDeviceInfo.TYPE_BLUETOOTH_SCO
)){
(drawableBLE)
} else {
(drawableHeadphone)
}
}

private fun formatMilliseconds(context: Context, milliseconds: Long): String {
val hours = TimeUnit.MILLISECONDS.toHours(milliseconds)
val minutes = TimeUnit.MILLISECONDS.toMinutes(milliseconds) % 60
Expand All @@ -64,6 +94,13 @@ class ConnectionListAdapter : ListAdapter<Connection, ConnectionListAdapter.Conn
}
}

@SuppressLint("NotifyDataSetChanged")
fun updateItemLayout(newItemLayout: Int) {
currentItemLayout = newItemLayout
notifyDataSetChanged()
}


class ConnectionsComparator : DiffUtil.ItemCallback<Connection>(){
override fun areItemsTheSame(oldItem: Connection, newItem: Connection): Boolean {
return oldItem == newItem
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.maary.liveinpeace

import com.maary.liveinpeace.database.Connection

interface DeviceMapChangeListener {
fun onDeviceMapChanged(deviceMap: Map<String, Connection>)
}
68 changes: 67 additions & 1 deletion app/src/main/java/com/maary/liveinpeace/HistoryActivity.kt
Original file line number Diff line number Diff line change
@@ -1,21 +1,55 @@
package com.maary.liveinpeace

import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.LinearLayoutManager
import com.maary.liveinpeace.database.Connection
import com.maary.liveinpeace.databinding.ActivityHistoryBinding
import com.maary.liveinpeace.service.ForegroundService
import java.time.LocalDate


class HistoryActivity : AppCompatActivity() {
class HistoryActivity : AppCompatActivity(), DeviceMapChangeListener {

private lateinit var binding: ActivityHistoryBinding
private val connectionViewModel: ConnectionViewModel by viewModels {
ConnectionViewModelFactory((application as ConnectionsApplication).repository)
}
private val currentAdapter = ConnectionListAdapter()

override fun onResume() {
super.onResume()
ForegroundService.addDeviceMapChangeListener(this)
}

override fun onPause() {
super.onPause()
ForegroundService.removeDeviceMapChangeListener(this)
}

private fun currentConnectionsDuration(currentList: MutableList<Connection>) : MutableList<Connection>{
val now = System.currentTimeMillis()

for ( (index, connection) in currentList.withIndex()){
val connectedTime = connection.connectedTime
val duration = now - connectedTime!!
currentList[index] = Connection(
name = connection.name,
type = connection.type,
connectedTime = connection.connectedTime,
disconnectedTime = null,
duration = duration,
date = connection.date
)
}

return currentList
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand All @@ -24,6 +58,7 @@ class HistoryActivity : AppCompatActivity() {
setContentView(binding.root)

val connectionAdapter = ConnectionListAdapter()
binding.historyList.isNestedScrollingEnabled = false
binding.historyList.adapter = connectionAdapter
binding.historyList.layoutManager = LinearLayoutManager(this)

Expand All @@ -33,6 +68,12 @@ class HistoryActivity : AppCompatActivity() {
finish()
}

binding.currentList.isNestedScrollingEnabled = false
binding.currentList.adapter = currentAdapter
binding.currentList.layoutManager = LinearLayoutManager(this)

updateCurrentAdapter()

connectionViewModel.getAllConnectionsOnDate(LocalDate.now().toString()).observe(this) { connections ->
connections.let { connectionAdapter.submitList(it) }
}
Expand All @@ -43,15 +84,40 @@ class HistoryActivity : AppCompatActivity() {
connectionViewModel.getAllConnectionsOnDate(LocalDate.now().toString()).observe(this) { connections ->
connections.let { connectionAdapter.submitList(it) }
}
updateCurrentAdapter()
}
if (checkedId == R.id.button_summary) {
connectionViewModel.getSummaryOnDate(LocalDate.now().toString()).observe(this) { connections ->
connections.let { connectionAdapter.submitList(it) }

}
updateCurrentAdapter()
}
}


}

private fun updateCurrentAdapter(){
currentAdapter.submitList(currentConnectionsDuration(ForegroundService.getConnections()))
if (currentAdapter.itemCount == 0){
Log.v("MUTE_", "GG")
binding.titleCurrent.visibility = View.GONE
}else{
binding.titleCurrent.visibility = View.VISIBLE
}
}

override fun onDeviceMapChanged(deviceMap: Map<String, Connection>) {
if (deviceMap.isEmpty()){
Log.v("MUTE_", "GGA")

binding.titleCurrent.visibility = View.GONE
}else{
Log.v("MUTE_", "GGB")

binding.titleCurrent.visibility = View.VISIBLE
}
currentAdapter.submitList(currentConnectionsDuration(deviceMap.values.toMutableList()))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import com.maary.liveinpeace.Constants.Companion.MODE_NUM
import com.maary.liveinpeace.Constants.Companion.PREF_ICON
import com.maary.liveinpeace.Constants.Companion.PREF_WATCHING_CONNECTING_TIME
import com.maary.liveinpeace.Constants.Companion.SHARED_PREF
import com.maary.liveinpeace.DeviceMapChangeListener
import com.maary.liveinpeace.HistoryActivity
import com.maary.liveinpeace.R
import com.maary.liveinpeace.database.Connection
Expand All @@ -58,7 +59,7 @@ class ForegroundService: Service() {
private lateinit var connectionDao: ConnectionDao

private val deviceTimerMap: MutableMap<String, DeviceTimer> = mutableMapOf()
private val deviceMap: MutableMap<String, Connection> = mutableMapOf()
// private val deviceMap: MutableMap<String, Connection> = mutableMapOf()

private val volumeDrawableIds = intArrayOf(
R.drawable.ic_volume_silent,
Expand All @@ -81,6 +82,29 @@ class ForegroundService: Service() {
fun isForegroundServiceRunning(): Boolean {
return isForegroundServiceRunning
}

private val deviceMap: MutableMap<String, Connection> = mutableMapOf()

// 在伴生对象中定义一个静态方法,用于其他类访问deviceMap
fun getConnections(): MutableList<Connection> {
return deviceMap.values.toMutableList()
}

private val deviceMapChangeListeners: MutableList<DeviceMapChangeListener> = mutableListOf()

fun addDeviceMapChangeListener(listener: DeviceMapChangeListener) {
deviceMapChangeListeners.add(listener)
}

fun removeDeviceMapChangeListener(listener: DeviceMapChangeListener) {
deviceMapChangeListeners.remove(listener)
}
}

private fun notifyDeviceMapChange() {
deviceMapChangeListeners.forEach { listener ->
listener.onDeviceMapChanged(deviceMap)
}
}

private fun getVolumePercentage(context: Context): Int {
Expand Down Expand Up @@ -126,6 +150,31 @@ class ForegroundService: Service() {
}
}

private fun saveDateWhenStop(){
val disconnectedTime = System.currentTimeMillis()

for ( (deviceName, connection) in deviceMap){

val connectedTime = connection.connectedTime
val connectionTime = disconnectedTime - connectedTime!!

CoroutineScope(Dispatchers.IO).launch {
connectionDao.insert(
Connection(
name = connection.name,
type = connection.type,
connectedTime = connection.connectedTime,
disconnectedTime = disconnectedTime,
duration = connectionTime,
date = connection.date
)
)
}
deviceMap.remove(deviceName)
}
return
}

private val audioDeviceCallback = object : AudioDeviceCallback() {
@SuppressLint("MissingPermission")
override fun onAudioDevicesAdded(addedDevices: Array<out AudioDeviceInfo>?) {
Expand Down Expand Up @@ -158,6 +207,7 @@ class ForegroundService: Service() {
duration = null,
date = LocalDate.now().toString()
)
notifyDeviceMapChange()
// 执行其他逻辑,比如将设备信息保存到数据库或日志中
}

Expand Down Expand Up @@ -216,6 +266,7 @@ class ForegroundService: Service() {
}

deviceMap.remove(deviceName)
notifyDeviceMapChange()
}

val sharedPreferences = getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE)
Expand Down Expand Up @@ -254,12 +305,12 @@ class ForegroundService: Service() {
}

override fun onDestroy() {
super.onDestroy()

saveDateWhenStop()
// 取消注册音量变化广播接收器
unregisterReceiver(volumeChangeReceiver)
audioManager.unregisterAudioDeviceCallback(audioDeviceCallback)
isForegroundServiceRunning = false
super.onDestroy()
}

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/res/drawable/ic_bluetooth_alt.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="?attr/colorSecondaryVariant"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M17,7l-4.29,-4.29c-0.63,-0.63 -1.71,-0.19 -1.71,0.7v6.18L7.11,5.7c-0.39,-0.39 -1.02,-0.39 -1.41,0 -0.39,0.39 -0.39,1.02 0,1.41L10.59,12 5.7,16.89c-0.39,0.39 -0.39,1.02 0,1.41 0.39,0.39 1.02,0.39 1.41,0L11,14.41v6.18c0,0.89 1.08,1.34 1.71,0.71L17,17c0.39,-0.39 0.39,-1.02 0,-1.41L13.41,12 17,8.42c0.39,-0.39 0.39,-1.03 0,-1.42zM13,5.83l1.88,1.88L13,9.59L13,5.83zM14.88,16.29L13,18.17v-3.76l1.88,1.88z"/>
</vector>
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/ic_bluetooth_round_alt.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="@drawable/shape_circle_alt"/>
<item android:drawable="@drawable/ic_bluetooth_alt"
android:left="8dp"
android:right="8dp"
android:top="8dp"
android:bottom="8dp"/>
</layer-list>
5 changes: 5 additions & 0 deletions app/src/main/res/drawable/ic_headphone_alt.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="?attr/colorSecondaryVariant"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="?android:attr/textColorPrimary" android:pathData="M12,3c-4.97,0 -9,4.03 -9,9v7c0,1.1 0.9,2 2,2h4v-8H5v-1c0,-3.87 3.13,-7 7,-7s7,3.13 7,7v1h-4v8h4c1.1,0 2,-0.9 2,-2v-7C21,7.03 16.97,3 12,3z"/>
</vector>
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/ic_headphone_round_alt.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="@drawable/shape_circle_alt"/>
<item android:drawable="@drawable/ic_headphone_alt"
android:left="8dp"
android:right="8dp"
android:top="8dp"
android:bottom="8dp"/>
</layer-list>
Loading

0 comments on commit cd260e4

Please sign in to comment.