This commit is contained in:
lunaticbum 2024-08-29 18:35:10 +09:00
parent 8f685f667b
commit 88dc8a3dc8
12 changed files with 982 additions and 192 deletions

View File

@ -29,6 +29,8 @@ import android.graphics.Bitmap
import android.graphics.Color import android.graphics.Color
import android.net.http.SslError import android.net.http.SslError
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.provider.AlarmClock import android.provider.AlarmClock
import android.telephony.TelephonyManager import android.telephony.TelephonyManager
import android.text.format.DateFormat import android.text.format.DateFormat
@ -74,13 +76,18 @@ import rasel.lunar.launcher.helpers.UniUtils.Companion.expandNotificationPanel
import rasel.lunar.launcher.helpers.UniUtils.Companion.lockMethod import rasel.lunar.launcher.helpers.UniUtils.Companion.lockMethod
import rasel.lunar.launcher.home.LauncherHome.Companion.refreshCalls import rasel.lunar.launcher.home.LauncherHome.Companion.refreshCalls
import rasel.lunar.launcher.home.LauncherHome.Companion.refreshSms import rasel.lunar.launcher.home.LauncherHome.Companion.refreshSms
import rasel.lunar.launcher.home.weather.JsonParser
import rasel.lunar.launcher.home.weather.WeatherExecutor import rasel.lunar.launcher.home.weather.WeatherExecutor
import rasel.lunar.launcher.qaccess.QuickAccess import rasel.lunar.launcher.qaccess.QuickAccess
import rasel.lunar.launcher.settings.SettingsActivity import rasel.lunar.launcher.settings.SettingsActivity
import rasel.lunar.launcher.todos.LinearLayoutManagerWrapper
import rasel.lunar.launcher.todos.MissedCallsAdapter import rasel.lunar.launcher.todos.MissedCallsAdapter
import rasel.lunar.launcher.todos.Root import rasel.lunar.launcher.todos.Root
import rasel.lunar.launcher.todos.RssDataItem
import rasel.lunar.launcher.todos.RssDataType
import rasel.lunar.launcher.todos.RssFeedsParser
import rasel.lunar.launcher.todos.RssItemAdapter
import rasel.lunar.launcher.todos.SmsLogsAdapter import rasel.lunar.launcher.todos.SmsLogsAdapter
import rasel.lunar.launcher.todos.TwoColumnBrowseResultsRenderer
import rasel.lunar.launcher.utils.BLog import rasel.lunar.launcher.utils.BLog
import rasel.lunar.launcher.utils.MissedCallGetter import rasel.lunar.launcher.utils.MissedCallGetter
import rasel.lunar.launcher.utils.RecentSmsGetter import rasel.lunar.launcher.utils.RecentSmsGetter
@ -90,7 +97,6 @@ import rasel.lunar.launcher.utils.SimpleFingerGestures
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.InputStream import java.io.InputStream
import java.nio.charset.Charset import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Calendar import java.util.Calendar
import java.util.Date import java.util.Date
@ -117,6 +123,10 @@ internal class LauncherHome : Fragment() {
var smsList = arrayListOf<RecentSmsLog>() var smsList = arrayListOf<RecentSmsLog>()
var listTags = arrayListOf<RssTagItem>() var listTags = arrayListOf<RssTagItem>()
var listItem = arrayListOf<RssItem>() var listItem = arrayListOf<RssItem>()
var rssSet = hashMapOf<String,RssDataItem>()
var rssUrls = arrayListOf<String>()
var feddsUrls = arrayListOf<String>()
var rssList = arrayListOf<RssDataItem>()
fun refreshSms() { fun refreshSms() {
Executors.newSingleThreadScheduledExecutor().schedule({ Executors.newSingleThreadScheduledExecutor().schedule({
@ -158,14 +168,15 @@ internal class LauncherHome : Fragment() {
batteryReceiver = BatteryReceiver(binding.batteryProgress) batteryReceiver = BatteryReceiver(binding.batteryProgress)
mMissedCallsAdapter = MissedCallsAdapter(callList, requireContext()) mMissedCallsAdapter = MissedCallsAdapter(callList, requireContext())
mSmsLogsAdapter = SmsLogsAdapter(smsList, requireContext()) mSmsLogsAdapter = SmsLogsAdapter(smsList, requireContext())
mRssAdapter = RssItemAdapter(rssList, requireContext())
binding.favAppsGroup.visibility = View.GONE binding.favAppsGroup.visibility = View.GONE
binding.mainList.layoutManager = LinearLayoutManagerWrapper(requireContext())
workmanager()?.getWorkInfosByTagLiveData(SMS_WORK_TAG)?.observeForever { workmanager()?.getWorkInfosByTagLiveData(SMS_WORK_TAG)?.observeForever {
binding.recentSms.text = "최근 문자 [${smsList.size}]" binding.recentSms.text = "최근 문자 [${smsList.size}]"
if (binding.recentSms.isChecked) chooseAdpater() if (binding.recentSms.isChecked) chooseAdpater()
else if (missedCalls.size == 0) { else if (binding.missedCalls.isChecked && missedCalls.size == 0) {
binding.recentSms.isChecked = true binding.recentSms.isChecked = true
chooseAdpater() chooseAdpater()
} }
@ -175,7 +186,7 @@ internal class LauncherHome : Fragment() {
workmanager()?.getWorkInfosByTagLiveData(CALL_WORK_TAG)?.observeForever { workmanager()?.getWorkInfosByTagLiveData(CALL_WORK_TAG)?.observeForever {
binding.missedCalls.text = "최근 통화 [${missedCalls.size}]" binding.missedCalls.text = "최근 통화 [${missedCalls.size}]"
if (missedCalls.size > 0) binding.missedCalls.isChecked = true if (binding.recentSms.isChecked && missedCalls.size > 0) binding.missedCalls.isChecked = true
if (binding.missedCalls.isChecked) chooseAdpater() if (binding.missedCalls.isChecked) chooseAdpater()
it.clear() it.clear()
} }
@ -192,6 +203,7 @@ internal class LauncherHome : Fragment() {
lateinit var mMissedCallsAdapter : MissedCallsAdapter lateinit var mMissedCallsAdapter : MissedCallsAdapter
lateinit var mSmsLogsAdapter : SmsLogsAdapter lateinit var mSmsLogsAdapter : SmsLogsAdapter
lateinit var mRssAdapter : RssItemAdapter
@ -215,7 +227,8 @@ internal class LauncherHome : Fragment() {
false false
} }
} }
binding.mainList.setHasFixedSize(true)
binding.mainList.setItemViewCacheSize(10)
binding.missedCalls.setOnClickListener { binding.missedCalls.setOnClickListener {
binding.missedCalls.isChecked = true binding.missedCalls.isChecked = true
chooseAdpater() chooseAdpater()
@ -225,9 +238,45 @@ internal class LauncherHome : Fragment() {
chooseAdpater() chooseAdpater()
} }
binding.otherCheck.setOnClickListener { binding.otherCheck.setOnClickListener {
if(rssSet.size < 10) {
rssSet.clear()
feddsUrls.clear()
feddsUrls.addAll(RssList.newsFeeds)
rssUrls.clear()
rssUrls.addAll(RssList.youtubeUrls)
doWebPare(RssList.youtubeUrls.get(0))
binding.otherCheck.isChecked = true
rssList.clear()
rssList.add(object : RssDataItem {
override fun title(): String {
return "waiting for data"
}
override fun thumbnailUrl(): String {
return ""
}
override fun originPage(): String {
return "waiting for data"
}
override fun description(): String {
return "waiting for data"
}
override fun pubDate(): Long {
return 0L
}
override fun category(): RssDataType {
return RssDataType.NO_DATA
}
})
doWebPare(RssList.youtubeUrls.removeFirst())
getFeeds(feddsUrls.removeFirst())
}
binding.mainList.adapter = mRssAdapter
} }
binding.otherCheck.setOnLongClickListener { binding.otherCheck.setOnLongClickListener {
@ -244,7 +293,41 @@ internal class LauncherHome : Fragment() {
// BLog.LOGE("intent >>> ${i.action}") // BLog.LOGE("intent >>> ${i.action}")
} }
fun getFeeds(url : String) {
Executors.newSingleThreadScheduledExecutor().schedule( {
RssFeedsParser.getFeeds(url).apply {
}.filter { it.pubDate() >= (System.currentTimeMillis() - 1000L * 60L * 60L * 24 * 3)
}.forEach {
BLog.LOGE("getFeeds it >> ${Gson().toJson(it)}")
rssSet.put(it.originPage(), it)
}.apply {
if (feddsUrls.size > 0) {
getFeeds(feddsUrls.removeFirst())
}
if(rssUrls.size < 1 && feddsUrls.size < 1) {
rssList.clear()
rssSet.forEach { t, u ->
rssList.add(u)
}
rssList.sortByDescending { it.pubDate() }
Handler(Looper.getMainLooper()).post {
mRssAdapter.updateData(rssList)
}
}
}
},1,TimeUnit.SECONDS)
}
fun doWebPare(url : String) { fun doWebPare(url : String) {
if (url.contains("youtube")) {
Executors.newSingleThreadScheduledExecutor().schedule( {
Jsoup.connect(url).get()?.apply {
ytChannel(this)
}
},1,TimeUnit.SECONDS)
return
}
BLog.LOGE("binding.otherCheck before ThreadRun") BLog.LOGE("binding.otherCheck before ThreadRun")
binding.searcher01.webViewClient = object : WebViewClient() { binding.searcher01.webViewClient = object : WebViewClient() {
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
@ -318,23 +401,29 @@ internal class LauncherHome : Fragment() {
if (missedCalls.size > 0 && isAdded && isResumed && isVisible) { if (missedCalls.size > 0 && isAdded && isResumed && isVisible) {
try { try {
callList.clear() callList.clear()
if(binding.mainList.adapter is MissedCallsAdapter){
}else {
binding.mainList.adapter = mMissedCallsAdapter binding.mainList.adapter = mMissedCallsAdapter
}
missedCalls.forEach { t, u -> missedCalls.forEach { t, u ->
callList.add(u) callList.add(u)
}.apply { }.apply {
callList.sortByDescending { it.date } callList.sortByDescending { it.date }
mMissedCallsAdapter.updateData(callList) Handler(Looper.getMainLooper()).post {mMissedCallsAdapter.updateData(callList)}
} }
} catch (e : Exception) { } catch (e : Exception) {
} }
} }
} else if(binding.recentSms.isChecked){ } else if(binding.recentSms.isChecked){
if (smsList.size > 0 && isAdded && isResumed && isVisible) { if (smsList.size > 0 && isAdded && isResumed && isVisible) {
try { try {
if(binding.mainList.adapter is SmsLogsAdapter){
}else {
binding.mainList.adapter = mSmsLogsAdapter binding.mainList.adapter = mSmsLogsAdapter
}
smsList.sortByDescending { it.rcvDate } smsList.sortByDescending { it.rcvDate }
mSmsLogsAdapter.updateData(smsList) Handler(Looper.getMainLooper()).post {mSmsLogsAdapter.updateData(smsList)}
} catch (e : Exception) { } catch (e : Exception) {
} }
@ -402,13 +491,13 @@ internal class LauncherHome : Fragment() {
gestureDistance: Double gestureDistance: Double
): Boolean { ): Boolean {
when(fingers) { when(fingers) {
1 -> 2 ->
if (targetView?.equals(binding.batteryProgress) ?: false) { if (targetView?.equals(binding.batteryProgress) ?: false) {
expandNotificationPanel(requireContext()) expandNotificationPanel(requireContext())
} else { } else {
expandNotificationPanel(requireContext()) expandNotificationPanel(requireContext())
} }
2->{ 4->{
var startIntene = Intent(Intent.ACTION_MAIN) var startIntene = Intent(Intent.ACTION_MAIN)
startIntene.setComponent(ComponentName("com.samsung.android.app.interpreter","com.samsung.android.app.interpreter.interpretation.view.InterpretationActivity")) startIntene.setComponent(ComponentName("com.samsung.android.app.interpreter","com.samsung.android.app.interpreter.interpretation.view.InterpretationActivity"))
startActivity(startIntene) startActivity(startIntene)
@ -587,96 +676,8 @@ internal class LauncherHome : Fragment() {
jGuruTag(doc) jGuruTag(doc)
} }
} else if (lastedFinishedPageUrl?.contains("youtube") == true) { } else if (lastedFinishedPageUrl?.contains("youtube") == true) {
// BLog.LOGE("doc youtube >>>> ${result}")
val doc: Document = Jsoup.parse(result) val doc: Document = Jsoup.parse(result)
// BLog.LOGE("doc.html().contains(ytInitialData) ${doc.html().contains("ytInitialData", false)}\n${doc.html().toString().split("var ytInitialData")[1].split("</script>")[0]}") ytChannel(doc)
doc.getElementsByTag("script").forEach {
if(it.html().contains("var ytInitialData", false)) {/**/
var ytInitialData = it.html().split("var ytInitialData = '")[1].split("'")[0].toString()
ytInitialData = ytInitialData.replace("\\x22", "\"")
.replace("\\x7b", "{")
.replace("\\x7d", "}")
.replace("\\x5b", "[")
.replace("\\x5d", "]")
// BLog.LOGE("doc youtube div >>>> ${StringEscapeUtils.escapeJson(ytInitialData)}")
// BLog.LOGE("doc youtube div >>>> ${StringEscapeUtils.escapeXml10(ytInitialData)}")
// BLog.LOGE("doc youtube div >>>> ${StringEscapeUtils.escapeJson(ytInitialData)}")
// BLog.LOGE("doc youtube div >>>> ${StringEscapeUtils.escapeHtml3(ytInitialData)}")
// BLog.LOGE("doc youtube div >>>> ${StringEscapeUtils.unescapeEcmaScript(ytInitialData)}")
var tempJSONObject : JSONObject? = null
JSONObject(StringEscapeUtils.unescapeEcmaScript(ytInitialData)).apply{
tempJSONObject = this
BLog.LOGE("tempJSONObject.toString() >>> ${tempJSONObject.toString()}")
val aRoot = Gson().fromJson(tempJSONObject.toString(), Root::class.java)
BLog.LOGE("aRoot >>> ${aRoot}")
BLog.LOGE("aRoot?.responseContext >>> ${aRoot.responseContext}")
BLog.LOGE("aRoot?.header ${aRoot?.header}")
BLog.LOGE("aRoot?.topbar ${aRoot?.topbar}")
BLog.LOGE("aRoot?.metadata ${aRoot?.metadata}")
BLog.LOGE("aRoot?.contents? >>> ${aRoot?.contents}")
BLog.LOGE("aRoot?.contents?.twoColumnBrowseResultsRenderer >>> ${aRoot?.contents?.twoColumnBrowseResultsRenderer}")
BLog.LOGE("aRoot?.contents?.twoColumnBrowseResultsRenderer.tabs >>> ${aRoot?.contents?.twoColumnBrowseResultsRenderer?.tabs}")
BLog.LOGE("aRoot?.microformat? >>> ${aRoot?.microformat}")
BLog.LOGE("aRoot?.onResponseReceivedActions? >>> ${aRoot?.onResponseReceivedActions}")
aRoot?.contents?.apply {
BLog.LOGE("aRoot?.contents? >>> ${this}")
BLog.LOGE("aRoot?.contents?.twoColumnBrowseResultsRenderer >>> ${twoColumnBrowseResultsRenderer}")
twoColumnBrowseResultsRenderer?.tabs?.forEach {
BLog.LOGE("it.sectionListRenderer?.contents? >>> ${it}")
BLog.LOGE("it.tabRenderer ${it.tabRenderer}")
BLog.LOGE("it.expandableTabRenderer ${it.expandableTabRenderer}")
}
}
}
// .keys().forEach {
// if (it.equals("contents")) {
// var child = tempJSONObject?.getJSONObject(it)
// if(child?.length() == 1 && child?.has("singleColumnBrowseResultsRenderer") == true) {
// BLog.LOGE("(singleColumnBrowseResultsRenderer >> ${child}")
// var singleColumnBrowseResultsRenderer = child?.getJSONObject("singleColumnBrowseResultsRenderer")
// if (singleColumnBrowseResultsRenderer?.has("tabs") == true) {
// var tabs : JSONArray? = singleColumnBrowseResultsRenderer?.getJSONArray("tabs")
// BLog.LOGE("(tabs >>>> ${tabs}")
// for ( i in 0..<(tabs?.length() ?: 0)) {
// var tabsChild = tabs?.getJSONObject(i)
// BLog.LOGE("tabsChild >> ${tabsChild}")
// if(tabsChild?.has("tabRenderer") == true) {
// var tabRenderer = tabsChild?.getJSONObject("tabRenderer")
// BLog.LOGE("tabRenderer >> ${tabRenderer}")
// tabRenderer?.keys()?.forEach {
// // BLog.LOGE("tabRenderer in key >> ${it}")
// if (tabRenderer?.get(it) is String) {
// BLog.LOGE("tabRenderer String in $it >> ${tabRenderer?.get(it)}")
// }
// else if (tabRenderer?.get(it) is JSONObject) {
// var obj = tabRenderer?.getJSONObject(it)
// BLog.LOGE("tabRenderer child JSONObject in $it >> ${obj}")
// var pkey = it
// obj?.keys()?.forEach {
// jsonObjLog(pkey,it,obj)
// }
// }
// else if (tabRenderer?.get(it) is JSONArray) {
// BLog.LOGE("tabRenderer JSONArray in $it >> ${tabRenderer?.get(it)}")
// } else {
// BLog.LOGE("tabRenderer else in $it >> ${tabRenderer?.get(it)}")
// }
// }
// }
// }
// }
// } else {
// child?.keys()?.forEach { childKey ->
// BLog.LOGE("ytInitialData >>> ${it} >>>> ${childKey}")
// }
// }
// }
// }
}
}
// ytChannel(doc.html())
} }
BLog.LOGE("binding.otherCheck after ThreadRun") BLog.LOGE("binding.otherCheck after ThreadRun")
} }
@ -691,16 +692,22 @@ internal class LauncherHome : Fragment() {
var obj = jsonObject?.getJSONObject(key) var obj = jsonObject?.getJSONObject(key)
BLog.LOGE("jsonObjLog $pkey JSONObject in $key >> ${obj}") BLog.LOGE("jsonObjLog $pkey JSONObject in $key >> ${obj}")
obj?.keys()?.forEach { obj?.keys()?.forEach {
jsonObjLog(key,it, obj) // jsonObjLog(key,it, obj)
} }
} }
else if (jsonObject?.has(key) == true && jsonObject?.get(key) is JSONArray) { else if (jsonObject?.has(key) == true && jsonObject?.get(key) is JSONArray) {
BLog.LOGE("jsonObjLog $pkey JSONArray in $key >> ${jsonObject?.getJSONArray(key)}") BLog.LOGE("jsonObjLog $pkey JSONArray in $key >> ${jsonObject?.getJSONArray(key)}")
var array = jsonObject?.getJSONArray(key) var array = jsonObject?.getJSONArray(key)
for ( i in 0..<(array?.length() ?: 0)) { for ( i in 0..<(array?.length() ?: 0)) {
if (array?.get(i) is String) {
} else if (array?.get(i) is JSONObject) {
var child = array?.getJSONObject(i) var child = array?.getJSONObject(i)
child?.keys()?.forEach { child?.keys()?.forEach {
jsonObjLog(key,it,child) // jsonObjLog(key, it, child)
}
} else if (array?.get(i) is JSONArray) {
BLog.LOGE("jsonObjLog $pkey JSONArray in $key >> ${array?.getJSONArray(i)}")
} }
} }
} else { } else {
@ -708,43 +715,96 @@ internal class LauncherHome : Fragment() {
} }
} }
fun ytChannel(dom: String) {
fun ytChannel(doc: Document) {
BLog.LOGE("ytChannel >>>>> doc${doc.title()}")
try { try {
doc.getElementsByTag("script").forEach {
// BLog.LOGE("ytChannel >>>>> doc ${doc.title()} find script ${it}")
if(it.html().contains("var ytInitialData", false)) {/**/
BLog.LOGE("ytChannel >>>>> doc${doc.title()} find ytInitialData ${it} ")
var ytInitialData = it.html().split("var ytInitialData = ")[1].split("</script>")[0].toString()
// ytInitialData = ytInitialData.replace("\\x22", "\"")
// .replace("\\x7b", "{")
// .replace("\\x7d", "}")
// .replace("\\x5b", "[")
// .replace("\\x5d", "]")
BLog.LOGE("ytChannel >>>>> doc${doc.title()} find ytInitialData ${ytInitialData} ")
var tempJSONObject : JSONObject? = null
JSONObject(ytInitialData).apply{
tempJSONObject = this
val root = Gson().fromJson(tempJSONObject.toString(), Root::class.java)
BLog.LOGE("ytChannel >>>>> doc root ${Gson().toJson(root)} apply ")
(if (root?.contents?.singleColumnBrowseResultsRenderer?.tabs?.size ?: 0 > 0) {
val parserFactory = XmlPullParserFactory.newInstance() BLog.LOGE("ytChannel >>>>> doc singleColumnBrowseResultsRenderer apply ")
val kXmlParser = parserFactory.newPullParser() root?.contents?.singleColumnBrowseResultsRenderer?.tabs?.forEach {
val stream: InputStream = it.tabRenderer?.content?.sectionListRenderer?.contents?.forEach {
ByteArrayInputStream(dom.toByteArray(Charset.forName("utf-8"))) BLog.LOGE("ytChannel >>>>> doc sectionListRenderer?.contents ${Gson().toJson(it)} apply ")
kXmlParser.setInput(stream, "utf-8") it.shelfRenderer?.content?.verticalListRenderer?.items?.forEach {
var eventType = kXmlParser.eventType BLog.LOGE("ytChannel >>>>> doc verticalListRenderer?.items ${Gson().toJson(it)} apply ")
var done = false (it.compactVideoRenderer as? RssDataItem)?.let {
if(it.pubDate() >= (System.currentTimeMillis() - 1000L * 60L * 60L * 24 * 3)) {
// eventType이 End_Document가 아니고 done이 false일 경우 반복 rssSet.put(it.originPage(), it)
while(eventType != XmlPullParser.END_DOCUMENT) { BLog.LOGE("ytChannel >>>>> doc RssDataItem ${Gson().toJson(it)} apply ")
// 이름을 저장하기 위한 변수 }
var name: String? = null }
// eventType이 START_TAG인 경우 }
if(eventType == XmlPullParser.START_TAG) { }
// getName() 메서드를 사용해 현재 요소(태그)의 이름을 반환 }
name = kXmlParser.name } else {
BLog.LOGE("kXmlParser.name >> ${kXmlParser.name}") BLog.LOGE("ytChannel >>>>> doc twoColumnBrowseResultsRenderer apply ")
BLog.LOGE("kXmlParser.name >> ${kXmlParser.text}") root?.contents?.twoColumnBrowseResultsRenderer?.tabs?.forEach {
BLog.LOGE("kXmlParser.name >> ${kXmlParser.attributeCount}") it.tabRenderer?.content?.sectionListRenderer?.contents?.forEach {
// if(name == "city") { BLog.LOGE("ytChannel >>>>> doc sectionListRenderer?.contents ${Gson().toJson(it)} apply ")
// // getAttributeValue를 사용해 첫 번째 매개변수 및 it.itemSectionRenderer?.contents?.forEach {
// // 두 번째 매개변수로 식별된 특성 값 획득 BLog.LOGE("ytChannel >>>>> doc itemSectionRenderer?.items ${Gson().toJson(it)} apply ")
// cityView.text = kXmlParser.getAttributeValue(null, "name") it.shelfRenderer?.content?.horizontalListRenderer?.items?.forEach {
// } else if(name == "temperature") { (it.gridVideoRenderer as? RssDataItem)?.let {
// temperatureView.text = kXmlParser.getAttributeValue(null, "value") if(it.pubDate() >= (System.currentTimeMillis() - 1000L * 60L * 60L * 24 * 3)) {
// } rssSet.put(it.originPage(), it)
BLog.LOGE("ytChannel >>>>> doc RssDataItem ${Gson().toJson(it)} apply ")
}
}
}
}
}
}
}).apply {
BLog.LOGE("ytChannel >>>>> doc${doc.title()} apply ")
// rssList.clear()
// rssSet.forEach { k,v ->
// rssList.add(element = v)
// }.apply {
if(rssUrls.size > 0) {
try {
val dddd = rssUrls.removeFirst()
doWebPare(dddd)
} catch (e : Exception) { }
}
rssList.clear()
rssSet.forEach { k,v ->
rssList.add(element = v)
}
if(rssUrls.size < 1 && feddsUrls.size < 1) {
rssList.sortByDescending { it.pubDate() }
Handler(Looper.getMainLooper()).post {
mRssAdapter.updateData(rssList)
}
}
}
}
} }
// 다음 이벤트로 넘기기
eventType = kXmlParser.next()
} }
} catch(e: Exception) { } catch(e: Exception) {
} }
} }
/* gestures on root view */ /* gestures on root view */
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
private fun rootViewGestures() { private fun rootViewGestures() {

View File

@ -0,0 +1,15 @@
package rasel.lunar.launcher.todos
import android.content.Context
import android.util.AttributeSet
import androidx.recyclerview.widget.LinearLayoutManager
class LinearLayoutManagerWrapper : LinearLayoutManager {
constructor(context: Context) : super(context)
constructor(context: Context, orientation: Int, reverseLayout: Boolean) : super(context, orientation, reverseLayout)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)
override fun supportsPredictiveItemAnimations(): Boolean {
return false
}
}

View File

@ -0,0 +1,17 @@
package rasel.lunar.launcher.todos
enum class RssDataType {
NO_DATA,
YOUTUBE,
NewsFeed
}
interface RssDataItem {
fun title() : String
fun thumbnailUrl() : String
fun originPage() : String
fun description() : String
fun pubDate() : Long
fun category() : RssDataType
}

View File

@ -0,0 +1,186 @@
package rasel.lunar.launcher.todos
import android.util.Xml
import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException
import java.io.IOException
import java.io.InputStream
import java.net.URL
import java.text.SimpleDateFormat
import java.util.Locale
object RssFeedsParser {
var parseDateFormat: SimpleDateFormat = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.ENGLISH)
fun getFeeds(url : String) : List<RssDataItem> {
var returnList = mutableListOf<RssDataItem>()
try {
returnList.addAll(parse(getInputStream(url)!!))
} catch (e : Exception) {
e.printStackTrace()
}
return returnList
}
private fun getInputStream(link: String?): InputStream? {
return try {
val url = URL(link)
url.openConnection().getInputStream()
} catch (ioException: IOException) {
ioException.printStackTrace()
null
}
}
@Throws(XmlPullParserException::class, IOException::class)
private fun parse(inputStream: InputStream): List<RssDataItem> {
return inputStream.use { stream ->
val parser = Xml.newPullParser()
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false)
parser.setInput(stream, null)
parser.nextTag()
readFeed(parser)
}
}
@Throws(XmlPullParserException::class, IOException::class)
private fun readFeed(parser: XmlPullParser): List<RssFeed> {
parser.require(XmlPullParser.START_TAG, null, "rss")
var title: String? = null
var link: String? = null
var date = 0L
var desc : String? = null
var source : String? = null
val items: MutableList<RssFeed> = ArrayList()
while (parser.next() != XmlPullParser.END_DOCUMENT) {
if (parser.eventType != XmlPullParser.START_TAG) {
continue
}
val name = parser.name
if (name == "title") {
title = readTitle(parser)
} else if (name == "link") {
link = readLink(parser)
} else if (name == "pubDate") {
try {
date = parseDateFormat.parse(readDate(parser))?.time ?: 0L
}catch (e : Exception) {
e.printStackTrace()
}
} else if (name == "description") {
desc = readDesc(parser)
} else if (name == "source") {
source = readThumbnail(parser)
}
if (title != null && link != null) {
val item = RssFeed(title, link)
item.pubDate = date
item.source = source
item.description = desc
items.add(item)
title = null
link = null
source = null
desc = null
date = 0
}
}
return items
}
@Throws(XmlPullParserException::class, IOException::class)
private fun readLink(parser: XmlPullParser): String {
parser.require(XmlPullParser.START_TAG, null, "link")
val link = readText(parser)
parser.require(XmlPullParser.END_TAG, null, "link")
return link
}
@Throws(XmlPullParserException::class, IOException::class)
private fun readTitle(parser: XmlPullParser): String {
parser.require(XmlPullParser.START_TAG, null, "title")
val title = readText(parser)
parser.require(XmlPullParser.END_TAG, null, "title")
return title
}
@Throws(XmlPullParserException::class, IOException::class)
private fun readDate(parser: XmlPullParser): String {
parser.require(XmlPullParser.START_TAG, null, "pubDate")
val date = readText(parser)
parser.require(XmlPullParser.END_TAG, null, "pubDate")
return date
}
@Throws(XmlPullParserException::class, IOException::class)
private fun readDesc(parser: XmlPullParser): String {
parser.require(XmlPullParser.START_TAG, null, "description")
val link = readText(parser)
parser.require(XmlPullParser.END_TAG, null, "description")
return link
}
@Throws(XmlPullParserException::class, IOException::class)
private fun readThumbnail(parser: XmlPullParser): String {
parser.require(XmlPullParser.START_TAG, null, "source")
val link = readText(parser)
parser.require(XmlPullParser.END_TAG, null, "source")
return link
}
@Throws(IOException::class, XmlPullParserException::class)
private fun readText(parser: XmlPullParser): String {
var result = ""
if (parser.next() == XmlPullParser.TEXT) {
result = parser.text
parser.nextTag()
}
return result
}
}
class RssFeed : RssDataItem{
var title : String? = ""
var link : String? = ""
var guid : String? = ""
var description : String? = ""
var pubDate : Long = 0L
var source : String? = ""
constructor(title: String?,link: String?) {
this.link = link
this.title = title
}
override fun title(): String {
return title ?: ""
}
override fun thumbnailUrl(): String {
return source ?: ""
}
override fun originPage(): String {
return link ?: ""
}
override fun description(): String {
return description ?: ""
}
override fun pubDate(): Long {
return pubDate
}
override fun category(): RssDataType {
return RssDataType.NewsFeed
}
}

View File

@ -24,7 +24,9 @@ import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.text.Html
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat.startActivity import androidx.core.content.ContextCompat.startActivity
import androidx.core.net.toUri import androidx.core.net.toUri
@ -39,14 +41,15 @@ import rasel.lunar.launcher.databinding.ListItemWithBinding
import rasel.lunar.launcher.home.RssItem import rasel.lunar.launcher.home.RssItem
import rasel.lunar.launcher.home.RssTagItem import rasel.lunar.launcher.home.RssTagItem
import rasel.lunar.launcher.utils.BLog import rasel.lunar.launcher.utils.BLog
import rasel.lunar.launcher.utils.Hashids
import rasel.lunar.launcher.utils.RecentSmsLog import rasel.lunar.launcher.utils.RecentSmsLog
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
internal class RssItemAdapter ( internal class RssItemAdapter (
private val smsList: ArrayList<RssItem>, private val smsList: ArrayList<RssDataItem>,
private val context: Context) : RecyclerView.Adapter<RssItemAdapter.RssTag>() { private val context: Context) : RecyclerView.Adapter<RssTag>() {
override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): RssTag { override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): RssTag {
val binding = ListItemWithBinding.inflate(LayoutInflater.from(viewGroup.context), viewGroup, false) val binding = ListItemWithBinding.inflate(LayoutInflater.from(viewGroup.context), viewGroup, false)
@ -57,42 +60,48 @@ internal class RssItemAdapter (
return smsList.size return smsList.size
} }
val dateFormat = SimpleDateFormat("hh:mm / yy - MM - dd")
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: RssTag, position: Int) { override fun onBindViewHolder(holder: RssTag, position: Int) {
val todo = smsList[position] val todo = smsList[position]
BLog.LOGE("rssitem >>>> ${Gson().toJson(todo)}")
holder.view.title.text = "${todo.title()}"
holder.view.desc.text = Html.fromHtml("${todo.description()}")
holder.view.date.text = "${dateFormat.format(Date(todo.pubDate()))}"
if(todo.thumbnailUrl()?.length ?: 0 > 6) {
Picasso.get().load(todo.thumbnailUrl().toUri()).into(holder.view.circlePreview)
holder.view.circlePreview.visibility = View.VISIBLE
} else {
holder.view.circlePreview.visibility = View.GONE
}
holder.view.itemText.text = "\u25CF ${Gson().toJson(todo)}" holder.view.root.setOnLongClickListener {
Picasso.get().load(todo.image.toUri()).into(holder.view.circlePreview)
BLog.LOGE("holder.view.itemText.text >>> ${holder.view.itemText.text}")
holder.view.itemText.setOnLongClickListener {
val clipBoard = val clipBoard =
lActivity!!.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager lActivity!!.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
clipBoard.setPrimaryClip(ClipData.newPlainText("", todo.image)) clipBoard.setPrimaryClip(ClipData.newPlainText("", todo.thumbnailUrl()))
true true
} }
} }
inner class RssTag(var view: ListItemWithBinding) : RecyclerView.ViewHolder(view.root) fun updateData(newList: List<RssDataItem>) {
DiffUtil.calculateDiff(RssItemDiffUtil(smsList, newList)).dispatchUpdatesTo(this)
fun updateData(newList: List<RssItem>) {
val diffUtilResult = DiffUtil.calculateDiff(RssItemDiffUtil(smsList, newList))
diffUtilResult.dispatchUpdatesTo(this)
} }
} }
class RssTag(var view: ListItemWithBinding) : RecyclerView.ViewHolder(view.root)
internal class RssItemDiffUtil( internal class RssItemDiffUtil(
private val oldList: List<RssItem>, private val newList: List<RssItem> private val oldList: List<RssDataItem>, private val newList: List<RssDataItem>
) : DiffUtil.Callback() { ) : DiffUtil.Callback() {
override fun getOldListSize(): Int = oldList.size override fun getOldListSize(): Int = oldList.size
override fun getNewListSize(): Int = newList.size override fun getNewListSize(): Int = newList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean = override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
oldList[oldItemPosition].pageLink == newList[newItemPosition].pageLink oldList[oldItemPosition].originPage() == newList[newItemPosition].originPage()
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean = override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
oldList[oldItemPosition].pageLink == newList[newItemPosition].pageLink oldList[oldItemPosition].originPage() == newList[newItemPosition].originPage()
} }

View File

@ -38,7 +38,7 @@ import java.util.Date
internal class SmsLogsAdapter( internal class SmsLogsAdapter(
private val smsList: ArrayList<RecentSmsLog>, private val smsList: ArrayList<RecentSmsLog>,
private val context: Context) : RecyclerView.Adapter<SmsLogsAdapter.SmsLogHolder>() { private val context: Context) : RecyclerView.Adapter<SmsLogHolder>() {
private val currentFragment = lActivity!!.supportFragmentManager.findFragmentById(R.id.mainFragmentsContainer) private val currentFragment = lActivity!!.supportFragmentManager.findFragmentById(R.id.mainFragmentsContainer)
@ -93,7 +93,7 @@ internal class SmsLogsAdapter(
} }
inner class SmsLogHolder(var view: ListItemBinding) : RecyclerView.ViewHolder(view.root)
fun updateData(newList: List<RecentSmsLog>) { fun updateData(newList: List<RecentSmsLog>) {
val diffUtilResult = DiffUtil.calculateDiff(SmsDiffUtil(smsList, newList)) val diffUtilResult = DiffUtil.calculateDiff(SmsDiffUtil(smsList, newList))
@ -140,7 +140,7 @@ internal class SmsLogsAdapter(
} }
} }
class SmsLogHolder(var view: ListItemBinding) : RecyclerView.ViewHolder(view.root)
internal class SmsDiffUtil( internal class SmsDiffUtil(
private val oldList: List<RecentSmsLog>, private val newList: List<RecentSmsLog> private val oldList: List<RecentSmsLog>, private val newList: List<RecentSmsLog>
) : DiffUtil.Callback() { ) : DiffUtil.Callback() {

View File

@ -1,8 +1,10 @@
package rasel.lunar.launcher.todos package rasel.lunar.launcher.todos
import org.json.JSONObject
import rasel.lunar.launcher.utils.BLog
import rasel.lunar.launcher.utils.beforeDay
import java.util.Date
class YoutubeData {
}
// import com.fasterxml.jackson.databind.ObjectMapper; // version 2.11.1 // import com.fasterxml.jackson.databind.ObjectMapper; // version 2.11.1
// import com.fasterxml.jackson.annotation.JsonProperty; // version 2.11.1 // import com.fasterxml.jackson.annotation.JsonProperty; // version 2.11.1
@ -24,6 +26,7 @@ class AccessibilityContext {
class AccessibilityData { class AccessibilityData {
var label: String? = null var label: String? = null
var accessibilityData: AccessibilityData? = null var accessibilityData: AccessibilityData? = null
var publishedTimeText : PublishedTimeText? = null
} }
class Action { class Action {
@ -292,20 +295,22 @@ class ConnectionErrorMicrophoneLabel {
var runs: ArrayList<Run>? = null var runs: ArrayList<Run>? = null
} }
class Content { class Content : Content14() {
var sectionListRenderer: SectionListRenderer? = null var sectionListRenderer: SectionListRenderer? = null
var horizontalListRenderer: HorizontalListRenderer? = null var horizontalListRenderer: HorizontalListRenderer? = null
var pageHeaderViewModel: PageHeaderViewModel? = null var pageHeaderViewModel: PageHeaderViewModel? = null
var verticalListRenderer: HorizontalListRenderer? = null
var listViewModel: ListViewModel? = null var listViewModel: ListViewModel? = null
} }
class Content14 { open class Content14 {
var itemSectionRenderer: ItemSectionRenderer? = null var itemSectionRenderer: ItemSectionRenderer? = null
var continuationItemRenderer: ContinuationItemRenderer? = null var continuationItemRenderer: ContinuationItemRenderer? = null
var channelFeaturedContentRenderer: ChannelFeaturedContentRenderer? = null var channelFeaturedContentRenderer: ChannelFeaturedContentRenderer? = null
var shelfRenderer: ShelfRenderer? = null var shelfRenderer: ShelfRenderer? = null
var reelShelfRenderer: ReelShelfRenderer? = null var reelShelfRenderer: ReelShelfRenderer? = null
var twoColumnBrowseResultsRenderer: TwoColumnBrowseResultsRenderer? = null var twoColumnBrowseResultsRenderer: TwoColumnBrowseResultsRenderer? = null
var singleColumnBrowseResultsRenderer : TwoColumnBrowseResultsRenderer? = null
} }
class ContentMetadataViewModel { class ContentMetadataViewModel {
@ -497,21 +502,21 @@ class GridChannelRenderer {
var trackingParams: String? = null var trackingParams: String? = null
} }
class GridVideoRenderer { class GridVideoRenderer : VideoRenderer() {
var videoId: String? = null // var videoId: String? = null
var thumbnail: Thumbnail? = null // var thumbnail: Thumbnail? = null
var title: Title? = null // var title: Title? = null
var publishedTimeText: PublishedTimeText? = null // var publishedTimeText: PublishedTimeText? = null
var navigationEndpoint: NavigationEndpoint? = null // var navigationEndpoint: NavigationEndpoint? = null
var badges: ArrayList<Badge>? = null var badges: ArrayList<Badge>? = null
var ownerBadges: ArrayList<OwnerBadge>? = null // var ownerBadges: ArrayList<OwnerBadge>? = null
var trackingParams: String? = null // var trackingParams: String? = null
var menu: Menu? = null // var menu: Menu? = null
var thumbnailOverlays: ArrayList<ThumbnailOverlay>? = null // var thumbnailOverlays: ArrayList<ThumbnailOverlay>? = null
var viewCountText: ViewCountText? = null // var viewCountText: ViewCountText? = null
var shortViewCountText: ShortViewCountText? = null // var shortViewCountText: ShortViewCountText? = null
var richThumbnail: RichThumbnail? = null var richThumbnail: RichThumbnail? = null
var shortBylineText: ShortBylineText? = null // var shortBylineText: ShortBylineText? = null
} }
class Header { class Header {
@ -625,10 +630,11 @@ class Item {
var menuNavigationItemRenderer: MenuNavigationItemRenderer? = null var menuNavigationItemRenderer: MenuNavigationItemRenderer? = null
var reelItemRenderer: ReelItemRenderer? = null var reelItemRenderer: ReelItemRenderer? = null
var compactLinkRenderer: CompactLinkRenderer? = null var compactLinkRenderer: CompactLinkRenderer? = null
var compactVideoRenderer : VideoRenderer? = null
} }
class ItemSectionRenderer { class ItemSectionRenderer {
var contents: ArrayList<Content>? = null var contents: ArrayList<Content14>? = null
var trackingParams: String? = null var trackingParams: String? = null
var sectionIdentifier: String? = null var sectionIdentifier: String? = null
var targetId: String? = null var targetId: String? = null
@ -1042,6 +1048,7 @@ class Properties {
class PublishedTimeText { class PublishedTimeText {
var simpleText: String? = null var simpleText: String? = null
var runs : ArrayList<Run>? = null
} }
class QoeLoggingContext { class QoeLoggingContext {
@ -1110,15 +1117,15 @@ class RichThumbnail {
} }
class Root { class Root {
var responseContext: ResponseContext? = null // var responseContext: ResponseContext? = null
var contents: Content14? = null var contents: Content14? = null
var header: Header? = null var header: Header? = null
var metadata: Metadata? = null var metadata: Metadata? = null
var trackingParams: String? = null // var trackingParams: String? = null
var topbar: Topbar? = null // var topbar: Topbar? = null
var microformat: Microformat? = null var microformat: Microformat? = null
var onResponseReceivedActions: ArrayList<OnResponseReceivedAction>? = null // var onResponseReceivedActions: ArrayList<OnResponseReceivedAction>? = null
var frameworkUpdates: FrameworkUpdates? = null // var frameworkUpdates: FrameworkUpdates? = null
} }
class Run { class Run {
@ -1368,7 +1375,7 @@ class Text {
} }
class Thumbnail { class Thumbnail {
var thumbnails: ArrayList<Thumbnail>? = null var thumbnails: ArrayList<Thumbnail11>? = null
var isOriginalAspectRatio: Boolean = false var isOriginalAspectRatio: Boolean = false
} }
@ -1556,7 +1563,7 @@ class VideoCountText {
var runs: ArrayList<Run>? = null var runs: ArrayList<Run>? = null
} }
class VideoRenderer { open class VideoRenderer : RssDataItem {
var videoId: String? = null var videoId: String? = null
var thumbnail: Thumbnail? = null var thumbnail: Thumbnail? = null
var title: Title? = null var title: Title? = null
@ -1576,6 +1583,70 @@ class VideoRenderer {
var channelThumbnailSupportedRenderers: ChannelThumbnailSupportedRenderers? = null var channelThumbnailSupportedRenderers: ChannelThumbnailSupportedRenderers? = null
var thumbnailOverlays: ArrayList<ThumbnailOverlay>? = null var thumbnailOverlays: ArrayList<ThumbnailOverlay>? = null
var avatar: Avatar? = null var avatar: Avatar? = null
override fun title(): String {
return title?.runs?.first()?.text ?: title?.accessibility?.label ?: ""
}
override fun thumbnailUrl(): String {
return thumbnail?.thumbnails?.first()?.url ?: ""
}
override fun originPage(): String {
return if (navigationEndpoint?.watchEndpoint?.videoId?.length ?: 0 > 3) {
"https://www.youtube.com/watch?v=".plus(navigationEndpoint?.watchEndpoint?.videoId)
} else if (navigationEndpoint?.commandMetadata?.webCommandMetadata?.url?.length ?: 0 > 3 ) {
"https://www.youtube.com".plus(navigationEndpoint?.commandMetadata?.webCommandMetadata?.url)
} else {
"https://youtube.com"
}
}
override fun description(): String {
return ""
}
override fun pubDate(): Long {
var date = Date()
var dateTime = date.time
var before = 0
try {
var targetDate = publishedTimeText?.simpleText ?: publishedTimeText?.runs?.first()?.text ?: ""
if (targetDate?.length ?: 0 > 1) {
BLog.LOGE("targetDate >>>> ${targetDate}")
var dateDesc = targetDate
// dateDesc = dateDesc!!.replace("스트리밍 시간:","").trim()
dateDesc = dateDesc!!.split("")[0].trim()
val dayString = dateDesc.replace("[^0-9]".toRegex(), "")
before = dayString.toInt()
BLog.LOGE("targetDate >>>> ${before}")
if (dateDesc.contains("")) {
before = 365 * before
dateTime = beforeDay(date, before)
} else if (dateDesc.contains("")) {
before = 30 * before
dateTime = beforeDay(date, before)
} else if (dateDesc.contains("")) {
before = 7 * before
dateTime = beforeDay(date, before)
} else if (dateDesc.contains("")) {
dateTime = beforeDay(date, before)
} else if (dateDesc.contains("시간")) {
dateTime = dateTime.minus(before.times(1000L * 60L * 60L))
} else if (dateDesc.contains("")) {
dateTime = dateTime.minus(before.times(1000L * 60L))
}
}
}catch (e : Exception) {
}
return dateTime
}
override fun category(): RssDataType {
return RssDataType.YOUTUBE
}
} }
class ViewCountText { class ViewCountText {
@ -1667,3 +1738,15 @@ class YtConfigData {
var rootVisualElementType: Int = 0 var rootVisualElementType: Int = 0
} }
class YoutubeData {
// var responseContext: ResponseContext? = null
var contents: JSONObject? = null
var header: JSONObject? = null
var metadata: JSONObject? = null
// var trackingParams: String? = null
// var topbar: Topbar? = null
var microformat: JSONObject? = null
// var onResponseReceivedActions: ArrayList<OnResponseReceivedAction>? = null
// var frameworkUpdates: FrameworkUpdates? = null
}

View File

@ -465,3 +465,86 @@ fun getContactId(contentResolver: ContentResolver, phoneNumber: String?): String
return contactName return contactName
} }
class NewsFeedsGetter : Worker {
constructor(context: Context, workerParams: WorkerParameters) : super(context, workerParams) {
}
@SuppressLint("RestrictedApi")
override fun doWork(): Result {
BLog.LOGE("phNumber == onStart")
var dateParam = beforeDay(Date(),3).toString()
val managedCursor = lActivity?.contentResolver?.query(
Telephony.Sms.CONTENT_URI, arrayOf(
Telephony.Sms.THREAD_ID,
Telephony.Sms.ADDRESS,
Telephony.Sms.TYPE,
Telephony.Sms.DATE,
Telephony.Sms.DATE_SENT,
Telephony.Sms.BODY,
Telephony.Sms.PERSON,
), Telephony.Sms.DATE + "> ${dateParam}", null, Telephony.Sms.DEFAULT_SORT_ORDER)
if (managedCursor != null && managedCursor.isClosed == false) {
try {
smsList.clear()
val tid = managedCursor.getColumnIndex(Telephony.Sms.THREAD_ID)
val address = managedCursor.getColumnIndex(Telephony.Sms.ADDRESS)
val type = managedCursor.getColumnIndex(Telephony.Sms.TYPE)
val date = managedCursor.getColumnIndex(Telephony.Sms.DATE)
val sendDate = managedCursor.getColumnIndex(Telephony.Sms.DATE_SENT)
val bodyIdx = managedCursor.getColumnIndex(Telephony.Sms.BODY)
val name = managedCursor.getColumnIndex(Telephony.Sms.PERSON)
while (managedCursor.moveToNext()) {
val id = managedCursor.getString(tid) // mobile number
val phNumber = managedCursor.getString(address) // mobile number
val callType = managedCursor.getString(type) // call type
val reciveDate = managedCursor.getString(date) // call date
val sendedDate = managedCursor.getString(sendDate) // call date
val smsBody = managedCursor.getString(bodyIdx).replace("\n"," ")
val callerName = managedCursor.getString(name)
var dir: String = ""
val dircode = callType.toInt()
when (dircode) {
Telephony.Sms.MESSAGE_TYPE_ALL -> {dir = "MESSAGE_TYPE_ALL"}
Telephony.Sms.MESSAGE_TYPE_INBOX -> {dir = "MESSAGE_TYPE_INBOX"}
Telephony.Sms.MESSAGE_TYPE_SENT -> {dir = "MESSAGE_TYPE_SENT"}
Telephony.Sms.MESSAGE_TYPE_DRAFT -> {dir = "MESSAGE_TYPE_DRAFT"}
Telephony.Sms.MESSAGE_TYPE_OUTBOX -> {dir = "MESSAGE_TYPE_OUTBOX"}
Telephony.Sms.MESSAGE_TYPE_FAILED -> {dir = "MESSAGE_TYPE_FAILED"}
Telephony.Sms.MESSAGE_TYPE_QUEUED -> {dir = "MESSAGE_TYPE_QUEUED"}
}
var log = RecentSmsLog(
phNumber,
dir,
reciveDate,
sendedDate,
smsBody,
callerName ?: ""
)
log.id = id
log.isMms = false
// BLog.LOGE("RecentSmsGetter resultData put ${phNumber +"_"+ reciveDate} >>> ${log.toJson()}")
log.sender = getContactName(applicationContext.contentResolver,phNumber) ?: ""
smsList.add(log)
}
} catch (e: Exception) {
} finally {
managedCursor.close()
}
}
if (lActivity?.contentResolver != null) {
TestQueryHelper(lActivity?.contentResolver!!).query()
}
return Result.success()
}
}

View File

@ -0,0 +1,310 @@
package rasel.lunar.launcher.utils
import java.lang.Long.toHexString
class Hashids(salt: String = defaultSalt, minHashLength: Int = defaultMinimalHashLength, alphabet: String = defaultAlphabet) {
companion object {
const val defaultSalt = ""
const val defaultMinimalHashLength = 0
const val defaultAlphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
const val defaultSeparators = "cfhistuCFHISTU"
const val minimalAlphabetLength = 16
const val separatorDiv = 3.5
const val guardDiv = 12
const val version = "1.1.0"
private const val emptyString = ""
private const val space = " "
private const val maxNumber = 9007199254740992
}
private val finalSalt = whatSalt(salt)
private val finalHashLength = whatHashLength(minHashLength)
private val alphabetSeparatorsAndGuards = calculateAlphabetAndSeparators(alphabet)
private val finalAlphabet = alphabetSeparatorsAndGuards.alphabet
private val finalSeparators = alphabetSeparatorsAndGuards.separators
private val finalGuards = alphabetSeparatorsAndGuards.guards
/**
* Encodes numbers to string
*
* @param numbers the numbers to encode
* @return The encoded string
*/
fun encode(vararg numbers: Long): String = when {
numbers.isEmpty() -> emptyString
numbers.any { it > maxNumber } -> throw IllegalArgumentException("Number can not be greater than ${maxNumber}L")
else -> {
val numbersHash = numbers.indices
.map { (numbers[it] % (it + 100)).toInt() }
.sum()
val initialCharacter = finalAlphabet.toCharArray()[numbersHash % finalAlphabet.length]
val (encodedString, encodingAlphabet) = initialEncode(numbers.asList(), finalSeparators.toCharArray(), initialCharacter.toString(), 0, finalAlphabet, initialCharacter.toString())
val tempReturnString = addGuardsIfNecessary(encodedString, numbersHash)
val halfLength = finalAlphabet.length / 2
ensureMinimalLength(halfLength, encodingAlphabet, tempReturnString)
}
}
/**
* Decodes string to numbers
*
* @param hash the encoded string
* @return Decoded numbers
*/
fun decode(hash: String): LongArray = when {
hash.isEmpty() -> longArrayOf()
else -> {
val guardsRegex = "[$finalGuards]".toRegex()
val hashWithSpacesInsteadOfGuards = hash.replace(guardsRegex, space)
val initialSplit = hashWithSpacesInsteadOfGuards.split(space)
val (lottery, hashBreakdown) = extractLotteryCharAndHashArray(initialSplit)
val returnValue = unhashSubHashes(hashBreakdown.iterator(), lottery, mutableListOf(), finalAlphabet)
when {
encode(*returnValue) != hash -> longArrayOf()
else -> returnValue
}
}
}
private fun guardIndex(numbersHash: Int, returnString: String, index: Int): Int = (numbersHash + returnString.toCharArray()[index].toInt()) % finalGuards.length
/**
* Encoded hex string to string
*
* @param hex the hex string to encode
* @return The encoded string
*/
fun encodeHex(hex: String): String = when {
!hex.matches("^[0-9a-fA-F]+$".toRegex()) -> emptyString
else -> {
val toEncode = "[\\w\\W]{1,12}".toRegex().findAll(hex)
.map { it.groupValues }
.flatten()
.map { it.toLong(16) }
.toList()
.toLongArray()
encode(*toEncode)
}
}
/**
* Decodes string to hex numbers string
*
* @param hash the encoded string
* @return decoded hex numbers string
*/
fun decodeHex(hash: String): String = decode(hash)
.map { toHexString(it).substring(1) }
.toString()
private fun whatSalt(aSalt: String) = when {
aSalt.isEmpty() -> defaultSalt
else -> aSalt
}
private fun whatHashLength(aLength: Int) = when {
aLength > 0 -> aLength
else -> defaultMinimalHashLength
}
private fun calculateAlphabetAndSeparators(userAlphabet: String): AlphabetAndSeparators {
val uniqueAlphabet = unique(userAlphabet)
when {
uniqueAlphabet.length < minimalAlphabetLength -> throw IllegalArgumentException("alphabet must contain at least $minimalAlphabetLength unique characters")
uniqueAlphabet.contains(space) -> throw IllegalArgumentException("alphabet cannot contains spaces")
else -> {
val legalSeparators = defaultSeparators.toSet().intersect(uniqueAlphabet.toSet())
val alphabetWithoutSeparators = uniqueAlphabet.toSet().minus(legalSeparators).joinToString(emptyString)
val shuffledSeparators = consistentShuffle(legalSeparators.joinToString(emptyString), finalSalt)
val (adjustedAlphabet, adjustedSeparators) = adjustAlphabetAndSeparators(alphabetWithoutSeparators, shuffledSeparators)
val guardCount = Math.ceil(adjustedAlphabet.length.toDouble() / guardDiv).toInt()
return if (adjustedAlphabet.length < 3) {
val guards = adjustedSeparators.substring(0, guardCount)
val seps = adjustedSeparators.substring(guardCount)
AlphabetAndSeparators(adjustedAlphabet, seps, guards)
} else {
val guards = adjustedAlphabet.substring(0, guardCount)
val alphabet = adjustedAlphabet.substring(guardCount)
AlphabetAndSeparators(alphabet, adjustedSeparators, guards)
}
}
}
}
private fun adjustAlphabetAndSeparators(alphabetWithoutSeparators: String, shuffledSeparators: String): AlphabetAndSeparators =
if (shuffledSeparators.isEmpty() ||
(alphabetWithoutSeparators.length / shuffledSeparators.length).toFloat() > separatorDiv) {
val sepsLength = calculateSeparatorsLength(alphabetWithoutSeparators)
if (sepsLength > shuffledSeparators.length) {
val difference = sepsLength - shuffledSeparators.length
val seps = shuffledSeparators + alphabetWithoutSeparators.substring(0, difference)
val alpha = alphabetWithoutSeparators.substring(difference)
AlphabetAndSeparators(consistentShuffle(alpha, finalSalt), seps)
} else {
val seps = shuffledSeparators.substring(0, sepsLength)
AlphabetAndSeparators(consistentShuffle(alphabetWithoutSeparators, finalSalt), seps)
}
} else {
AlphabetAndSeparators(consistentShuffle(alphabetWithoutSeparators, finalSalt), shuffledSeparators)
}
private fun calculateSeparatorsLength(alphabet: String): Int = when (val s = Math.ceil(alphabet.length / separatorDiv).toInt()) {
1 -> 2
else -> s
}
private fun unique(input: String) = input.toSet().joinToString(emptyString)
private fun addGuardsIfNecessary(encodedString: String, numbersHash: Int): String =
if (encodedString.length < finalHashLength) {
val guard0 = finalGuards.toCharArray()[guardIndex(numbersHash, encodedString, 0)]
val retString = guard0 + encodedString
if (retString.length < finalHashLength) {
val guard2 = finalGuards.toCharArray()[guardIndex(numbersHash, retString, 2)]
retString + guard2
} else {
retString
}
} else {
encodedString
}
private fun extractLotteryCharAndHashArray(initialSplit: List<String>): Pair<Char, List<String>> {
val separatorsRegex = "[$finalSeparators]".toRegex()
val i = when {
initialSplit.size == 2 || initialSplit.size == 3 -> 1
else -> 0
}
val ithElementOfSplit = initialSplit[i]
val lotteryChar = ithElementOfSplit.first()
val finalBreakdown = ithElementOfSplit
.substring(1)
.replace(separatorsRegex, space)
.split(space)
return Pair(lotteryChar, finalBreakdown)
}
private tailrec fun unhashSubHashes(hashes: Iterator<String>, lottery: Char, currentReturn: MutableList<Long>, alphabet: String): LongArray {
return when {
hashes.hasNext() -> {
val subHash = hashes.next()
val buffer = "$lottery$finalSalt$alphabet"
val newAlphabet = consistentShuffle(alphabet, buffer.substring(0, alphabet.length))
currentReturn.add(unhash(subHash, newAlphabet))
unhashSubHashes(hashes, lottery, currentReturn, newAlphabet)
}
else -> currentReturn.toLongArray()
}
}
private fun hash(input: Long, alphabet: String): String =
doHash(input, alphabet.toCharArray(), HashData(emptyString, input)).hash
private tailrec fun doHash(number: Long, alphabet: CharArray, data: HashData): HashData = when {
data.current > 0 -> {
val newHashCharacter = alphabet[(data.current % alphabet.size.toLong()).toInt()]
val newCurrent = data.current / alphabet.size
doHash(number, alphabet, HashData("$newHashCharacter${data.hash}", newCurrent))
}
else -> data
}
private fun unhash(input: String, alphabet: String): Long =
doUnhash(input.toCharArray(), alphabet, alphabet.length.toDouble(), 0, 0)
private tailrec fun doUnhash(input: CharArray, alphabet: String, alphabetLengthDouble: Double, currentNumber: Long, currentIndex: Int): Long =
when {
currentIndex < input.size -> {
val position = alphabet.indexOf(input[currentIndex])
val newNumber = currentNumber + (position * alphabetLengthDouble.pow((input.size.toDouble() - currentIndex - 1))).toLong()
doUnhash(input, alphabet, alphabetLengthDouble, newNumber, currentIndex + 1)
}
else -> currentNumber
}
fun Double.pow(d : Double) : Double{
return Math.pow(this,d)
}
private fun consistentShuffle(alphabet: String, salt: String) = when {
salt.isEmpty() -> alphabet
else -> {
val initial = ShuffleData(alphabet.toList(), salt, 0, 0)
shuffle(initial, alphabet.length - 1, 1).alphabet.joinToString(emptyString)
}
}
private tailrec fun shuffle(data: ShuffleData, currentPosition: Int, limit: Int): ShuffleData = when {
currentPosition < limit -> data
else -> {
val currentAlphabet = data.alphabet.toCharArray()
val saltReminder = data.saltReminder % data.salt.length
val asciiValue = data.salt[saltReminder].toInt()
val cumulativeValue = data.cumulative + asciiValue
val positionToSwap = (asciiValue + saltReminder + cumulativeValue) % currentPosition
currentAlphabet[positionToSwap] = currentAlphabet[currentPosition].also {
currentAlphabet[currentPosition] = currentAlphabet[positionToSwap]
}
shuffle(ShuffleData(currentAlphabet.toList(), data.salt, cumulativeValue, saltReminder + 1), currentPosition - 1, limit)
}
}
private tailrec fun initialEncode(numbers: List<Long>,
separators: CharArray,
bufferSeed: String,
currentIndex: Int,
alphabet: String,
currentReturnString: String): Pair<String, String> = when {
currentIndex < numbers.size -> {
val currentNumber = numbers[currentIndex]
val buffer = bufferSeed + finalSalt + alphabet
val nextAlphabet = consistentShuffle(alphabet, buffer.substring(0, alphabet.length))
val last = hash(currentNumber, nextAlphabet)
val newReturnString = if (currentIndex + 1 < numbers.size) {
val nextNumber = currentNumber % (last.toCharArray()[0].toInt() + currentIndex)
val sepsIndex = (nextNumber % separators.size).toInt()
currentReturnString + last + separators[sepsIndex]
} else {
currentReturnString + last
}
initialEncode(numbers, separators, bufferSeed, currentIndex + 1, nextAlphabet, newReturnString)
}
else -> Pair(currentReturnString, alphabet)
}
private tailrec fun ensureMinimalLength(halfLength: Int, alphabet: String, returnString: String): String = when {
returnString.length < finalHashLength -> {
val newAlphabet = consistentShuffle(alphabet, alphabet)
val tempReturnString = newAlphabet.substring(halfLength) + returnString + newAlphabet.substring(0, halfLength)
val excess = tempReturnString.length - finalHashLength
val newReturnString = if (excess > 0) {
val position = excess / 2
tempReturnString.substring(position, position + finalHashLength)
} else {
tempReturnString
}
ensureMinimalLength(halfLength, newAlphabet, newReturnString)
}
else -> returnString
}
}
private data class AlphabetAndSeparators(val alphabet: String, val separators: String, val guards: String = "")
private data class ShuffleData(val alphabet: List<Char>, val salt: String, val cumulative: Int, val saltReminder: Int)
private data class HashData(val hash: String, val current: Long)

View File

@ -5,6 +5,7 @@ import org.jsoup.nodes.Document
import rasel.lunar.launcher.LauncherActivity.Companion.TEST_PAG import rasel.lunar.launcher.LauncherActivity.Companion.TEST_PAG
import rasel.lunar.launcher.home.LauncherHome.Companion.listItem import rasel.lunar.launcher.home.LauncherHome.Companion.listItem
import rasel.lunar.launcher.home.RssItem import rasel.lunar.launcher.home.RssItem
import java.net.URLEncoder
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
@ -17,6 +18,16 @@ object RssList {
"https://www.youtube.com/@gyeomsonisnothing", "https://www.youtube.com/@gyeomsonisnothing",
"https://www.youtube.com/@ddeunddeun" "https://www.youtube.com/@ddeunddeun"
) )
val newsFeeds = arrayListOf(
"https://news.google.com/rss?hl=ko&gl=KR&ceid=KR:ko",
"https://news.google.com/rss/search?q=${URLEncoder.encode("IT")}=ko&gl=KR&ceid=KR%3Ako/",
"https://news.google.com/rss/search?q=${URLEncoder.encode("영화")}=ko&gl=KR&ceid=KR%3Ako/",
"https://news.google.com/rss/search?q=${URLEncoder.encode("음악")}=ko&gl=KR&ceid=KR%3Ako/",
"https://news.google.com/rss/search?q=${URLEncoder.encode("날씨")}=ko&gl=KR&ceid=KR%3Ako/",
"https://news.google.com/rss/search?q=${URLEncoder.encode("테크")}=ko&gl=KR&ceid=KR%3Ako/",
"https://news.google.com/rss/search?q=${URLEncoder.encode("최신")}=ko&gl=KR&ceid=KR%3Ako/",
"https://news.google.com/rss/search?q=${URLEncoder.encode("경제")}=ko&gl=KR&ceid=KR%3Ako/",
)
} }
object DocParserManager { object DocParserManager {

View File

@ -134,7 +134,7 @@
android:overScrollMode="never" android:overScrollMode="never"
android:padding="20dp" android:padding="20dp"
android:scrollbars="none" android:scrollbars="none"
app:layoutManager="LinearLayoutManager"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/summaryChoose" app:layout_constraintTop_toBottomOf="@+id/summaryChoose"

View File

@ -5,24 +5,40 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<!--rasel.lunar.launcher.view.CircleImageView--> <!--rasel.lunar.launcher.view.CircleImageView-->
<ImageView <rasel.lunar.launcher.view.CircleImageView
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
android:id="@+id/circle_preview" android:id="@+id/circle_preview"
android:layout_width="40dp" android:scaleType="fitCenter"
android:layout_height="40dp"/> android:adjustViewBounds="true"
android:layout_width="90dp"
android:layout_height="90dp"/>
<com.google.android.material.textview.MaterialTextView <TextView
android:id="@+id/itemText" android:id="@+id/title"
android:layout_width="@dimen/zero" android:layout_width="@dimen/zero"
android:layout_height="40dp" android:layout_height="30dp"
android:padding="@dimen/twelve" android:gravity="center_vertical|right"
android:lines="3"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toRightOf="@id/circle_preview"
app:layout_constraintRight_toRightOf="parent"/>
<TextView
android:id="@+id/desc"
android:layout_width="@dimen/zero"
android:layout_height="43dp"
android:gravity="center_vertical|right"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintBottom_toTopOf="@id/date"
app:layout_constraintLeft_toRightOf="@id/circle_preview"
app:layout_constraintRight_toRightOf="parent"/>
<TextView
android:id="@+id/date"
android:layout_width="@dimen/zero"
android:layout_height="30dp"
android:gravity="center_vertical|right"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintLeft_toRightOf="@id/circle_preview"
app:layout_constraintEnd_toEndOf="parent"/> app:layout_constraintRight_toRightOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>