# Conflicts:
#	app/src/main/kotlin/bums/lunatic/launcher/home/RssHome.kt
#	app/src/main/kotlin/bums/lunatic/launcher/utils/JsoupUtils.kt
...
This commit is contained in:
2025-07-23 16:40:50 +09:00
21 changed files with 954 additions and 3224 deletions
@@ -50,6 +50,7 @@ import androidx.core.net.toUri
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.OneTimeWorkRequest
@@ -66,7 +67,7 @@ import bums.lunatic.launcher.helpers.Constants.Companion.PREFS_SETTINGS
import bums.lunatic.launcher.helpers.Constants.Companion.widgetHostId
import bums.lunatic.launcher.helpers.HeadsetActionButtonReceiver
import bums.lunatic.launcher.helpers.PrefLong
import bums.lunatic.launcher.home.LauncherHome
import bums.lunatic.launcher.home.RssHome
import bums.lunatic.launcher.home.RssViewBuilder
import bums.lunatic.launcher.model.RssData
import bums.lunatic.launcher.model.RssDataType
@@ -612,7 +613,7 @@ internal class LauncherActivity : CommonActivity() {
when(id) {
R.id.feeds -> {
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, LauncherHome())
.replace(R.id.fragment_container, RssHome())
.commit()
}
R.id.books ->{
@@ -728,8 +729,12 @@ internal class LauncherActivity : CommonActivity() {
override fun handleOnBackPressed() {
val currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container)
when(currentFragment) {
is LauncherHome ->{
currentFragment.doNextPage()
is RssHome ->{
if (currentFragment.binding.layoutRssSummary.root.isVisible) {
currentFragment.openGecko("")
} else {
currentFragment.doNextPage()
}
}
}
}
@@ -1,31 +1,31 @@
//import androidx.annotation.Nullable;
//
//import java.util.HashMap;
//
import androidx.annotation.Nullable;
import java.util.HashMap;
////package bums.lunatic.launcher.home;
////
////import androidx.annotation.Nullable;
////
////import java.util.HashMap;
////
public class ExtHahMap extends HashMap<String,Object> {
@Nullable
@Override
public Object put(String key, Object value) {
if (value instanceof String) {
if (((String) value).length() > 0) {
} else {
}
}else {
return super.put(key, value);
}
}
}
//
//import androidx.annotation.Nullable;
//
//import java.util.HashMap;
//
//////package bums.lunatic.launcher.home;
//////
//////import androidx.annotation.Nullable;
//////
//////import java.util.HashMap;
//////
//public class ExtHahMap extends HashMap<String,Object> {
// @Nullable
// @Override
// public Object put(String key, Object value) {
// if (value instanceof String) {
// if (((String) value).length() > 0) {
//
// } else {
//
// }
// }else {
// return super.put(key, value);
// }
// }
//}
//
@@ -1,12 +1,17 @@
package bums.lunatic.launcher.home
import android.app.DownloadManager
import android.content.Context
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.net.Uri
import android.os.Environment
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.AttributeSet
import android.util.Base64
import android.util.Log
import android.view.KeyEvent
import android.view.KeyEvent.ACTION_UP
@@ -18,14 +23,23 @@ import android.view.KeyEvent.KEYCODE_BUTTON_X
import android.view.KeyEvent.KEYCODE_BUTTON_Y
import android.view.KeyEvent.KEYCODE_DPAD_DOWN
import android.view.KeyEvent.KEYCODE_DPAD_UP
import android.view.View
import android.widget.ProgressBar
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import bums.lunatic.launcher.LauncherActivity.Companion.getRuntime
import bums.lunatic.launcher.tokiz.data.HistoryManager
import bums.lunatic.launcher.tokiz.data.model.History
import bums.lunatic.launcher.tokiz.data.model.PortMessage
import bums.lunatic.launcher.tokiz.view.BWebview
import bums.lunatic.launcher.tokiz.view.JxEvent
import bums.lunatic.launcher.utils.Blog
import bums.lunatic.launcher.utils.afterDay
import bums.lunatic.launcher.workers.WorkersDb
import com.google.gson.Gson
import org.json.JSONException
import org.json.JSONObject
import org.jsoup.Jsoup
import org.mozilla.gecko.util.ThreadUtils
import org.mozilla.geckoview.ExperimentDelegate
import org.mozilla.geckoview.GeckoResult
@@ -39,13 +53,18 @@ import org.mozilla.geckoview.WebExtension.MessageDelegate
import org.mozilla.geckoview.WebExtension.PortDelegate
import org.mozilla.geckoview.WebExtensionController.AddonManagerDelegate
import org.mozilla.geckoview.WebRequestError
import java.io.File
import java.text.SimpleDateFormat
class GeckoWeb : BWebview {
constructor(context: Context?) : super(context) {
buildWeb()
}
var decoViews = arrayListOf<View>()
override fun setVisibility(visibility: Int) {
super.setVisibility(visibility)
decoViews.forEach { it.visibility = visibility }
}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
buildWeb()
@@ -54,6 +73,7 @@ class GeckoWeb : BWebview {
val mPortNam = "browser"
val extPath = "resource://android/assets/extensions/my_extension/"
val extId = "messaging@booktoki468.com"
private fun buildWeb() {
getRuntime()?.let {
val session: GeckoSession = GeckoSession()
@@ -87,7 +107,7 @@ class GeckoWeb : BWebview {
var lastedUrl: String? = null
var canGoBack: Boolean? = null
var mPort: WebExtension.Port? = null
var mCaache : WebExtension.Port? = null
object WebExtensionInfo {
val mPortNam = "browser"
val extPath = "resource://android/assets/extensions/my_extension/"
@@ -238,6 +258,32 @@ class GeckoWeb : BWebview {
return super.onRecordMalformedConfigurationEvent(feature, part)
}
}
fun showImageDownloadDialog(context: Context, imageUrl: Uri) {
AlertDialog.Builder(context)
.setTitle("이미지 다운로드")
.setMessage("이미지를 저장하시겠습니까?")
.setPositiveButton("저장") { _, _ ->
downloadImage(context, imageUrl)
}
.setNegativeButton("취소", null)
.show()
}
fun downloadImage(context: Context, url: Uri) {
val fileName = url.lastPathSegment ?: "${SimpleDateFormat("yyyyMMddHHmmsss")}.jpg"
val request = DownloadManager.Request(url)
request.setTitle(fileName)
request.setDescription("이미지 다운로드 중...")
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)
// 네트워크타입, 알림설정 등 옵션 추가 가능
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
val dm = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
dm.enqueue(request)
Toast.makeText(context, "다운로드 시작: $fileName", Toast.LENGTH_SHORT).show()
}
fun getFilterF() = String(java.util.Base64.getMimeDecoder().decode("aHR0cHM6Ly9pamF2dG9ycmVudC5jb20=".toByteArray()))
val contentDelegate = object : GeckoSession.ContentDelegate {
override fun onTitleChange(
session: GeckoSession,
@@ -260,6 +306,21 @@ class GeckoWeb : BWebview {
super.onFirstContentfulPaint(session)
}
override fun onContextMenu(
session: GeckoSession,
screenX: Int,
screenY: Int,
element: GeckoSession.ContentDelegate.ContextElement
) {
if (element.type == GeckoSession.ContentDelegate.ContextElement.TYPE_IMAGE) {
Uri.parse(element.srcUri)?.let {
showImageDownloadDialog(context,it)
}
}
super.onContextMenu(session, screenX, screenY, element)
}
}
val progressDelegate = object : GeckoSession.ProgressDelegate {
override fun onSecurityChange(
@@ -284,15 +345,20 @@ class GeckoWeb : BWebview {
}
override fun onPageStart(session: GeckoSession, url: String) {
super.onPageStart(session, url)
if (url?.contains("reddit.app.link") == true) {
session.stop()
Uri.parse(url)?.let { uri ->
context.startActivity(Intent().apply {
action = Intent.ACTION_VIEW
data = uri
})
}
if (url.contains(getFilterF()) && url.contains("jpg") == false) {
this@GeckoWeb.visibility = View.INVISIBLE
}
// if (url?.contains("reddit.app.link") == true) {
// session.stop()
// Uri.parse(url)?.let { uri ->
// context.startActivity(Intent().apply {
// action = Intent.ACTION_VIEW
// flags = Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS.or(FLAG_ACTIVITY_CLEAR_TOP).or(
// FLAG_ACTIVITY_NEW_TASK)
// data = uri
// })
// }
// }
}
override fun onPageStop(session: GeckoSession, success: Boolean) {
@@ -305,6 +371,7 @@ class GeckoWeb : BWebview {
}
}
}
}
@@ -328,7 +395,18 @@ class GeckoWeb : BWebview {
): GeckoResult<GeckoSession>? {
Blog.LOGE("GeckoView", "onNewSession: $session from WebExtension")
Uri.parse(uri)?.let {
if(it.host?.let { it1 -> lastedUrl?.contains(it1, true) } == true) {
loadUrl(uri)
} else {
context.startActivity(Intent().apply {
action = Intent.ACTION_VIEW
flags = Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS.or(FLAG_ACTIVITY_CLEAR_TOP).or(
FLAG_ACTIVITY_NEW_TASK)
data = it
})
}
}
return super.onNewSession(session, uri)
}
@@ -343,6 +421,9 @@ class GeckoWeb : BWebview {
Blog.LOGE("GeckoView", "현재 session: $session")
url?.let { url ->
if (url?.contains(getFilterF()) == true && url.contains("jpg") == false) {
this@GeckoWeb.visibility = View.INVISIBLE
}
if (url.split("//").size > 1) {
url.replace("//", "/").replace("https:/", "https://").let {
Blog.LOGE("url >> ${url} , it >>> ${it}")
@@ -371,23 +452,129 @@ class GeckoWeb : BWebview {
message: Any, port: WebExtension.Port
) {
Blog.LOGE("PortDelegate", "Received message from extension: $message")
if (message is String && message.contains("type")) {
try {
var lPortMessage =
Gson().fromJson<PortMessage>(message, PortMessage::class.java)
when(lPortMessage.type) {
"getListResult" -> {
}
"BookContents"->{
}
"NotRegistered" -> {
}
"WebtoonContents"-> {
}
"MSG" -> {
}
"SHOWVIEWER" -> {
}
"PRIVATES"->{
lPortMessage.privates?.forEach {
Blog.LOGE("Item screenshots >>> ${it.screenshots}")
it.pubDate = afterDay(it.pubDate)
WorkersDb.insertData(it)
}
}
"PagerContents" -> {
if (lPortMessage.contents?.isNotEmpty() == true) {
}
}
else -> {
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
override fun onDisconnect(port: WebExtension.Port) {
// This port is not usable anymore.
mPort = null
}
}
val cacheDelegate: PortDelegate =
object : PortDelegate {
override fun onPortMessage(
message: Any, port: WebExtension.Port
) {
Blog.LOGE("cacheDelegate", "cacheDelegate message : $message")
}
override fun onDisconnect(port: WebExtension.Port) {
// This port is not usable anymore.
if (port === mPort) {
mPort = null
if (port === mCaache) {
mCaache = null
}
}
}
fun onReceiveFromExtension(message: JSONObject) {
val url = message.getString("url")
val dataBase64 = message.getString("data")
val host = Uri.parse(url).host!!
val cacheDir = File(context.filesDir, "webcache/$host")
if (!cacheDir.exists()) cacheDir.mkdirs()
// 파일명 생성 (중복처리 필요)
val fileName = filenameFromUrl(url)
val resourceFile = File(cacheDir, fileName)
val rawData = Base64.decode(dataBase64, Base64.DEFAULT)
resourceFile.writeBytes(rawData)
}
fun filenameFromUrl(url: String): String {
// URL을 파싱
val uri = Uri.parse(url)
// 경로 마지막 세그먼트 (예시: /images/logo.png → logo.png)
var filename = uri.lastPathSegment ?: "index.html"
// 쿼리 파라미터 등 URL이 붙은 경우 처리 (예: index.html?version=2)
if (filename.contains("?")) {
filename = filename.substringBefore("?")
}
if (filename.isEmpty() || filename.endsWith("/")) {
filename = "index.html"
}
// 파일명에 사용할 수 없는 문자 제거(윈도우, 리눅스, 맥 등 호환)
filename = filename.replace(Regex("[\\\\/:*?\"<>|]"), "_")
// 너무 긴 파일명은 자르기
val maxLength = 100
if (filename.length > maxLength) {
val ext = filename.substringAfterLast('.', "")
filename = filename.take(maxLength - ext.length - 1) +
if (ext.isNotBlank()) ".$ext" else ""
}
return filename
}
val messageDelegate: MessageDelegate =
object : MessageDelegate {
override fun onConnect(port: WebExtension.Port) {
mPort = port
mPort!!.setDelegate(portDelegate)
Blog.LOGE("onConnect port >>> ${port.name}")
if (port != null) {
mPort = port
mPort!!.setDelegate(portDelegate)
}
}
override fun onMessage(
@@ -401,11 +588,8 @@ class GeckoWeb : BWebview {
)
return super.onMessage(nativeApp, message, sender)
}
}
fun onStart() {
}
@@ -19,8 +19,9 @@
package bums.lunatic.launcher.home
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Handler
@@ -29,9 +30,11 @@ import android.view.LayoutInflater
import android.view.PointerIcon
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.Toast
import androidx.annotation.NonNull
import androidx.annotation.RequiresApi
import androidx.core.view.isVisible
import androidx.databinding.BindingAdapter
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.DividerItemDecoration
@@ -48,7 +51,6 @@ import bums.lunatic.launcher.home.adapters.SwipeToDeleteCallback
import bums.lunatic.launcher.model.RssData
import bums.lunatic.launcher.model.RssDataType
import bums.lunatic.launcher.model.WeatherForcast
import bums.lunatic.launcher.openClient
import bums.lunatic.launcher.openReddit
import bums.lunatic.launcher.openYouTube
import bums.lunatic.launcher.tokiz.view.JxEvent
@@ -57,10 +59,13 @@ import bums.lunatic.launcher.utils.SimpleFingerGestures
import bums.lunatic.launcher.utils.beforeDay
import bums.lunatic.launcher.utils.beforeOneDay
import bums.lunatic.launcher.workers.WorkersDb
import com.bumptech.glide.Glide
import com.google.android.material.imageview.ShapeableImageView
import io.realm.kotlin.UpdatePolicy
import io.realm.kotlin.ext.query
import io.realm.kotlin.notifications.InitialResults
import io.realm.kotlin.notifications.ResultsChange
import io.realm.kotlin.notifications.UpdatedResults
import io.realm.kotlin.query.RealmQuery
import io.realm.kotlin.query.RealmResults
import io.realm.kotlin.query.Sort
@@ -70,7 +75,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
internal class LauncherHome : Fragment() {
internal class RssHome : Fragment() {
lateinit var binding: LauncherHomeBinding
private lateinit var fragManager: FragmentManager
@@ -78,18 +83,17 @@ internal class LauncherHome : Fragment() {
private var shouldResume = true
companion object {
var home: LauncherHome? = null
var home: RssHome? = null
var lastedFinishedPageUrl: String = ""
}
val UPDATE_DELAY = 5L
val commandHandler = Handler(Looper.getMainLooper())
val infoUpdate = Runnable { chooseAdpater() }
var result: RealmResults<WeatherForcast>? = null
val nomoreShowCount = 5
fun rssStateVote() = (lasted?.filter { it.vote == true }?.size ?: -1) == (lasted?.size ?: 0)
var lasted: List<RssData>? = null
var lasted: ArrayList<RssData> = arrayListOf()
var infosJob: Job? = null
var rssId = ""
lateinit var mRssAdapter: RssItemAdapter
@@ -105,7 +109,9 @@ internal class LauncherHome : Fragment() {
gestureDistance: Double
): Boolean {
Blog.LOGE("")
if (imageView){
openGecko("")
}
return true
}
@@ -115,6 +121,9 @@ internal class LauncherHome : Fragment() {
gestureDuration: Long,
gestureDistance: Double
): Boolean {
if (imageView){
openGecko("")
}
Blog.LOGE("")
return true
}
@@ -202,19 +211,27 @@ internal class LauncherHome : Fragment() {
}
}
when(rss.category()) {
//RssDataType.GURU,RssDataType.MOST,
RssDataType.REDDIT_NSFW -> {
RssDataType.REDDIT_NSFW,RssDataType.PRIVATE -> {
v.findViewById<ShapeableImageView>(R.id.circle_preview)?.let {
if (it.visibility == View.GONE) {
it.visibility = View.VISIBLE
it.postDelayed({
it.visibility = View.GONE
}, 2000L)
if (RssDataType.PRIVATE.equals(rss.category()) && imageView) {
openGecko("")
} else {
if (RssDataType.REDDIT_NSFW.equals(rss.category())) {
openReddit(rss.originPage())
if (it.visibility == View.GONE) {
it.visibility = View.VISIBLE
it.postDelayed({
it.visibility = View.GONE
}, 2000L)
} else {
openGecko(rss.originPage())
if (RssDataType.REDDIT_NSFW.equals(rss.category())) {
openReddit(rss.originPage())
} else if (RssDataType.PRIVATE.equals(rss.category())) {
startActivity(Intent().apply {
action = Intent.ACTION_VIEW
data = Uri.parse(rss.originPage)
})
} else {
openGecko(rss.originPage())
}
}
}
}
@@ -238,15 +255,38 @@ internal class LauncherHome : Fragment() {
}
fun openGecko(originPage: String) {
rssId = originPage
targetList.clear()
if (!imageView) {
rssId = originPage
targetList.clear()
var setString = hashSetOf<String>()
setString.addAll(rssList)
setString.removeAll { it.equals(rssId) }
var setString = hashSetOf<String>()
setString.addAll(rssList)
setString.removeAll { it.equals(rssId) }
targetList.addAll(setString)
binding.geckoWeb.loadUrl(rssId)
targetList.addAll(setString)
binding.geckoWeb.loadUrl(rssId)
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
lasted?.removeFirst()?.let {
binding.layoutRssSummary.root.visibility = View.VISIBLE
binding.layoutRssSummary.title.text = it.title()
binding.layoutRssSummary.desc.text = it.description()
binding.layoutRssSummary.link.text = it.originPage()
loadImage(binding.layoutRssSummary.cover,it.thumbnailUrl())
loadImage(binding.layoutRssSummary.screen,it.getScreen())
}
}
// targetList.clear()
// lasted?.forEach {
// it.thumbnail?.let {
// targetList.add(it)
// }
// }
// rssId = targetList.removeAt(0)
// binding.geckoWeb.loadUrl(rssId)
}
}
@SuppressLint("ClickableViewAccessibility")
@@ -272,6 +312,54 @@ internal class LauncherHome : Fragment() {
return@setOnTouchListener false
}
}
binding.vote.setOnClickListener {
if (binding.geckoWeb.isVisible) {
vote()
}
}
binding.test.setOnClickListener {
if(binding.geckoWeb.isVisible) {
binding.geckoWeb.visibility = View.GONE
}
binding.geckoWeb.visibility = View.GONE
binding.geckoWeb.loadUrl("aHR0cHM6Ly9pamF2dG9ycmVudC5jb20=")
}
binding.hide.setOnClickListener {
if (binding.geckoWeb.isVisible) {
WorkersDb.getRealm().apply {
writeBlocking {
val result = query<RssData>().query("originPage == $0", rssId).find()
if (result.size > 0) {
result.forEach {
if(it.vote) {
it.vote = false
}
}
}
}
}
doNextPage()
}
}
binding.home.setOnClickListener {
if (binding.geckoWeb.isVisible) {
binding.geckoWeb.visibility = View.GONE
}
binding.layoutRssSummary.root.visibility = View.GONE
queryInfos()
}
binding.bookmark.setOnClickListener {
binding.layoutRssSummary.root.visibility = View.GONE
queryVotes()
}
binding.prv.setOnClickListener {
queryPrevate()
}
queryInfos()
binding.geckoWeb.progress = binding.progressBar
binding.geckoWeb.jxInteface = { jxEvent ->
@@ -289,20 +377,19 @@ internal class LauncherHome : Fragment() {
}
}
}
val nullCursor = PointerIcon.getSystemIcon(context!!, PointerIcon.TYPE_NULL)
val nullCursor = PointerIcon.getSystemIcon(requireContext(), PointerIcon.TYPE_NULL)
binding.root.setPointerIcon(nullCursor)
binding.geckoWeb.decoViews.add(binding.hide)
binding.geckoWeb.decoViews.add(binding.vote)
binding.geckoWeb.decoViews.add(binding.progressBar)
return binding.root
}
fun vote(){
Blog.LOGE("Arrow Center Click")
WorkersDb.getRealm().apply {
writeBlocking {
val result = query<RssData>().query("originPage == $0", rssId).find()
val result = query<RssData>().query(if(imageView)"thumbnail == $0" else "originPage == $0", rssId).find()
if (result.size > 0) {
result.forEach { it.vote = true }
}
@@ -316,10 +403,10 @@ internal class LauncherHome : Fragment() {
fun doNextPage() {
WorkersDb.getRealm().apply {
writeBlocking {
val result = query<RssData>().query("originPage == $0", rssId).find()
val result = query<RssData>().query(if(imageView)"thumbnail == $0" else "originPage == $0", rssId).find()
if (result.size > 0) {
result.forEach {
it.read = it.read + nomoreShowCount
it.read = it.read + nomoreShowCount
}
}
}
@@ -357,30 +444,48 @@ internal class LauncherHome : Fragment() {
mRssDataResult?.asFlow()?.let { flow ->
infosJob = CoroutineScope(Dispatchers.IO).launch {
flow.collect { changes: ResultsChange<RssData> ->
commandHandler.removeCallbacks(infoUpdate)
WorkersDb.getRealm().apply {
lasted = copyFromRealm(changes.list)
when(changes) {
is InitialResults -> {
commandHandler.removeCallbacks(infoUpdate)
WorkersDb.getRealm().apply {
lasted.clear()
lasted.addAll(copyFromRealm(changes.list))
}
commandHandler.post(infoUpdate)
}
is UpdatedResults -> {
CoroutineScope(Dispatchers.Main).launch {
changes.changeRanges.forEach {
mRssAdapter.notifyItemRangeChanged(it.startIndex, it.length)
}
}
}
}
commandHandler.postDelayed(infoUpdate, UPDATE_DELAY)
}
}
infosJob?.start()
}
}
fun queryPrevate() {
imageView = true
beforeQuery()
updateQuery(WorkersDb.getPrivate())
}
fun queryVotes() {
imageView = false
beforeQuery()
updateQuery(WorkersDb.getVotedRss())
}
var imageView = false
fun queryInfos(
//RssDataType.GURU, RssDataType.MOST,
filter: Collection<RssDataType>? = arrayListOf(RssDataType.REDDIT_NSFW), noLimit: Boolean = false
filter: Collection<RssDataType>? = arrayListOf(RssDataType.REDDIT_NSFW,RssDataType.PRIVATE), noLimit: Boolean = false
) {
imageView = false
beforeQuery()
var rQ = WorkersDb.getRealm().query<RssData>().query("read < $0", nomoreShowCount).distinct("originPage", "title")
if (!noLimit) rQ.query("pubDate > $0", beforeOneDay())
// ((filter?.size ?: 0) > 0).letTrue {filter!!.forEach {rQ = rQ.query("category != $0", it.name)}}
((filter?.size ?: 0) > 0).letTrue {filter!!.forEach {rQ = rQ.query("category != $0", it.name)}}
updateQuery(rQ)
}
@@ -401,16 +506,16 @@ internal class LauncherHome : Fragment() {
Blog.LOGE("onViewCreated()")
fragManager.addOnBackStackChangedListener {
Blog.LOGE("addOnBackStackChangedListener()")
shouldResume = if (fragManager.backStackEntryCount == 0) {
binding.root.visibility = View.VISIBLE
true
} else {
binding.root.visibility = View.GONE
false
}
}
// fragManager.addOnBackStackChangedListener {
// Blog.LOGE("addOnBackStackChangedListener()")
// shouldResume = if (fragManager.backStackEntryCount == 0) {
// binding.root.visibility = View.VISIBLE
// true
// } else {
// binding.root.visibility = View.GONE
// false
// }
// }
enableSwipeToDeleteAndUndo()
}
@@ -475,8 +580,6 @@ internal class LauncherHome : Fragment() {
override fun onResume() {
super.onResume()
val nullCursor = PointerIcon.getSystemIcon(context!!, PointerIcon.TYPE_NULL)
binding.root.setPointerIcon(nullCursor)
if (shouldResume) {
}
@@ -488,7 +591,21 @@ internal class LauncherHome : Fragment() {
}
@BindingAdapter("imageUrl")
fun loadImage(imageView: ImageView, url: String?) {
url?.let {
if (it.length > 4) {
Blog.LOGE("loadImage >>> $it")
Glide.with(imageView.context)
.load(url)
.fitCenter()
.into(imageView)
imageView.visibility = View.VISIBLE
} else {
imageView.visibility = View.GONE
}
} ?: {
imageView.visibility = View.GONE
}
}
@@ -219,8 +219,8 @@ internal class RssItemAdapter (
v: View,
event: MotionEvent
): Boolean {
Blog.LOGE("event.device.name >>> ${event.device.name}")
if (event.device.name?.contains("JX-12",true) == true|| event.device.name?.equals("J06",true) == true) {
if (event.device != null && event.device.name != null && (event.device.name?.contains("JX-12",true) == true|| event.device.name?.equals("J06",true) == true)) {
Blog.LOGE("event.device.name >>> ${event.device.name}")
return true//mSimpleFingerGestures.onTouch(v,event)
} else {
return false
@@ -1,5 +1,6 @@
package bums.lunatic.launcher.model
import bums.lunatic.launcher.utils.Blog
import bums.lunatic.launcher.utils.JamoUtils
import bums.lunatic.launcher.utils.afterDay
import bums.lunatic.launcher.utils.beforeDayBy
@@ -218,6 +219,12 @@ class RssData : RealmObject, RssDataInterface {
var thumbnail : String? = null
var pubDate : Long = 0L
var category : String? = null
var magnet : String? = null
var screenshots : String? = null
fun getScreen() : String {
Blog.LOGE("getScreen $screenshots")
return screenshots ?: ""
}
var chosung : String? = null
var vote : Boolean = false
@@ -232,15 +239,18 @@ class RssData : RealmObject, RssDataInterface {
}
else -> title ?: ""
}.apply {
Blog.LOGE("title $this")
chosung = JamoUtils.split(title).joinToString("")
}
}
override fun thumbnailUrl(): String {
Blog.LOGE("thumbnail $thumbnail")
return thumbnail ?: ""
}
override fun originPage(): String {
Blog.LOGE("originPage $originPage")
return originPage ?: ""
}
@@ -6,6 +6,7 @@ import bums.lunatic.launcher.helpers.PrefHelper
enum class RssDataType {
NO_DATA,
PRIVATE,
YOUTUBE,
NEWSFEED,
// GURU,
@@ -35,14 +36,14 @@ enum class RssDataType {
fun defaultImgSize() = when (this) {
YOUTUBE -> 200
REDDIT_NSFW -> 360
REDDIT_NSFW,PRIVATE -> 360
//,GURU,MOST
else -> { 120 }
}
fun getDefaultVisibiliy() = when (this) {
//,GURU,MOST
REDDIT_NSFW,NEWSFEED -> View.GONE
REDDIT_NSFW,PRIVATE -> View.GONE
else -> { View.VISIBLE }
}
@@ -1174,7 +1174,6 @@ abstract class BaseToki : Fragment(), PagedTextViewInterface {
}
}
}
}
fun onLoadedContents(aContents: String) {
@@ -1,5 +1,6 @@
package bums.lunatic.launcher.tokiz.data.model
import bums.lunatic.launcher.model.RssData
import io.realm.kotlin.ext.realmListOf
import io.realm.kotlin.types.RealmList
import io.realm.kotlin.types.RealmObject
@@ -10,6 +11,8 @@ class PortMessage {
var bookInfos : PageInfosJ? = null
var book : BookContents? = null
var msg : String? = null
var contents : String? = null
var privates : ArrayList<RssData>? = null
}
class BookContents {
var chapterTitle : String? = null
@@ -12,6 +12,7 @@ import bums.lunatic.launcher.tokiz.common.TouchArea
import bums.lunatic.launcher.utils.Blog
import bums.lunatic.launcher.utils.SimpleFingerGestures
import org.mozilla.geckoview.GeckoView
import java.util.Base64
enum class JxEvent {
SCROLL_UP,
@@ -160,14 +161,19 @@ open class BWebview : GeckoView {
var lastDomain : String = ""
fun loadUrl(url: String) {
var nUrl = url
Blog.LOGE("url >>>> ${url}")
if (url.endsWith("=")) {
nUrl = String(Base64.getMimeDecoder().decode(url.toByteArray()))
} else if (url.startsWith("http") == false) {
nUrl = lastDomain
}
if (this.isVisible == false) {
this.visibility = View.VISIBLE
}
Blog.LOGE("url >>>> ${url}")
var nUrl = url
if (url.startsWith("http") == false) {
nUrl = lastDomain
}
Blog.LOGE("nUrl >>>> ${nUrl}")
nUrl?.let { url ->
if (url.split("//").size > 1) {
url.replace("//","/").replace("https:/","https://").let {
@@ -8,6 +8,14 @@ import android.provider.ContactsContract.PhoneLookup
import java.util.Calendar
import java.util.Date
fun afterDay(date: Long): Long {
val cal: Calendar = Calendar.getInstance()
cal.setTime(Date(date))
cal.add(Calendar.HOUR_OF_DAY, 23)
cal.add(Calendar.MINUTE, 53)
return cal.timeInMillis
}
fun before30Min(date: Date): Long {
val cal: Calendar = Calendar.getInstance()
cal.setTime(date)
@@ -1,6 +1,6 @@
package bums.lunatic.launcher.utils
import bums.lunatic.launcher.home.LauncherHome.Companion.lastedFinishedPageUrl
import bums.lunatic.launcher.home.RssHome.Companion.lastedFinishedPageUrl
import bums.lunatic.launcher.model.RssData
import bums.lunatic.launcher.model.RssDataType
import bums.lunatic.launcher.model.dateFormat
@@ -23,6 +23,7 @@ import bums.lunatic.launcher.model.LocationLog
import bums.lunatic.launcher.model.NotificationItem
import bums.lunatic.launcher.model.RssData
import bums.lunatic.launcher.model.RssDataInterface
import bums.lunatic.launcher.model.RssDataType
import bums.lunatic.launcher.model.TelegramBotUpdate
import bums.lunatic.launcher.model.TelegramChat
import bums.lunatic.launcher.model.TelegramData
@@ -33,6 +34,7 @@ import bums.lunatic.launcher.utils.Blog
import bums.lunatic.launcher.utils.JamoUtils
import bums.lunatic.launcher.utils.beforeDay
import bums.lunatic.launcher.utils.beforeOneDay
import com.google.gson.Gson
import io.realm.kotlin.Realm
import io.realm.kotlin.RealmConfiguration
import io.realm.kotlin.UpdatePolicy
@@ -86,9 +88,21 @@ object WorkersDb {
getRealm().apply {
this.writeBlocking {
try {
if (query<RssData>("originPage == $0", rssData.originPage).find().isEmpty()) {
this.copyToRealm(rssData, UpdatePolicy.ERROR)
if (rssData.category().equals(RssDataType.PRIVATE)) {
this.copyToRealm(rssData, UpdatePolicy.ALL)
Blog.LOGE("rssData >> $rssData ${rssData.getScreen()}")
} else {
if (query<RssData>("originPage == $0", rssData.originPage).find()
.isEmpty()
) {
this.copyToRealm(rssData, UpdatePolicy.ERROR)
}
}
try {
query<RssData>("originPage == $0",rssData.originPage).find()?.first()?.let {
Blog.LOGE("SAVED CHECK ${Gson().toJson(this.copyFromRealm(it))}")
}
}catch (e: Exception) {e.printStackTrace()}
} catch (e : Exception) {
}
@@ -212,6 +226,9 @@ object WorkersDb {
}
}
fun getPrivate() = getRealm().query<RssData>().query("category == $0 ",
RssDataType.PRIVATE.name).distinct("originPage", "title").sort("pubDate", Sort.DESCENDING)
fun getVotedRss() = getRealm().query<RssData>().query("vote == $0", true).distinct("originPage", "title")
fun getDeleteQuery( ) : RealmQuery<RssData>{