...
This commit is contained in:
parent
8f685f667b
commit
88dc8a3dc8
@ -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() {
|
||||||
|
|||||||
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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
|
||||||
|
}
|
||||||
186
app/src/main/kotlin/rasel/lunar/launcher/todos/RssFeedsParser.kt
Normal file
186
app/src/main/kotlin/rasel/lunar/launcher/todos/RssFeedsParser.kt
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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() {
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
}
|
||||||
@ -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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
310
app/src/main/kotlin/rasel/lunar/launcher/utils/HashUtils.kt
Normal file
310
app/src/main/kotlin/rasel/lunar/launcher/utils/HashUtils.kt
Normal 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)
|
||||||
@ -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 {
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -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>
|
||||||
Loading…
x
Reference in New Issue
Block a user