This commit is contained in:
lunaticbum 2024-08-30 14:43:42 +09:00
parent e9dff17966
commit 2e9900201e
13 changed files with 795 additions and 618 deletions

View File

@ -62,6 +62,8 @@
android:name=".LauncherActivity"
android:theme="@style/Theme.LunarLauncher.Starting"
android:launchMode="singleInstance"
android:screenOrientation="portrait"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|screenLayout|layoutDirection"
android:windowSoftInputMode="adjustResize"
android:exported="true">
<intent-filter>
@ -131,13 +133,13 @@
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
<receiver android:name=".home.EndCallReceiver"
<receiver android:name=".LauncherActivity$EndCallReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
</receiver>
<receiver android:name=".home.SMSReceiver"
<receiver android:name=".LauncherActivity$SMSReceiver"
android:exported="true"
android:permission="android.permission.BROADCAST_SMS">
<intent-filter>

View File

@ -20,19 +20,31 @@ package rasel.lunar.launcher
import android.Manifest
import android.appwidget.AppWidgetManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Color
import android.net.Uri
import android.net.http.SslError
import android.os.Build
import android.os.Bundle
import android.provider.CallLog
import android.provider.ContactsContract
import android.provider.Settings
import android.telephony.TelephonyManager
import android.util.Log
import android.view.View
import android.view.WindowInsets
import android.view.WindowManager
import android.webkit.JavascriptInterface
import android.webkit.SslErrorHandler
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
@ -44,8 +56,13 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import com.google.android.material.color.DynamicColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import rasel.lunar.launcher.apps.AppDrawer
import rasel.lunar.launcher.apps.DismissCalback
import rasel.lunar.launcher.apps.SearchMenu
@ -62,8 +79,20 @@ import rasel.lunar.launcher.helpers.Constants.Companion.widgetHostId
import rasel.lunar.launcher.helpers.UniUtils.Companion.getColorResId
import rasel.lunar.launcher.helpers.ViewPagerAdapter
import rasel.lunar.launcher.home.LauncherHome
import rasel.lunar.launcher.home.LauncherHome.Companion.lastedFinishedPageUrl
import rasel.lunar.launcher.home.LauncherHome.Companion.listItem
import rasel.lunar.launcher.home.LauncherHome.Companion.listTags
import rasel.lunar.launcher.home.RssItem
import rasel.lunar.launcher.home.RssTagItem
import rasel.lunar.launcher.utils.BLog
import rasel.lunar.launcher.utils.MissedCallGetter
import rasel.lunar.launcher.utils.NewsFeedsGetter
import rasel.lunar.launcher.utils.RecentSmsGetter
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
internal class LauncherActivity : AppCompatActivity() {
@ -74,15 +103,69 @@ internal class LauncherActivity : AppCompatActivity() {
companion object {
val TEST_PAG = "https://jav.guru/"
private var mWorkManager: WorkManager? = null
val TEST_PAG2 = "https://torrentsee246.com/topic/index?category1=129&category2=132"
val SMS_WORK_TAG = "RecentSmsGetter"
val CALL_WORK_TAG = "MissedCallGetter"
val FEDDS_WORK_TAG = "NewsFeedsGetter"
@JvmStatic var lActivity: LauncherActivity? = null
@JvmStatic var appWidgetManager: AppWidgetManager? = null
@JvmStatic var appWidgetHost: WidgetHost? = null
fun refreshSms() {
Executors.newSingleThreadScheduledExecutor().schedule({
mWorkManager?.cancelAllWorkByTag(SMS_WORK_TAG)
mWorkManager?.enqueueUniquePeriodicWork(
SMS_WORK_TAG,
ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<RecentSmsGetter>(30, TimeUnit.MINUTES)
.addTag(SMS_WORK_TAG)
.build())
}, 2, TimeUnit.SECONDS)
}
fun refreshCalls() {
Executors.newSingleThreadScheduledExecutor().schedule({
mWorkManager?.cancelAllWorkByTag(CALL_WORK_TAG)
mWorkManager?.enqueueUniquePeriodicWork(
CALL_WORK_TAG,
ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<MissedCallGetter>(30, TimeUnit.MINUTES)
.addTag(CALL_WORK_TAG)
.build())
}, 2, TimeUnit.SECONDS)
}
fun refreshFeeds() {
Executors.newSingleThreadScheduledExecutor().schedule({
mWorkManager?.cancelAllWorkByTag(FEDDS_WORK_TAG)
mWorkManager?.enqueueUniquePeriodicWork(
FEDDS_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
PeriodicWorkRequestBuilder<NewsFeedsGetter>(30, TimeUnit.MINUTES)
.addTag(FEDDS_WORK_TAG)
.build())
}, 2, TimeUnit.SECONDS)
}
fun workmanager() : WorkManager? {
if (mWorkManager == null && lActivity != null) {
mWorkManager = WorkManager.getInstance(lActivity!!)
}
return mWorkManager
}
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
binding.viewPager.invalidate()
binding.viewPager.post {
binding.viewPager?.adapter?.notifyDataSetChanged()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
mWorkManager = WorkManager.getInstance(this)
DynamicColors.applyToActivityIfAvailable(this)
settingsPrefs = getSharedPreferences(PREFS_SETTINGS, 0)
@ -105,6 +188,9 @@ internal class LauncherActivity : AppCompatActivity() {
/* handle navigation back events */
handleBackPress()
refreshSms()
refreshCalls()
refreshFeeds()
}
override fun onDestroy() {
@ -274,5 +360,175 @@ internal class LauncherActivity : AppCompatActivity() {
SearchMenu().show(supportFragmentManager,keyword) {dismissCalback?.invoke()}
}
class EndCallReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
BLog.LOGE("EndCallReceiver >>> ${intent}")
val phoneState = intent.getStringExtra(TelephonyManager.EXTRA_STATE) ?: return
if (phoneState == TelephonyManager.EXTRA_STATE_IDLE) {
refreshCalls()
}
}
}
class SMSReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
BLog.LOGE("SMSReceiver >>> ${intent}")
refreshSms()
}
}
fun doWebPare(url : String, callBack : (()->Unit)?) {
BLog.LOGE("binding.otherCheck before ThreadRun")
binding.searcher01.webViewClient = object : WebViewClient() {
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
BLog.LOGE("binding.otherCheck searcher01 in onPageStarted ${url}")
super.onPageStarted(view, url, favicon)
}
override fun onReceivedSslError(
view: WebView?,
handler: SslErrorHandler?,
error: SslError?
) {
handler?.proceed()
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
lastedFinishedPageUrl = url ?: ""
BLog.LOGE("binding.otherCheck searcher01 in onPageFinished ${url}")
if (url?.contains("youtube", false) == true) {
view?.evaluateJavascript(
"function getAll() {\n" +
" MyJavaScriptInterface.sendValueFromHtml(document.getElementsByTagName('html')[0].innerHTML)" +
" };getAll()"
) { result ->
(result as? String)?.let {
}
}
} else {
view?.evaluateJavascript(
"function getAll() {\n" +
" MyJavaScriptInterface.sendValueFromHtml(document.getElementsByTagName('html')[0].innerHTML)" +
" };getAll()"
) { result ->
(result as? String)?.let {
}
}
}
callBack?.invoke()
}
}
WebView.setWebContentsDebuggingEnabled(false)
binding.searcher01.apply {
this.addJavascriptInterface(MyJavaScriptInterface(this),"MyJavaScriptInterface")
setBackgroundColor(Color.WHITE) // 백그라운드 색상 설정
setLayerType(View.LAYER_TYPE_SOFTWARE, null) // 랜더링 이슈 해결
try {
settings.apply {
javaScriptEnabled = true // 자바스크립트 사용 가능하도록 설정
loadWithOverviewMode = true // 전체 웹페이지를 화면에 맞게 로드
useWideViewPort = true // 화면에 맞게 페이지 확대/축소
domStorageEnabled = true // DOM 저장소 사용 가능하도록 설정
setSupportMultipleWindows(true)
javaScriptCanOpenWindowsAutomatically = true // 팝업창 차단 해제
cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK
textZoom = 100 // system 에 의한 글꼴 변형 방지
defaultTextEncodingName = "UTF-8" // 인코딩 설정
allowContentAccess = true // 웹뷰를 통해 content url에 접근할지 여부
layoutAlgorithm = WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING // 웹페이지의 레이아웃을 화면에 맞게 자동으로 조정
}
} catch (ignore: NoSuchMethodError) {}
loadUrl(url) // 웹페이지 연결
}
}
var maxDate : Long = Long.MIN_VALUE
var minDate : Long = Long.MAX_VALUE
val simpldateFormat = SimpleDateFormat("d MMM, yy", Locale.US)
fun jGuruMain(doc: Document) {
BLog.LOGE("do default parsing")
BLog.LOGE("SimpleDateFormat D MM yy => ${SimpleDateFormat("d MMM yy", Locale.US).format(Date())}")
val prevUrl = doc.getElementsByClass("prev").get(0).getElementsByAttribute("href").get(0).attr("href")
BLog.LOGE("doc.getElementsByClass(prev).get(0).html() ${prevUrl}")
doc.getElementsByClass("column").forEach {
var title = it.getElementsByAttribute("title").get(0).text()
var model = title.replace("[","").split("]")[0]
var pageLink = it.getElementsByClass("imgg").get(0).getElementsByAttribute("href").get(0).attr("href")
var imgg = it.getElementsByClass("imgg").get(0).getElementsByAttribute("src").get(0).attr("src")
var tags = it.getElementsByClass("tags").get(0).text()
var date = it.getElementsByClass("date").get(0).text()
var regDate = simpldateFormat.parse(date).time
minDate = Math.min(minDate,regDate)
maxDate = Math.max(maxDate,regDate)
listItem.add(RssItem(model = model, title = title, pageLink = pageLink, image = imgg, tags = tags, date = simpldateFormat.parse(date).time))
}.apply {
BLog.LOGE("listItem.size >>> ${listItem.size}")
if (prevUrl!=null && prevUrl.length > TEST_PAG.length && prevUrl.contains("/page/") && (maxDate - minDate) < (1000L * 60L * 60L * 24L * 3L)) {
BLog.LOGE("listItem.size >>> ${listItem.size} do next ")
BLog.LOGE("saving data :: ${listItem.size}items ${simpldateFormat.format(Date(minDate))} ~ ${simpldateFormat.format(Date(maxDate))}")
binding.searcher01.postDelayed({binding.searcher01.loadUrl(prevUrl)}, 5000L)
} else {
listItem.sortByDescending { it.date }
BLog.LOGE("Stored data :: ${listItem.size}items ${simpldateFormat.format(Date(minDate))} ~ ${simpldateFormat.format(Date(maxDate))}")
Toast.makeText(this@LauncherActivity,
"Stored data :: ${listItem.size} items :: [${simpldateFormat.format(Date(minDate))} ~ ${simpldateFormat.format(Date(maxDate))}]", Toast.LENGTH_LONG).show()
}
}
}
fun jGuruTag(doc: Document) {
listTags.clear()
doc.getElementsByTag("ul").forEach {
it.children().forEach {
if (it.tag().name.contains("li")) {
listTags.add(
RssTagItem(
tagTitle = it.getElementsByTag("a").get(0).text(),
link = it.getElementsByTag("a").get(0).attr("href")
)
)
}
}
}.apply {
listTags.sortByDescending { it.count }
Toast.makeText(this@LauncherActivity,
"Stored data :: ${listTags.size}tags", Toast.LENGTH_SHORT).show()
}
}
inner class MyJavaScriptInterface(val webView: WebView) {
@JavascriptInterface
fun sendValueFromHtml(result: String) {
if (lastedFinishedPageUrl.contains(TEST_PAG)) {
var htmlString = result.replace("\\u003","<")
val doc: Document = Jsoup.parse(htmlString)
if (lastedFinishedPageUrl?.contains("page") == true || lastedFinishedPageUrl?.equals(
TEST_PAG
) == true
) {
jGuruMain(doc)
} else if (lastedFinishedPageUrl?.contains("/tags/") == true) {
jGuruTag(doc)
}
} else if (lastedFinishedPageUrl?.contains("youtube") == true) {
// val doc: Document = Jsoup.parse(result)
// ytChannel(doc)
}
BLog.LOGE("binding.otherCheck after ThreadRun")
}
}
}
}

View File

@ -22,41 +22,36 @@ import android.R.attr.*
import android.app.Activity.RESULT_CANCELED
import android.app.Activity.RESULT_OK
import android.appwidget.AppWidgetManager
import android.content.DialogInterface
import android.content.Intent
import android.content.SharedPreferences
import android.os.*
import android.view.*
import android.widget.EditText
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.LinearLayoutCompat.LayoutParams
import androidx.appcompat.widget.PopupMenu
import androidx.core.app.JobIntentService.enqueueWork
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.button.MaterialButtonToggleGroup
import kotlinx.coroutines.*
import rasel.lunar.launcher.LauncherActivity.Companion.TEST_PAG
import rasel.lunar.launcher.LauncherActivity.Companion.appWidgetHost
import rasel.lunar.launcher.LauncherActivity.Companion.appWidgetManager
import rasel.lunar.launcher.LauncherActivity.Companion.lActivity
import rasel.lunar.launcher.R
import rasel.lunar.launcher.databinding.FeedsBinding
import rasel.lunar.launcher.feeds.rss.Rss
import rasel.lunar.launcher.feeds.rss.RssAdapter
import rasel.lunar.launcher.feeds.rss.RssService
import rasel.lunar.launcher.helpers.Constants.Companion.KEY_RSS_URL
import rasel.lunar.launcher.helpers.Constants.Companion.KEY_RSS_URL2
import rasel.lunar.launcher.helpers.Constants.Companion.KEY_WIDGET_HEIGHTS
import rasel.lunar.launcher.helpers.Constants.Companion.KEY_WIDGET_IDS
import rasel.lunar.launcher.helpers.Constants.Companion.PREFS_SETTINGS
import rasel.lunar.launcher.helpers.Constants.Companion.PREFS_WIDGETS
import rasel.lunar.launcher.helpers.Constants.Companion.RSS_ITEMS
import rasel.lunar.launcher.helpers.Constants.Companion.RSS_RECEIVER
import rasel.lunar.launcher.helpers.Constants.Companion.SEPARATOR
import rasel.lunar.launcher.helpers.Constants.Companion.requestCreateWidget
import rasel.lunar.launcher.helpers.Constants.Companion.requestPickWidget
import rasel.lunar.launcher.helpers.Constants.Companion.rssJobId
import rasel.lunar.launcher.helpers.UniUtils.Companion.isNetworkAvailable
import rasel.lunar.launcher.home.LauncherHome.Companion.listItem
import java.util.*
@ -64,18 +59,18 @@ internal class Feeds : Fragment() {
private lateinit var binding: FeedsBinding
private val requestCodeString = "requestCode"
var mRssAdapter : RssAdapter? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = FeedsBinding.inflate(inflater, container, false)
mRssAdapter = RssAdapter(listItem, requireContext())
binding.feedsRss.rss.adapter = mRssAdapter
updateWidgets()
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.feedsRss.rss.adapter = RssAdapter(arrayListOf(), requireContext())
expandCollapse()
systemInfo()
}
@ -88,6 +83,8 @@ internal class Feeds : Fragment() {
override fun onPause() {
super.onPause()
unregisterForContextMenu(binding.widgetContainer)
if (binding.expandRss.isChecked)
binding.expandRss.isChecked = false
}
override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
@ -125,65 +122,118 @@ internal class Feeds : Fragment() {
}
}
/* start rss service if network is active and rss url is not empty */
/* start rss service if network is active and rss url is not empty */
private fun startService() {
binding.feedsRss.apply {
if(rss.adapter != null) {
(rss.adapter as RssAdapter).items.clear()
}
}
val rssUrl = lActivity!!.getSharedPreferences(PREFS_SETTINGS, 0)
.getString(KEY_RSS_URL, "")
when {
isNetworkAvailable && !rssUrl.isNullOrEmpty() -> {
Intent(lActivity!!, RssService::class.java)
.putExtra(RSS_RECEIVER, resultReceiver).let {
enqueueWork(lActivity!!, RssService::class.java, rssJobId, it)
binding.feedsRss.rss.visibility = View.GONE
binding.feedsRss.loading.visibility = View.VISIBLE
binding.feedsRss.refresh.visibility = View.VISIBLE
val builder: AlertDialog.Builder = AlertDialog.Builder(requireContext())
builder.setTitle("Title")
val viewInflated: View = LayoutInflater.from(context)
.inflate(R.layout.text_inpu_password, view as ViewGroup?, false)
val input = viewInflated.findViewById<View>(R.id.input) as EditText
builder.setView(viewInflated)
builder.setPositiveButton(android.R.string.ok,
DialogInterface.OnClickListener { dialog, which ->
dialog.dismiss()
when(input.text.toString()) {
"jJguru","vVioPup*383v" -> {
mRssAdapter?.updateData(listItem)
binding.feedsRss.apply {
loading.visibility = View.VISIBLE
if (listItem.size > 0) {
rss.visibility = View.VISIBLE
loading.visibility = View.GONE
} else {
refresh.visibility = View.VISIBLE
rss.visibility = View.GONE
refresh.setOnClickListener {
lActivity?.doWebPare(TEST_PAG) {
if (listItem.size > 0) {
mRssAdapter?.updateData(listItem)
rss?.post {
loading.visibility = View.GONE
refresh.visibility = View.GONE
rss.visibility = View.VISIBLE
}
}
}
}
}
}
}
}
else -> resumeService()
}
val rssUrl2 = lActivity!!.getSharedPreferences(PREFS_SETTINGS, 0)
.getString(KEY_RSS_URL2, "")
when {
isNetworkAvailable && !rssUrl2.isNullOrEmpty() -> {
Intent(lActivity!!, RssService::class.java)
.putExtra(RSS_RECEIVER, resultReceiver).let {
enqueueWork(lActivity!!, RssService::class.java, rssJobId, it)
"jJTag"-> {
// lActivity?.doWebPare(TEST_PAG.plus("tags"))
}
}
else -> resumeService()
}
else -> {
binding.expandRss.isChecked = false
}
}
})
builder.setNegativeButton(android.R.string.cancel,
DialogInterface.OnClickListener { dialog, which -> dialog.cancel() })
builder.show()
// binding.feedsRss.apply {
// if(rss.adapter != null) {
// (rss.adapter as RssAdapter).items.clear()
// }
// }
// val rssUrl = lActivity!!.getSharedPreferences(PREFS_SETTINGS, 0)
// .getString(KEY_RSS_URL, "")
// when {
// isNetworkAvailable && !rssUrl.isNullOrEmpty() -> {
//// Intent(lActivity!!, RssService::class.java)
//// .putExtra(RSS_RECEIVER, resultReceiver).let {
//// enqueueWork(lActivity!!, RssService::class.java, rssJobId, it)
//// }
// }
// else -> resumeService()
// }
// val rssUrl2 = lActivity!!.getSharedPreferences(PREFS_SETTINGS, 0)
// .getString(KEY_RSS_URL2, "")
// when {
// isNetworkAvailable && !rssUrl2.isNullOrEmpty() -> {
// Intent(lActivity!!, RssService::class.java)
// .putExtra(RSS_RECEIVER, resultReceiver).let {
// enqueueWork(lActivity!!, RssService::class.java, rssJobId, it)
// }
// }
// else -> resumeService()
// }
}
/* retry to start rss service */
/* retry to start rss service */
private fun resumeService() {
binding.feedsRss.apply {
rss.visibility = View.GONE
loading.visibility = View.GONE
refresh.visibility = View.VISIBLE
refresh.setOnClickListener { startService() }
refresh.setOnClickListener {
mRssAdapter?.notifyDataSetChanged()
}
}
}
/* rss service's result receiver */
/* rss service's result receiver */
@Suppress("UNCHECKED_CAST")
private val resultReceiver: ResultReceiver = object : ResultReceiver(Handler(Looper.getMainLooper())) {
override fun onReceiveResult(resultCode: Int, resultData: Bundle) {
when (val items = resultData.getSerializable(RSS_ITEMS) as List<Rss>?) {
null -> resumeService()
else -> {
binding.feedsRss.apply {
if(rss.adapter != null) {
(rss.adapter as RssAdapter).items.addAll(items)
}
refresh.visibility = View.GONE
loading.visibility = View.GONE
rss.visibility = View.VISIBLE
}
}
}
}
// override fun onReceiveResult(resultCode: Int, resultData: Bundle) {
// when (val items = resultData.getSerializable(RSS_ITEMS) as List<Rss>?) {
// null -> resumeService()
// else -> {
// binding.feedsRss.apply {
// if(rss.adapter != null) {
// (rss.adapter as RssAdapter).items.addAll(items)
// }
// refresh.visibility = View.GONE
// loading.visibility = View.GONE
// rss.visibility = View.VISIBLE
// }
// }
// }
// }
}
private fun systemInfo() {

View File

@ -225,7 +225,7 @@ internal class SystemStats {
currentFreq = curFreq.toDouble() / 1000
readerCurFreq.close()
currentFReq = currentFreq.toInt()
println("$currentFReq----------------------------------------------------")
// println("$currentFReq----------------------------------------------------")
} catch (ex: java.lang.Exception) {
ex.printStackTrace()
}

View File

@ -23,63 +23,103 @@ import android.view.ViewGroup
import android.view.LayoutInflater
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.view.Gravity
import androidx.core.content.ContextCompat
import android.graphics.Typeface
import android.util.TypedValue
import android.content.res.ColorStateList
import android.net.Uri
import android.view.View
import androidx.browser.customtabs.CustomTabsIntent
import androidx.recyclerview.widget.DiffUtil
import com.squareup.picasso.Picasso
import rasel.lunar.launcher.LauncherActivity.Companion.lActivity
import rasel.lunar.launcher.databinding.ListItemBinding
import rasel.lunar.launcher.databinding.ListItemWithBinding
import rasel.lunar.launcher.helpers.UniUtils.Companion.getColorResId
import rasel.lunar.launcher.home.RssItem
import rasel.lunar.launcher.todos.RssDataItem
import rasel.lunar.launcher.todos.RssItemDiffUtil
import java.net.URLEncoder
import java.nio.charset.Charset
import java.text.SimpleDateFormat
import java.util.Date
internal class RssAdapter(var items: ArrayList<Rss> = arrayListOf(), private val context: Context) :
internal class RssAdapter(var items: ArrayList<RssItem> = arrayListOf(), private val context: Context) :
RecyclerView.Adapter<RssAdapter.RssViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RssViewHolder {
val binding = ListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
val binding = ListItemWithBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return RssViewHolder(binding)
}
override fun getItemCount(): Int = items.size
val dateFormat = SimpleDateFormat("hh:mm / yy - MM - dd")
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: RssViewHolder, position: Int) {
val item = items[position]
/* customize the first item */
if (position == 0) {
holder.view.itemText.apply {
text = "\u22B6 " + items[position].title + " \u22B7"
gravity = Gravity.CENTER
setTextColor(ContextCompat.getColor(context,
getColorResId(context, com.google.android.material.R.attr.colorPrimary)))
setTypeface(null, Typeface.BOLD)
textSize = 20f
}
/* reset customization for rest */
} else {
holder.view.itemText.apply {
text = items[position].title
gravity = holder.gravity
setTextColor(holder.color)
typeface = holder.typeface
setTextSize(TypedValue.COMPLEX_UNIT_PX, holder.size)
}
// if (position == 0) {
holder.view.title.apply {
text = item.title
}
holder.view.date.text = dateFormat.format(Date(item.date))
holder.view.desc.text = item.tags.plus("\n").plus(item.model)
holder.view.circlePreview.visibility = View.GONE
/* reset customization for rest */
// } else {
// holder.view.desc.apply {
// text = items[position].title
//// gravity = holder.gravity
//// setTextColor(holder.color)
//// typeface = holder.typeface
//// setTextSize(TypedValue.COMPLEX_UNIT_PX, holder.size)
// }
// }
Picasso.get().load(item.image).into(holder.view.circlePreview)
holder.view.root.setOnClickListener {
holder.view.circlePreview.visibility = View.VISIBLE
holder.view.circlePreview.postDelayed({
holder.view.circlePreview.visibility = View.GONE
},500L)
}
/* on click - open in browser */
holder.view.itemText.setOnClickListener {
val customTabsIntent = CustomTabsIntent.Builder().setUrlBarHidingEnabled(true).build()
customTabsIntent.launchUrl(context, Uri.parse(items[position].link))
holder.view.root.setOnLongClickListener {
openOpera("https://cili.site/search?q=${URLEncoder.encode(item.model, Charset.defaultCharset().name())}")
openOpera(item.pageLink)
true
}
}
inner class RssViewHolder(var view: ListItemBinding) : RecyclerView.ViewHolder(view.root) {
/* store previous styles for resetting */
var gravity: Int = view.itemText.gravity
var color: ColorStateList = view.itemText.textColors
var typeface: Typeface = view.itemText.typeface
var size: Float = view.itemText.textSize
fun openOpera(schemeString : String) {
val gmmIntentUri = Uri.parse(schemeString)
val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri)
mapIntent.setPackage("com.opera.browser")
lActivity?.startActivity(mapIntent)
}
fun updateData(newList: List<RssItem>) {
DiffUtil.calculateDiff(RssItemDiffUtil(items, newList)).dispatchUpdatesTo(this)
}
inner class RssViewHolder(var view: ListItemWithBinding) : RecyclerView.ViewHolder(view.root) {
}
}
internal class RssItemDiffUtil(
private val oldList: List<RssItem>, private val newList: List<RssItem>
) : DiffUtil.Callback() {
override fun getOldListSize(): Int = oldList.size
override fun getNewListSize(): Int = newList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
oldList[oldItemPosition].pageLink == newList[newItemPosition].pageLink
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
oldList[oldItemPosition].pageLink == newList[newItemPosition].pageLink
}

View File

@ -48,6 +48,7 @@ import androidx.core.content.ContextCompat.RECEIVER_EXPORTED
import androidx.core.content.ContextCompat.registerReceiver
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
@ -59,8 +60,12 @@ import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserFactory
import rasel.lunar.launcher.LauncherActivity.Companion.CALL_WORK_TAG
import rasel.lunar.launcher.LauncherActivity.Companion.FEDDS_WORK_TAG
import rasel.lunar.launcher.LauncherActivity.Companion.SMS_WORK_TAG
import rasel.lunar.launcher.LauncherActivity.Companion.TEST_PAG
import rasel.lunar.launcher.LauncherActivity.Companion.lActivity
import rasel.lunar.launcher.LauncherActivity.Companion.workmanager
import rasel.lunar.launcher.R
import rasel.lunar.launcher.databinding.LauncherHomeBinding
import rasel.lunar.launcher.helpers.Constants.Companion.BOTTOM_SHEET_TAG
@ -74,8 +79,6 @@ import rasel.lunar.launcher.helpers.UniUtils.Companion.biometricPromptInfo
import rasel.lunar.launcher.helpers.UniUtils.Companion.canAuthenticate
import rasel.lunar.launcher.helpers.UniUtils.Companion.expandNotificationPanel
import rasel.lunar.launcher.helpers.UniUtils.Companion.lockMethod
import rasel.lunar.launcher.home.LauncherHome.Companion.refreshCalls
import rasel.lunar.launcher.home.LauncherHome.Companion.refreshSms
import rasel.lunar.launcher.home.weather.WeatherExecutor
import rasel.lunar.launcher.qaccess.QuickAccess
import rasel.lunar.launcher.settings.SettingsActivity
@ -90,10 +93,12 @@ import rasel.lunar.launcher.todos.SmsLogsAdapter
import rasel.lunar.launcher.todos.TwoColumnBrowseResultsRenderer
import rasel.lunar.launcher.utils.BLog
import rasel.lunar.launcher.utils.MissedCallGetter
import rasel.lunar.launcher.utils.NewsFeedsGetter
import rasel.lunar.launcher.utils.RecentSmsGetter
import rasel.lunar.launcher.utils.RecentSmsLog
import rasel.lunar.launcher.utils.RssList
import rasel.lunar.launcher.utils.SimpleFingerGestures
import rasel.lunar.launcher.utils.beforeDay
import java.io.ByteArrayInputStream
import java.io.InputStream
import java.nio.charset.Charset
@ -113,10 +118,8 @@ internal class LauncherHome : Fragment() {
private lateinit var batteryReceiver: BatteryReceiver
private var shouldResume = true
companion object {
val SMS_WORK_TAG = "RecentSmsGetter"
val CALL_WORK_TAG = "MissedCallGetter"
var lastedFinishedPageUrl : String = ""
private var mWorkManager: WorkManager? = null
var missedCalls = hashMapOf<String, MissedCall>()
var callList = arrayListOf<MissedCall>()
@ -124,44 +127,44 @@ internal class LauncherHome : Fragment() {
var listTags = arrayListOf<RssTagItem>()
var listItem = arrayListOf<RssItem>()
var rssSet = hashMapOf<String,RssDataItem>()
var rssUrls = arrayListOf<String>()
var feddsUrls = arrayListOf<String>()
var rssList = arrayListOf<RssDataItem>()
fun refreshSms() {
Executors.newSingleThreadScheduledExecutor().schedule({
mWorkManager?.cancelAllWorkByTag(SMS_WORK_TAG)
mWorkManager?.enqueue(PeriodicWorkRequestBuilder<RecentSmsGetter>(30, TimeUnit.MINUTES).addTag(SMS_WORK_TAG).build())
}, 2, TimeUnit.SECONDS)
}
fun refreshCalls() {
Executors.newSingleThreadScheduledExecutor().schedule({
mWorkManager?.cancelAllWorkByTag(CALL_WORK_TAG)
mWorkManager?.enqueueUniquePeriodicWork(CALL_WORK_TAG,ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,PeriodicWorkRequestBuilder<MissedCallGetter>(30, TimeUnit.MINUTES).addTag("MissedCallGetter").build())
}, 2, TimeUnit.SECONDS)
}
}
private var nReceiver: NotificationReceiver? = null
class NotificationReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
// val temp = """
// ${intent.getStringExtra("notification_event")}
//// ${txtView.getText()}
// """.trimIndent()
// txtView.setText(temp)
BLog.LOGE("NotificationReceiver >>>> ${intent.extras?.keySet()}")
}
}
fun workmanager() : WorkManager? {
if (mWorkManager == null) {
mWorkManager = WorkManager.getInstance(requireContext())
}
return mWorkManager
}
val UPDATE_DELAY = 500L
val commandHandler = Handler(Looper.getMainLooper())
val smsUpdate = Runnable {
BLog.LOGE("observeForever smsList.size >>> ${smsList.size}")
binding.recentSms.text = "최근 문자 [${smsList.size}]"
chooseAdpater()
}
val callUpdate = Runnable {
binding.missedCalls.text = "최근 통화 [${missedCalls.size}]"
BLog.LOGE("observeForever missedCalls.size >>> ${missedCalls.size}")
chooseAdpater()
}
val infoUpdate = Runnable {
binding.otherCheck.text = "최근 정보[${rssSet.size}]"
BLog.LOGE("observeForever rssSet.size >>> ${rssSet.size}")
chooseAdpater()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
mWorkManager = WorkManager.getInstance(requireContext())
binding = LauncherHomeBinding.inflate(inflater, container, false)
fragManager = lActivity!!.supportFragmentManager
settingsPrefs = requireContext().getSharedPreferences(PREFS_SETTINGS, 0)
@ -169,35 +172,45 @@ internal class LauncherHome : Fragment() {
mMissedCallsAdapter = MissedCallsAdapter(callList, requireContext())
mSmsLogsAdapter = SmsLogsAdapter(smsList, requireContext())
mRssAdapter = RssItemAdapter(rssList, requireContext())
binding.favAppsGroup.visibility = View.GONE
binding.mainList.layoutManager = LinearLayoutManagerWrapper(requireContext())
binding.mainList.adapter = mMissedCallsAdapter
binding.smsList.adapter = mSmsLogsAdapter
binding.infoList.adapter = mRssAdapter
binding.missedCalls.isChecked = true
binding.smsList.visibility = View.GONE
binding.infoList.visibility = View.GONE
binding.favAppsGroup.visibility = View.GONE
binding.mainList.layoutManager = LinearLayoutManager(requireContext())
binding.smsList.layoutManager = LinearLayoutManager(requireContext())
binding.infoList.layoutManager = LinearLayoutManager(requireContext())
workmanager()?.getWorkInfosByTagLiveData(SMS_WORK_TAG)?.observeForever {
binding.recentSms.text = "최근 문자 [${smsList.size}]"
if (binding.recentSms.isChecked) chooseAdpater()
else if (binding.missedCalls.isChecked && missedCalls.size == 0) {
binding.recentSms.isChecked = true
chooseAdpater()
}
BLog.LOGE("smsList.size >>> ${smsList.size}")
commandHandler.removeCallbacks(smsUpdate)
commandHandler.postDelayed(smsUpdate,UPDATE_DELAY)
it.clear()
}
workmanager()?.getWorkInfosByTagLiveData(CALL_WORK_TAG)?.observeForever {
binding.missedCalls.text = "최근 통화 [${missedCalls.size}]"
if (binding.recentSms.isChecked && missedCalls.size > 0) binding.missedCalls.isChecked = true
if (binding.missedCalls.isChecked) chooseAdpater()
commandHandler.removeCallbacks(callUpdate)
commandHandler.postDelayed(callUpdate,UPDATE_DELAY)
it.clear()
}
workmanager()?.getWorkInfosByTagLiveData(FEDDS_WORK_TAG)?.observeForever {
commandHandler.removeCallbacks(infoUpdate)
commandHandler.postDelayed(infoUpdate,UPDATE_DELAY)
it.clear()
}
nReceiver = NotificationReceiver()
val filter = IntentFilter()
// filter.addAction("com.kpbird.nlsexample.NOTIFICATION_LISTENER_EXAMPLE")
registerReceiver(requireContext(),nReceiver, filter,RECEIVER_EXPORTED)
BLog.LOGE("onCreateView()")
refreshSms()
refreshCalls()
return binding.root
}
@ -238,178 +251,42 @@ internal class LauncherHome : Fragment() {
chooseAdpater()
}
binding.otherCheck.setOnClickListener {
if(rssSet.size < 10) {
rssSet.clear()
feddsUrls.clear()
feddsUrls.addAll(RssList.newsFeeds)
rssUrls.clear()
rssUrls.addAll(RssList.youtubeUrls)
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.isChecked = true
chooseAdpater()
}
binding.otherCheck.setOnLongClickListener {
listItem.clear()
doWebPare(TEST_PAG)
lActivity?.doWebPare(TEST_PAG, null)
true
}
binding.summaryChoose.setOnCheckedChangeListener { group, checkedId ->
chooseAdpater()
}
// val i = Intent("com.kpbird.nlsexample.NOTIFICATION_LISTENER_SERVICE_EXAMPLE")
// i.putExtra("command", "list")
// lActivity?.sendBroadcast(i)
// BLog.LOGE("intent >>> ${i.action}")
// binding.summaryChoose.setOnCheckedChangeListener { group, checkedId ->
// chooseAdpater()
// }
//// val i = Intent("com.kpbird.nlsexample.NOTIFICATION_LISTENER_SERVICE_EXAMPLE")
//// i.putExtra("command", "list")
//// lActivity?.sendBroadcast(i)
//// 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) {
if (url.contains("youtube")) {
Executors.newSingleThreadScheduledExecutor().schedule( {
Jsoup.connect(url).get()?.apply {
ytChannel(this)
}
},1,TimeUnit.SECONDS)
return
}
BLog.LOGE("binding.otherCheck before ThreadRun")
binding.searcher01.webViewClient = object : WebViewClient() {
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
BLog.LOGE("binding.otherCheck searcher01 in onPageStarted ${url}")
super.onPageStarted(view, url, favicon)
}
override fun onReceivedSslError(
view: WebView?,
handler: SslErrorHandler?,
error: SslError?
) {
handler?.proceed()
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
lastedFinishedPageUrl = url ?: ""
BLog.LOGE("binding.otherCheck searcher01 in onPageFinished ${url}")
if (url?.contains("youtube", false) == true) {
view?.evaluateJavascript(
"function getAll() {\n" +
" MyJavaScriptInterface.sendValueFromHtml(document.getElementsByTagName('html')[0].innerHTML)" +
" };getAll()"
) { result ->
(result as? String)?.let {
}
}
} else {
view?.evaluateJavascript(
"function getAll() {\n" +
" MyJavaScriptInterface.sendValueFromHtml(document.getElementsByTagName('html')[0].innerHTML)" +
" };getAll()"
) { result ->
(result as? String)?.let {
}
}
}
}
}
WebView.setWebContentsDebuggingEnabled(false)
binding.searcher01.apply {
this.addJavascriptInterface(MyJavaScriptInterface(this),"MyJavaScriptInterface")
setBackgroundColor(Color.WHITE) // 백그라운드 색상 설정
setLayerType(View.LAYER_TYPE_SOFTWARE, null) // 랜더링 이슈 해결
try {
settings.apply {
javaScriptEnabled = true // 자바스크립트 사용 가능하도록 설정
loadWithOverviewMode = true // 전체 웹페이지를 화면에 맞게 로드
useWideViewPort = true // 화면에 맞게 페이지 확대/축소
domStorageEnabled = true // DOM 저장소 사용 가능하도록 설정
setSupportMultipleWindows(true)
javaScriptCanOpenWindowsAutomatically = true // 팝업창 차단 해제
cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK
textZoom = 100 // system 에 의한 글꼴 변형 방지
defaultTextEncodingName = "UTF-8" // 인코딩 설정
allowContentAccess = true // 웹뷰를 통해 content url에 접근할지 여부
layoutAlgorithm = WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING // 웹페이지의 레이아웃을 화면에 맞게 자동으로 조정
}
} catch (ignore: NoSuchMethodError) {}
loadUrl(url) // 웹페이지 연결
}
}
fun chooseAdpater () {
binding.mainList.visibility = View.GONE
binding.smsList.visibility = View.GONE
binding.infoList.visibility = View.GONE
if (binding.missedCalls.isChecked) {
if (missedCalls.size > 0 && isAdded && isResumed && isVisible) {
try {
callList.clear()
if(binding.mainList.adapter is MissedCallsAdapter){
}else {
binding.mainList.adapter = mMissedCallsAdapter
}
missedCalls.forEach { t, u ->
callList.add(u)
}.apply {
callList.sortByDescending { it.date }
Handler(Looper.getMainLooper()).post {mMissedCallsAdapter.updateData(callList)}
Handler(Looper.getMainLooper()).post {
mMissedCallsAdapter.updateData(callList)
binding.mainList.visibility = View.VISIBLE
}
}
} catch (e : Exception) {
}
@ -417,17 +294,32 @@ internal class LauncherHome : Fragment() {
} else if(binding.recentSms.isChecked){
if (smsList.size > 0 && isAdded && isResumed && isVisible) {
try {
if(binding.mainList.adapter is SmsLogsAdapter){
}else {
binding.mainList.adapter = mSmsLogsAdapter
}
smsList.sortByDescending { it.rcvDate }
Handler(Looper.getMainLooper()).post {mSmsLogsAdapter.updateData(smsList)}
binding.smsList.visibility = View.VISIBLE
Handler(Looper.getMainLooper()).post {
mSmsLogsAdapter.updateData(smsList)
binding.smsList.visibility = View.VISIBLE
}
} catch (e : Exception) {
}
}
} else if(binding.otherCheck.isChecked) {
try {
rssList.clear()
rssSet.forEach { k,v ->
if(v.pubDate() > beforeDay(Date(),3)) {
rssList.add(v)
} else {
rssSet.remove(k,v)
}
}.apply {
Handler(Looper.getMainLooper()).post {
mRssAdapter.updateData(rssList)
binding.infoList.visibility = View.VISIBLE
}
}
}catch (e : Exception){}
}
}
override fun onResume() {
@ -448,7 +340,6 @@ internal class LauncherHome : Fragment() {
}
/* show weather */
WeatherExecutor(settingsPrefs).generateWeatherString(binding.weather)
chooseAdpater()
}
}
@ -599,90 +490,7 @@ internal class LauncherHome : Fragment() {
}
}
var maxDate : Long = Long.MIN_VALUE
var minDate : Long = Long.MAX_VALUE
val simpldateFormat = SimpleDateFormat("d MMM, yy",Locale.US)
fun jGuruMain(doc:Document) {
BLog.LOGE("do default parsing")
BLog.LOGE("SimpleDateFormat D MM yy => ${SimpleDateFormat("d MMM yy",Locale.US).format(Date())}")
val prevUrl = doc.getElementsByClass("prev").get(0).getElementsByAttribute("href").get(0).attr("href")
BLog.LOGE("doc.getElementsByClass(prev).get(0).html() ${prevUrl}")
doc.getElementsByClass("column").forEach {
var title = it.getElementsByAttribute("title").get(0).text()
var model = title.replace("[","").split("]")[0]
var pageLink = it.getElementsByClass("imgg").get(0).getElementsByAttribute("href").get(0).attr("href")
var imgg = it.getElementsByClass("imgg").get(0).getElementsByAttribute("src").get(0).attr("src")
var tags = it.getElementsByClass("tags").get(0).text()
var date = it.getElementsByClass("date").get(0).text()
var regDate = simpldateFormat.parse(date).time
minDate = Math.min(minDate,regDate)
maxDate = Math.max(maxDate,regDate)
listItem.add(RssItem(model = model, title = title, pageLink = pageLink, image = imgg, tags = tags, date = simpldateFormat.parse(date).time))
}.apply {
BLog.LOGE("listItem.size >>> ${listItem.size}")
if (prevUrl!=null && prevUrl.length > TEST_PAG.length && prevUrl.contains("/page/") && (maxDate - minDate) < (1000L * 60L * 60L * 24L * 3L)) {
BLog.LOGE("listItem.size >>> ${listItem.size} do next ")
BLog.LOGE("saving data :: ${listItem.size}items ${simpldateFormat.format(Date(minDate))} ~ ${simpldateFormat.format(Date(maxDate))}")
binding.searcher01.postDelayed({binding.searcher01.loadUrl(prevUrl)}, 5000L)
} else {
listItem.sortByDescending { it.date }
BLog.LOGE("Stored data :: ${listItem.size}items ${simpldateFormat.format(Date(minDate))} ~ ${simpldateFormat.format(Date(maxDate))}")
Toast.makeText(requireContext(),
"Stored data :: ${listItem.size} items :: [${simpldateFormat.format(Date(minDate))} ~ ${simpldateFormat.format(Date(maxDate))}]", Toast.LENGTH_LONG).show()
}
}
}
fun jGuruTag(doc: Document) {
listTags.clear()
doc.getElementsByTag("ul").forEach {
it.children().forEach {
if (it.tag().name.contains("li")) {
listTags.add(
RssTagItem(
tagTitle = it.getElementsByTag("a").get(0).text(),
link = it.getElementsByTag("a").get(0).attr("href")
)
)
}
}
}.apply {
listTags.sortByDescending { it.count }
Toast.makeText(requireContext(),
"Stored data :: ${listTags.size}tags", Toast.LENGTH_SHORT).show()
}
}
inner class MyJavaScriptInterface(val webView: WebView) {
@JavascriptInterface
fun sendValueFromHtml(result: String) {
if (lastedFinishedPageUrl.contains(TEST_PAG)) {
var htmlString = result.replace("\\u003","<")
val doc: Document = Jsoup.parse(htmlString)
if (lastedFinishedPageUrl?.contains("page") == true || lastedFinishedPageUrl?.equals(
TEST_PAG
) == true
) {
jGuruMain(doc)
} else if (lastedFinishedPageUrl?.contains("/tags/") == true) {
jGuruTag(doc)
}
} else if (lastedFinishedPageUrl?.contains("youtube") == true) {
val doc: Document = Jsoup.parse(result)
ytChannel(doc)
}
BLog.LOGE("binding.otherCheck after ThreadRun")
}
}
fun jsonObjLog(pkey : String ,key : String, jsonObject: JSONObject) {
if (jsonObject?.has(key) == true && jsonObject?.get(key) is String) {
@ -717,91 +525,7 @@ internal class LauncherHome : Fragment() {
fun ytChannel(doc: Document) {
BLog.LOGE("ytChannel >>>>> doc${doc.title()}")
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) {
BLog.LOGE("ytChannel >>>>> doc singleColumnBrowseResultsRenderer apply ")
root?.contents?.singleColumnBrowseResultsRenderer?.tabs?.forEach {
it.tabRenderer?.content?.sectionListRenderer?.contents?.forEach {
BLog.LOGE("ytChannel >>>>> doc sectionListRenderer?.contents ${Gson().toJson(it)} apply ")
it.shelfRenderer?.content?.verticalListRenderer?.items?.forEach {
BLog.LOGE("ytChannel >>>>> doc verticalListRenderer?.items ${Gson().toJson(it)} apply ")
(it.compactVideoRenderer as? RssDataItem)?.let {
if(it.pubDate() >= (System.currentTimeMillis() - 1000L * 60L * 60L * 24 * 3)) {
rssSet.put(it.originPage(), it)
BLog.LOGE("ytChannel >>>>> doc RssDataItem ${Gson().toJson(it)} apply ")
}
}
}
}
}
} else {
BLog.LOGE("ytChannel >>>>> doc twoColumnBrowseResultsRenderer apply ")
root?.contents?.twoColumnBrowseResultsRenderer?.tabs?.forEach {
it.tabRenderer?.content?.sectionListRenderer?.contents?.forEach {
BLog.LOGE("ytChannel >>>>> doc sectionListRenderer?.contents ${Gson().toJson(it)} apply ")
it.itemSectionRenderer?.contents?.forEach {
BLog.LOGE("ytChannel >>>>> doc itemSectionRenderer?.items ${Gson().toJson(it)} apply ")
it.shelfRenderer?.content?.horizontalListRenderer?.items?.forEach {
(it.gridVideoRenderer as? RssDataItem)?.let {
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)
}
}
}
}
}
}
} catch(e: Exception) {
}
}
@ -907,21 +631,6 @@ class MissedCall {
}
class EndCallReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
BLog.LOGE("EndCallReceiver >>> ${intent}")
val phoneState = intent.getStringExtra(TelephonyManager.EXTRA_STATE) ?: return
if (phoneState == TelephonyManager.EXTRA_STATE_IDLE) {
refreshCalls()
}
}
}
class SMSReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
BLog.LOGE("SMSReceiver >>> ${intent}")
refreshSms()
}
}
class RssTagItem {

View File

@ -25,6 +25,7 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import android.text.Html
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -61,24 +62,47 @@ internal class RssItemAdapter (
}
val dateFormat = SimpleDateFormat("hh:mm / yy - MM - dd")
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: RssTag, position: Int) {
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.desc.visibility = View.GONE
holder.view.circlePreview.visibility = View.GONE
when(todo.category()) {
RssDataType.YOUTUBE -> {
if(todo.thumbnailUrl()?.length ?: 0 > 6) {
Picasso.get().load(todo.thumbnailUrl().toUri()).into(holder.view.circlePreview)
holder.view.circlePreview.visibility = View.VISIBLE
}
holder.view.title.gravity = Gravity.CENTER_VERTICAL.plus(Gravity.RIGHT)
holder.view.desc.gravity = Gravity.CENTER_VERTICAL.plus(Gravity.RIGHT)
holder.view.date.gravity = Gravity.CENTER_VERTICAL.plus(Gravity.RIGHT)
holder.view.desc.visibility = View.VISIBLE
holder.view.desc.text = if(todo.description().contains("게시자")) todo.description().split("게시자")[0] else todo.description()
holder.view.root.setOnClickListener { openYouTube(todo.originPage()) }
}
RssDataType.NewsFeed -> {
holder.view.title.gravity = Gravity.CENTER_VERTICAL.plus(Gravity.LEFT)
holder.view.desc.gravity = Gravity.CENTER_VERTICAL.plus(Gravity.LEFT)
holder.view.date.gravity = Gravity.CENTER_VERTICAL.plus(Gravity.RIGHT)
holder.view.root.setOnClickListener { openNews(todo.originPage()) }
}
RssDataType.NO_DATA -> {}
}
holder.view.root.setOnLongClickListener {
val clipBoard =
lActivity!!.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
clipBoard.setPrimaryClip(ClipData.newPlainText("", todo.thumbnailUrl()))
when(todo.category()) {
RssDataType.YOUTUBE -> {}
RssDataType.NewsFeed -> {
openNews(todo.originPage())
}
RssDataType.NO_DATA -> {}
}
// val clipBoard =
// lActivity!!.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
// clipBoard.setPrimaryClip(ClipData.newPlainText("", todo.thumbnailUrl()))
true
}
@ -87,7 +111,20 @@ internal class RssItemAdapter (
fun updateData(newList: List<RssDataItem>) {
DiffUtil.calculateDiff(RssItemDiffUtil(smsList, newList)).dispatchUpdatesTo(this)
}
fun openNews(schemeString : String) {
val gmmIntentUri = Uri.parse(schemeString)
val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri)
mapIntent.setPackage("com.android.chrome")
lActivity?.startActivity(mapIntent)
}
fun openYouTube(schemeString : String) {
val gmmIntentUri = Uri.parse(schemeString)
val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri)
mapIntent.setPackage("com.google.android.youtube")
lActivity?.startActivity(mapIntent)
}
}
class RssTag(var view: ListItemWithBinding) : RecyclerView.ViewHolder(view.root)

View File

@ -503,13 +503,13 @@ class GridChannelRenderer {
}
class GridVideoRenderer : VideoRenderer() {
// var videoId: String? = null
// var videoId: String? = null
// var thumbnail: Thumbnail? = null
// var title: Title? = null
// var publishedTimeText: PublishedTimeText? = null
// var navigationEndpoint: NavigationEndpoint? = null
var badges: ArrayList<Badge>? = null
// var ownerBadges: ArrayList<OwnerBadge>? = null
// var ownerBadges: ArrayList<OwnerBadge>? = null
// var trackingParams: String? = null
// var menu: Menu? = null
// var thumbnailOverlays: ArrayList<ThumbnailOverlay>? = null
@ -1117,11 +1117,11 @@ class RichThumbnail {
}
class Root {
// var responseContext: ResponseContext? = null
// var responseContext: ResponseContext? = null
var contents: Content14? = null
var header: Header? = null
var metadata: Metadata? = null
// var trackingParams: String? = null
// var trackingParams: String? = null
// var topbar: Topbar? = null
var microformat: Microformat? = null
// var onResponseReceivedActions: ArrayList<OnResponseReceivedAction>? = null
@ -1584,7 +1584,7 @@ open class VideoRenderer : RssDataItem {
var thumbnailOverlays: ArrayList<ThumbnailOverlay>? = null
var avatar: Avatar? = null
override fun title(): String {
return shortBylineText?.runs?.get(0)?.text ?: title?.runs?.get(0)?.text ?: title?.accessibility?.label ?: ""
return shortBylineText?.runs?.get(0)?.text ?: title?.runs?.get(0)?.text ?: title?.accessibility?.label ?: title?.accessibility?.accessibilityData?.label ?: ""
}
override fun thumbnailUrl(): String {
@ -1602,43 +1602,40 @@ open class VideoRenderer : RssDataItem {
}
override fun description(): String {
return ""
return title?.accessibility?.accessibilityData?.label ?: ""
}
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))
}
try {
var targetDate = publishedTimeText?.simpleText ?: publishedTimeText?.runs?.first()?.text ?: ""
if (targetDate?.length ?: 0 > 1) {
var dateDesc = targetDate
dateDesc = dateDesc!!.split("")[0].trim()
val dayString = dateDesc.replace("[^0-9]".toRegex(), "")
before = dayString.toInt()
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) {
}
}catch (e : Exception) {
}
return dateTime
@ -1739,13 +1736,13 @@ class YtConfigData {
}
class YoutubeData {
// var responseContext: ResponseContext? = null
var contents: JSONObject? = null
var header: JSONObject? = null
var metadata: JSONObject? = null
// var trackingParams: String? = null
// 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 microformat: JSONObject? = null
// var onResponseReceivedActions: ArrayList<OnResponseReceivedAction>? = null
// var frameworkUpdates: FrameworkUpdates? = null

View File

@ -7,14 +7,24 @@ import android.database.Cursor
import android.net.Uri
import android.provider.CallLog
import android.provider.ContactsContract.PhoneLookup
import android.provider.Settings.Global
import android.provider.Telephony
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.google.gson.Gson
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import org.json.JSONObject
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import rasel.lunar.launcher.LauncherActivity.Companion.lActivity
import rasel.lunar.launcher.home.LauncherHome.Companion.missedCalls
import rasel.lunar.launcher.home.LauncherHome.Companion.rssSet
import rasel.lunar.launcher.home.LauncherHome.Companion.smsList
import rasel.lunar.launcher.home.MissedCall
import rasel.lunar.launcher.todos.Root
import rasel.lunar.launcher.todos.RssDataItem
import rasel.lunar.launcher.todos.RssFeedsParser
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStream
@ -22,6 +32,8 @@ import java.io.InputStreamReader
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
class MissedCallGetter : Worker {
@ -468,83 +480,101 @@ fun getContactId(contentResolver: ContentResolver, phoneNumber: String?): String
class NewsFeedsGetter : Worker {
var feddsUrls = arrayListOf<String>()
var rssUrls = arrayListOf<String>()
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"}
feddsUrls.clear()
feddsUrls.addAll(RssList.newsFeeds)
rssUrls.clear()
rssUrls.addAll(RssList.youtubeUrls)
var limitDateTime = beforeDay(Date(),3)
BLog.LOGE("getFeeds it >> NewsFeedsGetter doWork Start")
for(url in feddsUrls) {
GlobalScope.async {
for (it in RssFeedsParser.getFeeds(url)) {
if (it.pubDate() >= limitDateTime) {
rssSet.put(it.originPage(), it)
}
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()
BLog.LOGE("getFeeds it >> NewsFeedsGetter doWork before Yt")
for (url in rssUrls) {
GlobalScope.async {
ytChannel(Jsoup.connect(url).get())
}
}
BLog.LOGE("getFeeds it >> NewsFeedsGetter Result")
return Result.success()
}
fun ytChannel(doc: Document) {
BLog.LOGE("ytChannel >>>>> doc${doc.title()}")
try {
doc.getElementsByTag("script").forEach {
if(it.html().contains("var ytInitialData", false)) {/**/
var ytInitialData = it.html().split("var ytInitialData = ")[1].split("</script>")[0].toString()
var tempJSONObject : JSONObject? = null
JSONObject(ytInitialData).apply{
tempJSONObject = this
val root = Gson().fromJson(tempJSONObject.toString(), Root::class.java)
(if (root?.contents?.singleColumnBrowseResultsRenderer?.tabs?.size ?: 0 > 0) {
BLog.LOGE("ytChannel >>>>> doc singleColumnBrowseResultsRenderer apply ")
root?.contents?.singleColumnBrowseResultsRenderer?.tabs?.forEach {
it.tabRenderer?.content?.sectionListRenderer?.contents?.forEach {
// BLog.LOGE("ytChannel >>>>> doc sectionListRenderer?.contents ${Gson().toJson(it)} apply ")
it.shelfRenderer?.content?.verticalListRenderer?.items?.forEach {
// BLog.LOGE("ytChannel >>>>> doc verticalListRenderer?.items ${Gson().toJson(it)} apply ")
(it.compactVideoRenderer as? RssDataItem)?.let {
if(it.pubDate() >= (System.currentTimeMillis() - 1000L * 60L * 60L * 24 * 3)) {
rssSet.put(it.originPage(), it)
BLog.LOGE("ytChannel >>>>> doc RssDataItem ${Gson().toJson(it)} apply ")
}
}
}
}
}
} else {
BLog.LOGE("ytChannel >>>>> doc twoColumnBrowseResultsRenderer apply ")
root?.contents?.twoColumnBrowseResultsRenderer?.tabs?.forEach {
it.tabRenderer?.content?.sectionListRenderer?.contents?.forEach {
// BLog.LOGE("ytChannel >>>>> doc sectionListRenderer?.contents ${Gson().toJson(it)} apply ")
it.itemSectionRenderer?.contents?.forEach {
// BLog.LOGE("ytChannel >>>>> doc itemSectionRenderer?.items ${Gson().toJson(it)} apply ")
it.shelfRenderer?.content?.horizontalListRenderer?.items?.forEach {
// BLog.LOGE("it.gridVideoRenderer >>>>> ${Gson().toJson(it)}")
// BLog.LOGE("it.gridVideoRenderer >>>>> ${Gson().toJson(it.gridVideoRenderer)}")
// BLog.LOGE("it.gridVideoRenderer?.title ${Gson().toJson(it.gridVideoRenderer?.title)}")
// BLog.LOGE("it.gridVideoRenderer?.thumbnail ${Gson().toJson(it.gridVideoRenderer?.thumbnail)}")
// BLog.LOGE("it.gridVideoRenderer?.navigationEndpoint ${Gson().toJson(it.gridVideoRenderer?.navigationEndpoint)}")
// BLog.LOGE("it.gridVideoRenderer?.longBylineText ${Gson().toJson(it.gridVideoRenderer?.longBylineText)}")
// BLog.LOGE("it.gridVideoRenderer?.descriptionSnippet ${Gson().toJson(it.gridVideoRenderer?.descriptionSnippet)}")
// BLog.LOGE("it.gridVideoRenderer?.lengthText ${Gson().toJson(it.gridVideoRenderer?.lengthText)}")
(it.gridVideoRenderer as? RssDataItem)?.let {
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 ")
}
}
}
}
} catch(e: Exception) {
}
}
}

View File

@ -1,12 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:id="@+id/mainFragmentsContainer"
android:fitsSystemWindows="true">
<WebView
android:layout_margin="30dp"
android:id="@+id/searcher_01"
android:layout_width="match_parent"
android:alpha="0"
android:layout_height="match_parent"/>
<androidx.viewpager2.widget.ViewPager2
android:layout_width="match_parent"
android:layout_height="match_parent"

View File

@ -5,17 +5,6 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:layout_margin="30dp"
android:id="@+id/searcher_01"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_width="0dp"
android:alpha="0.01"
android:layout_height="0dp"/>
<View
app:layout_constraintLeft_toLeftOf="parent"
@ -140,6 +129,31 @@
app:layout_constraintTop_toBottomOf="@+id/summaryChoose"
app:layout_constraintBottom_toBottomOf="parent"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/smsList"
android:layout_width="0dp"
android:layout_height="0dp"
android:overScrollMode="never"
android:padding="20dp"
android:scrollbars="none"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/summaryChoose"
app:layout_constraintBottom_toBottomOf="parent"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/infoList"
android:layout_width="0dp"
android:layout_height="0dp"
android:overScrollMode="never"
android:padding="20dp"
android:scrollbars="none"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/summaryChoose"
app:layout_constraintBottom_toBottomOf="parent"
/>
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/favAppsGroup"

View File

@ -5,29 +5,35 @@
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!--rasel.lunar.launcher.view.CircleImageView-->
<rasel.lunar.launcher.view.CircleImageView
<ImageView
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:id="@+id/circle_preview"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:layout_width="90dp"
android:layout_height="90dp"/>
android:layout_width="120dp"
android:layout_height="120dp"/>
<TextView
android:id="@+id/title"
android:layout_width="@dimen/zero"
android:layout_height="30dp"
android:gravity="center_vertical|right"
android:layout_height="wrap_content"
android:textSize="24sp"
android:maxLines="2"
android:includeFontPadding="false"
android:ellipsize="middle"
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"
android:layout_height="wrap_content"
android:textSize="16sp"
android:maxLines="2"
android:includeFontPadding="false"
android:ellipsize="middle"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintBottom_toTopOf="@id/date"
app:layout_constraintLeft_toRightOf="@id/circle_preview"
@ -35,8 +41,10 @@
<TextView
android:id="@+id/date"
android:layout_width="@dimen/zero"
android:layout_height="30dp"
android:gravity="center_vertical|right"
android:layout_height="wrap_content"
android:textSize="16sp"
android:lines="1"
android:includeFontPadding="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/circle_preview"
app:layout_constraintRight_toRightOf="parent"/>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/colorInputLayout"
android:layout_width="@dimen/oneNinetySix"
android:layout_height="wrap_content"
android:hint="@string/argb"
app:boxBackgroundColor="?attr/colorSurface"
app:endIconMode="clear_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionDone"
android:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>