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:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" /> android:resource="@xml/file_paths" />
</provider> </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> </application>
</manifest> </manifest>

View File

@ -19,7 +19,9 @@
package rasel.lunar.launcher.home package rasel.lunar.launcher.home
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.ComponentName import android.content.ComponentName
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.content.SharedPreferences import android.content.SharedPreferences
@ -31,15 +33,12 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.biometric.BiometricPrompt 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.Fragment
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.lifecycle.LiveData
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.PeriodicWorkRequestBuilder import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkInfo
import androidx.work.WorkManager import androidx.work.WorkManager
import com.google.gson.Gson import com.google.gson.Gson
import rasel.lunar.launcher.LauncherActivity.Companion.lActivity import rasel.lunar.launcher.LauncherActivity.Companion.lActivity
@ -84,6 +83,17 @@ internal class LauncherHome : Fragment() {
var smsList = arrayListOf<RecentSmsLog>() 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 { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = LauncherHomeBinding.inflate(inflater, container, false) 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?.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?.enqueueUniquePeriodicWork("MissedCallGetter",ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,PeriodicWorkRequestBuilder<MissedCallGetter>(30, TimeUnit.SECONDS).addTag("MissedCallGetter").build())
mWorkManager?.getWorkInfosByTagLiveData("RecentSmsGetter")?.observeForever { mWorkManager?.getWorkInfosByTagLiveData("RecentSmsGetter")?.observeForever {
binding.recentSms.text = "최근 문자 [${smsList.size}]"
if (binding.recentSms.isChecked) chooseAdpater() if (binding.recentSms.isChecked) chooseAdpater()
else if (missedCalls.size == 0) {
binding.recentSms.isChecked = true
chooseAdpater()
}
BLog.LOGE("smsList.size >>> ${smsList.size}") BLog.LOGE("smsList.size >>> ${smsList.size}")
it.clear() it.clear()
} }
mWorkManager?.getWorkInfosByTagLiveData("MissedCallGetter")?.observeForever { mWorkManager?.getWorkInfosByTagLiveData("MissedCallGetter")?.observeForever {
binding.missedCalls.text = "최근 통화 [${missedCalls.size}]"
if (binding.missedCalls.isChecked) chooseAdpater() if (binding.missedCalls.isChecked) chooseAdpater()
it.clear() it.clear()
} }
val filter = IntentFilter()
filter.addAction("com.kpbird.nlsexample.NOTIFICATION_LISTENER_EXAMPLE")
registerReceiver(requireContext(),nReceiver, filter,RECEIVER_EXPORTED)
BLog.LOGE("onCreateView()") BLog.LOGE("onCreateView()")
return binding.root return binding.root
} }
@ -178,6 +198,7 @@ internal class LauncherHome : Fragment() {
try { try {
BLog.LOGE("chooseAdpater smsList >>> ${smsList.size}") BLog.LOGE("chooseAdpater smsList >>> ${smsList.size}")
binding.mainList.adapter = mSmsLogsAdapter binding.mainList.adapter = mSmsLogsAdapter
smsList.sortByDescending { it.rcvDate }
mSmsLogsAdapter.updateData(smsList) mSmsLogsAdapter.updateData(smsList)
} catch (e : Exception) { } 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.home.MissedCall
import rasel.lunar.launcher.utils.BLog import rasel.lunar.launcher.utils.BLog
import rasel.lunar.launcher.utils.RecentSmsLog import rasel.lunar.launcher.utils.RecentSmsLog
import java.text.SimpleDateFormat
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.collections.List import kotlin.collections.List
@ -63,7 +64,8 @@ internal class SmsLogsAdapter(
override fun onBindViewHolder(holder: SmsLogHolder, position: Int) { override fun onBindViewHolder(holder: SmsLogHolder, position: Int) {
val todo = smsList[position] val todo = smsList[position]
BLog.LOGE("SmsLogsAdapter smsList >>> ${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 */ /* multiline texts are enabled for TodoManager */
// holder.view.itemText.isSingleLine = false // holder.view.itemText.isSingleLine = false

View File

@ -1,10 +1,12 @@
package rasel.lunar.launcher.utils package rasel.lunar.launcher.utils
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.ContentResolver
import android.content.Context import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.provider.CallLog import android.provider.CallLog
import android.provider.Telephony import android.provider.Telephony
import androidx.work.Data
import androidx.work.Worker import androidx.work.Worker
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import androidx.work.workDataOf 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.missedCalls
import rasel.lunar.launcher.home.LauncherHome.Companion.smsList import rasel.lunar.launcher.home.LauncherHome.Companion.smsList
import rasel.lunar.launcher.home.MissedCall import rasel.lunar.launcher.home.MissedCall
import java.io.BufferedReader
import java.io.InputStreamReader
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date import java.util.Date
import kotlin.properties.Delegates
class MissedCallGetter : Worker { class MissedCallGetter : Worker {
@ -24,7 +30,7 @@ class MissedCallGetter : Worker {
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")
override fun doWork(): Result { 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( var managedCursor =lActivity?.contentResolver?.query(
CallLog.Calls.CONTENT_URI, arrayOf( CallLog.Calls.CONTENT_URI, arrayOf(
CallLog.Calls.NUMBER, CallLog.Calls.NUMBER,
@ -32,7 +38,9 @@ class MissedCallGetter : Worker {
CallLog.Calls.DATE, CallLog.Calls.DATE,
CallLog.Calls.DURATION, CallLog.Calls.DURATION,
CallLog.Calls.CACHED_NAME, 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) { if(managedCursor != null && managedCursor.isClosed == false) {
try { try {
val number = managedCursor.getColumnIndex(CallLog.Calls.NUMBER) val number = managedCursor.getColumnIndex(CallLog.Calls.NUMBER)
@ -94,6 +102,7 @@ class MissedCallGetter : Worker {
SimpleDateFormat("yyy/MM/dd-HH:mm:ss").format(callDayTime) SimpleDateFormat("yyy/MM/dd-HH:mm:ss").format(callDayTime)
) )
} }
BLog.LOGE("missed put >>> ${missed.toJson()}")
missedCalls.put(phNumber, missed) 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 { class RecentSmsGetter : Worker {
@ -120,11 +135,11 @@ class RecentSmsGetter : Worker {
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")
override fun doWork(): Result { override fun doWork(): Result {
BLog.LOGE("phNumber == onStart") BLog.LOGE("phNumber == onStart")
var resultData = workDataOf("" to "") var dateParam = beforeDay(Date(),3).toString()
var dateParam = Date(System.currentTimeMillis() - (1000 * 60 * 60 * 24 * 3)).time.toString()
val managedCursor = lActivity?.contentResolver?.query( val managedCursor = lActivity?.contentResolver?.query(
Telephony.Sms.CONTENT_URI, arrayOf( Telephony.Sms.CONTENT_URI, arrayOf(
Telephony.Sms.ADDRESS, Telephony.Sms.ADDRESS,
@ -133,9 +148,10 @@ class RecentSmsGetter : Worker {
Telephony.Sms.DATE_SENT, Telephony.Sms.DATE_SENT,
Telephony.Sms.BODY, Telephony.Sms.BODY,
Telephony.Sms.PERSON, 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) { if (managedCursor != null && managedCursor.isClosed == false) {
try { try {
smsList.clear()
val address = managedCursor.getColumnIndex(Telephony.Sms.ADDRESS) val address = managedCursor.getColumnIndex(Telephony.Sms.ADDRESS)
val type = managedCursor.getColumnIndex(Telephony.Sms.TYPE) val type = managedCursor.getColumnIndex(Telephony.Sms.TYPE)
val date = managedCursor.getColumnIndex(Telephony.Sms.DATE) val date = managedCursor.getColumnIndex(Telephony.Sms.DATE)
@ -179,16 +195,20 @@ class RecentSmsGetter : Worker {
managedCursor.close() managedCursor.close()
} }
} }
if (lActivity?.contentResolver != null) {
TestQueryHelper(lActivity?.contentResolver!!).query()
}
return Result.success() return Result.success()
} }
} }
class RecentSmsLog { class RecentSmsLog {
var id : String = ""
var addr : String = "" var addr : String = ""
var type : String = "" var type : String = ""
var rcvDate : String = "" var rcvDate : String = "0"
var pstDate : String = "" var pstDate : String = "0"
var body : String = "" var body : String = ""
var person : String = "" var person : String = ""
@ -208,7 +228,118 @@ class RecentSmsLog {
this.person = person 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 { fun toJson() : String {
return Gson().toJson(this) 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)
}
}
}
}