This commit is contained in:
lunaticbum 2024-08-26 18:34:53 +09:00
parent e1ec3c12d2
commit 55f2c3272c
5 changed files with 247 additions and 18 deletions

View File

@ -113,6 +113,14 @@
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
<service android:name=".utils.NLService"
android:label="@string/app_name"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
</application>
</manifest>

View File

@ -19,7 +19,9 @@
package rasel.lunar.launcher.home
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.SharedPreferences
@ -31,15 +33,12 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.biometric.BiometricPrompt
import androidx.core.content.ContextCompat.RECEIVER_EXPORTED
import androidx.core.content.ContextCompat.registerReceiver
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.LiveData
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkInfo
import androidx.work.WorkManager
import com.google.gson.Gson
import rasel.lunar.launcher.LauncherActivity.Companion.lActivity
@ -84,6 +83,17 @@ internal class LauncherHome : Fragment() {
var smsList = arrayListOf<RecentSmsLog>()
}
private val nReceiver: NotificationReceiver? = null
class NotificationReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
// val temp = """
// ${intent.getStringExtra("notification_event")}
//// ${txtView.getText()}
// """.trimIndent()
// txtView.setText(temp)
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = LauncherHomeBinding.inflate(inflater, container, false)
@ -100,16 +110,26 @@ internal class LauncherHome : Fragment() {
mWorkManager?.enqueue(PeriodicWorkRequestBuilder<RecentSmsGetter>(30, TimeUnit.SECONDS).addTag("RecentSmsGetter").build())
mWorkManager?.enqueueUniquePeriodicWork("MissedCallGetter",ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,PeriodicWorkRequestBuilder<MissedCallGetter>(30, TimeUnit.SECONDS).addTag("MissedCallGetter").build())
mWorkManager?.getWorkInfosByTagLiveData("RecentSmsGetter")?.observeForever {
binding.recentSms.text = "최근 문자 [${smsList.size}]"
if (binding.recentSms.isChecked) chooseAdpater()
else if (missedCalls.size == 0) {
binding.recentSms.isChecked = true
chooseAdpater()
}
BLog.LOGE("smsList.size >>> ${smsList.size}")
it.clear()
}
mWorkManager?.getWorkInfosByTagLiveData("MissedCallGetter")?.observeForever {
binding.missedCalls.text = "최근 통화 [${missedCalls.size}]"
if (binding.missedCalls.isChecked) chooseAdpater()
it.clear()
}
val filter = IntentFilter()
filter.addAction("com.kpbird.nlsexample.NOTIFICATION_LISTENER_EXAMPLE")
registerReceiver(requireContext(),nReceiver, filter,RECEIVER_EXPORTED)
BLog.LOGE("onCreateView()")
return binding.root
}
@ -178,6 +198,7 @@ internal class LauncherHome : Fragment() {
try {
BLog.LOGE("chooseAdpater smsList >>> ${smsList.size}")
binding.mainList.adapter = mSmsLogsAdapter
smsList.sortByDescending { it.rcvDate }
mSmsLogsAdapter.updateData(smsList)
} catch (e : Exception) {

View File

@ -38,6 +38,7 @@ import rasel.lunar.launcher.helpers.UniUtils.Companion.copyToClipboard
import rasel.lunar.launcher.home.MissedCall
import rasel.lunar.launcher.utils.BLog
import rasel.lunar.launcher.utils.RecentSmsLog
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.List
@ -63,7 +64,8 @@ internal class SmsLogsAdapter(
override fun onBindViewHolder(holder: SmsLogHolder, position: Int) {
val todo = smsList[position]
BLog.LOGE("SmsLogsAdapter smsList >>> ${smsList[position]}")
holder.view.itemText.text = "\u25CF ${todo.person} ${todo.addr} , ${todo.body} : ${todo.pstDate} : ${todo.type}"
holder.view.itemText.text = "\u25CF ${todo.person} ${todo.addr} , ${todo.body} : ${SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(Date(Math.max(todo.pstDate.toLong(),todo.rcvDate.toLong())))} : ${todo.type}"
/* multiline texts are enabled for TodoManager */
// holder.view.itemText.isSingleLine = false

View File

@ -1,10 +1,12 @@
package rasel.lunar.launcher.utils
import android.annotation.SuppressLint
import android.content.ContentResolver
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.provider.CallLog
import android.provider.Telephony
import androidx.work.Data
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
@ -13,8 +15,12 @@ import rasel.lunar.launcher.LauncherActivity.Companion.lActivity
import rasel.lunar.launcher.home.LauncherHome.Companion.missedCalls
import rasel.lunar.launcher.home.LauncherHome.Companion.smsList
import rasel.lunar.launcher.home.MissedCall
import java.io.BufferedReader
import java.io.InputStreamReader
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import kotlin.properties.Delegates
class MissedCallGetter : Worker {
@ -24,7 +30,7 @@ class MissedCallGetter : Worker {
@SuppressLint("RestrictedApi")
override fun doWork(): Result {
var dateParam = Date(System.currentTimeMillis() - (1000 * 60 * 60 * 24)).time.toString()
var dateParam = beforeDay(Date(),3).toString()
var managedCursor =lActivity?.contentResolver?.query(
CallLog.Calls.CONTENT_URI, arrayOf(
CallLog.Calls.NUMBER,
@ -32,7 +38,9 @@ class MissedCallGetter : Worker {
CallLog.Calls.DATE,
CallLog.Calls.DURATION,
CallLog.Calls.CACHED_NAME,
), CallLog.Calls.DATE + "> ? AND " + CallLog.Calls.TYPE + " > ?", arrayOf<String>(dateParam, "2"), CallLog.Calls.DATE + " desc")
), CallLog.Calls.DATE + " >= ? " , arrayOf<String>(dateParam), CallLog.Calls.DATE + " desc")
//+ CallLog.Calls.TYPE + " > ?"
//, "2"
if(managedCursor != null && managedCursor.isClosed == false) {
try {
val number = managedCursor.getColumnIndex(CallLog.Calls.NUMBER)
@ -94,6 +102,7 @@ class MissedCallGetter : Worker {
SimpleDateFormat("yyy/MM/dd-HH:mm:ss").format(callDayTime)
)
}
BLog.LOGE("missed put >>> ${missed.toJson()}")
missedCalls.put(phNumber, missed)
}
@ -111,6 +120,12 @@ class MissedCallGetter : Worker {
}
fun beforeDay(date: Date?, day: Int): Long {
val cal: Calendar = Calendar.getInstance()
cal.setTime(date)
cal.add(Calendar.DAY_OF_YEAR, Math.abs(day) * -1)
return cal.timeInMillis
}
class RecentSmsGetter : Worker {
@ -120,11 +135,11 @@ class RecentSmsGetter : Worker {
@SuppressLint("RestrictedApi")
override fun doWork(): Result {
BLog.LOGE("phNumber == onStart")
var resultData = workDataOf("" to "")
var dateParam = Date(System.currentTimeMillis() - (1000 * 60 * 60 * 24 * 3)).time.toString()
var dateParam = beforeDay(Date(),3).toString()
val managedCursor = lActivity?.contentResolver?.query(
Telephony.Sms.CONTENT_URI, arrayOf(
Telephony.Sms.ADDRESS,
@ -133,9 +148,10 @@ class RecentSmsGetter : Worker {
Telephony.Sms.DATE_SENT,
Telephony.Sms.BODY,
Telephony.Sms.PERSON,
), Telephony.Sms.DATE + ">= ? OR " + Telephony.Sms.DATE_SENT + " >= ?", arrayOf<String>(dateParam, dateParam), Telephony.Sms.DEFAULT_SORT_ORDER)
), Telephony.Sms.DATE + "> ${dateParam}", null, Telephony.Sms.DEFAULT_SORT_ORDER)
if (managedCursor != null && managedCursor.isClosed == false) {
try {
smsList.clear()
val address = managedCursor.getColumnIndex(Telephony.Sms.ADDRESS)
val type = managedCursor.getColumnIndex(Telephony.Sms.TYPE)
val date = managedCursor.getColumnIndex(Telephony.Sms.DATE)
@ -179,16 +195,20 @@ class RecentSmsGetter : Worker {
managedCursor.close()
}
}
if (lActivity?.contentResolver != null) {
TestQueryHelper(lActivity?.contentResolver!!).query()
}
return Result.success()
}
}
class RecentSmsLog {
var id : String = ""
var addr : String = ""
var type : String = ""
var rcvDate : String = ""
var pstDate : String = ""
var rcvDate : String = "0"
var pstDate : String = "0"
var body : String = ""
var person : String = ""
@ -208,7 +228,118 @@ class RecentSmsLog {
this.person = person
}
constructor(id: String, sender: String, date: String, body: String) {
this.id = id
this.rcvDate = date
this.body = body
this.addr = sender
}
fun toJson() : String {
return Gson().toJson(this)
}
}
class TestQueryHelper(
private val _contentResolver: ContentResolver
) {
private val mmsUri: Uri = Telephony.Mms.CONTENT_URI
private val cursor: Cursor?
get() =
_contentResolver.query(
mmsUri,
null,
null,
null,
null
)
fun dataMapper(cursor: Cursor): RecentSmsLog {
val id = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Mms._ID))
val date = cursor.getLong(cursor.getColumnIndexOrThrow(Telephony.Mms.DATE)) * 1000
val sender : String = getSender(id) ?: throw NullPointerException("NotFound Sender For Mms")
val body: String = getBody(id) ?: throw NullPointerException("NotFound Body by Mms")
return RecentSmsLog(
id = id.toString(),
date = date.toString(),
body = body,
sender = sender
)
}
private fun getSender(id: Int): String? {
_contentResolver.query(
Uri.parse("content://mms/$id/addr"),
null,
"${Telephony.Mms.Addr.MSG_ID} = ?",
arrayOf(id.toString()),
null
)?.use { senderAddressCursor ->
if (senderAddressCursor.moveToFirst()) {
do {
// 137 수신, 151 발신 타입 값이다. Telephony.Mms.Addr.Type 의 주석 참고.
val isSender = senderAddressCursor.getInt(
senderAddressCursor.getColumnIndexOrThrow(Telephony.Mms.Addr.TYPE)
) == 137
var sender = senderAddressCursor.getString(
senderAddressCursor.getColumnIndexOrThrow(Telephony.Mms.Addr.ADDRESS)
)
BLog.LOGE("sender >> ${sender}")
if (isSender) {
return sender
}
} while (senderAddressCursor.moveToNext())
}
}
return null
}
private fun getBody(id: Int): String? {
_contentResolver.query(
Uri.parse("content://mms/part"),
null,
"${Telephony.Mms.Part.MSG_ID} = ?",
arrayOf(id.toString()),
null
)?.use { partsCursor ->
if (partsCursor.moveToFirst()) {
do {
val partContentType =
partsCursor.getString(partsCursor.getColumnIndexOrThrow(Telephony.Mms.Part.CONTENT_TYPE))
if (partContentType == "text/plain") {
var textBody =
partsCursor.getString(partsCursor.getColumnIndexOrThrow(Telephony.Mms.Part.TEXT))
.replace("\n\n", "\n").replace("\n", " ")
if (textBody.length > 60) {
textBody = textBody.substring(0, Math.min(textBody.length, 60)).plus(" ... ")
}
BLog.LOGE("textBody >>> ${textBody}")
return textBody
}
} while (partsCursor.moveToNext())
}
}
return null
}
fun convertData(cursor: Cursor?) {
cursor ?: return
cursor.use {
if (cursor.moveToFirst()) {
do {
val data = kotlin.runCatching {
dataMapper(cursor)
}.getOrNull()
data?.let { smsList.add(it) }
} while (cursor.moveToNext())
}
}
}
fun query() {
return convertData(cursor)
}
}

View File

@ -0,0 +1,67 @@
package rasel.lunar.launcher.utils
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification
import android.util.Log
class NLService : NotificationListenerService() {
private val TAG: String = this.javaClass.simpleName
private var nlservicereciver: NLServiceReceiver? = null
override fun onCreate() {
super.onCreate()
nlservicereciver = NLServiceReceiver()
val filter = IntentFilter()
filter.addAction("com.kpbird.nlsexample.NOTIFICATION_LISTENER_SERVICE_EXAMPLE")
registerReceiver(nlservicereciver, filter)
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(nlservicereciver)
}
override fun onNotificationPosted(sbn: StatusBarNotification) {
Log.i(TAG, "********** onNotificationPosted")
Log.i(TAG, "ID :" + sbn.id + "\t" + sbn.notification.tickerText + "\t" + sbn.packageName)
val i = Intent("com.kpbird.nlsexample.NOTIFICATION_LISTENER_EXAMPLE")
i.putExtra("notification_event", "onNotificationPosted :" + sbn.packageName + "\n")
sendBroadcast(i)
}
override fun onNotificationRemoved(sbn: StatusBarNotification) {
Log.i(TAG, "********** onNOtificationRemoved")
Log.i(TAG, "ID :" + sbn.id + "\t" + sbn.notification.tickerText + "\t" + sbn.packageName)
val i = Intent("com.kpbird.nlsexample.NOTIFICATION_LISTENER_EXAMPLE")
i.putExtra("notification_event", "onNotificationRemoved :" + sbn.packageName + "\n")
sendBroadcast(i)
}
internal inner class NLServiceReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
if (intent.getStringExtra("command") == "clearall") {
this@NLService.cancelAllNotifications()
} else if (intent.getStringExtra("command") == "list") {
val i1 = Intent("com.kpbird.nlsexample.NOTIFICATION_LISTENER_EXAMPLE")
i1.putExtra("notification_event", "=====================")
sendBroadcast(i1)
var i = 1
for (sbn in this@NLService.activeNotifications) {
val i2 = Intent("com.kpbird.nlsexample.NOTIFICATION_LISTENER_EXAMPLE")
i2.putExtra("notification_event", i.toString() + " " + sbn.packageName + "\n")
sendBroadcast(i2)
i++
}
val i3 = Intent("com.kpbird.nlsexample.NOTIFICATION_LISTENER_EXAMPLE")
i3.putExtra("notification_event", "===== Notification List ====")
sendBroadcast(i3)
}
}
}
}