This commit is contained in:
lunaticbum 2025-07-21 20:10:44 +09:00
parent 2001be4424
commit 74c95e04fd
9 changed files with 378 additions and 126 deletions

View File

@ -1,40 +1,65 @@
//const port = browser.runtime.connectNative("browser");
browser.webRequest.onHeadersReceived.addListener( //// 모든 요청을 가로챔
function(details) { //browser.webRequest.onCompleted.addListener(async (details) => {
let headers = details.responseHeaders || []; // // 원래 요청 URL
// Cache-Control 헤더가 없거나 no-store, no-cache 등일 때 수정 // const url = details.url;
let found = false; // try {
for (let header of headers) { // // 실제 데이터 fetch (credentials, 쿠키 등 필요시 옵션 맞춤)
if (header.name.toLowerCase() === "cache-control") { // const res = await fetch(url, {credentials: "include"});
header.value = "public, max-age=31536000"; // 1년 캐시 // const blob = await res.blob();
found = true; // // 데이터 -> ArrayBuffer
} // const arrayBuffer = await blob.arrayBuffer();
} // // base64로 변환
if (!found) { // const base64 = btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)));
headers.push({name: "Cache-Control", value: "public, max-age=31536000"}); // // 네이티브 앱에게 전달
} // port.postMessage(JSON.stringify({type:"CACAHE",msg:msg , url: url, data: base64}));
return {responseHeaders: headers}; //// browser.runtime.sendNativeMessage(
}, //// "browser", // 등록한 네이티브 앱 id
{urls: ["*://*/*.gif"], types: ["image"]}, //// { url: url, data: base64 }
["blocking", "responseHeaders"] //// );
); // } catch(e) {
//const originalLog = console.log; // // 에러 처리
//console.log = function(...args) { // console.error(e);
// // 메시지 가공 또는 네이티브로 전달
// originalLog.apply(console, args);
// browser.runtime.sendNativeMessage("browser", args);
//};
browser.webRequest.onBeforeRequest.addListener(
function(details) {
return { cancel: true };
},
{ urls: ["*://*/*.gif"], types: ["image"] },
["blocking"]
);
//window.addEventListener('error', function(event) {
// if (event.message && event.message.includes('No impl for message: MozAfterPaint')) {
// // 앱으로 오류 신호 전달
// browser.runtime.sendMessage({ type: 'RELOAD_REQUEST' });
// } // }
//}); //}, {urls: ["<all_urls>"]});
//
////
////browser.webRequest.onHeadersReceived.addListener(
//// function(details) {
//// let headers = details.responseHeaders || [];
//// // Cache-Control 헤더가 없거나 no-store, no-cache 등일 때 수정
//// let found = false;
//// for (let header of headers) {
//// if (header.name.toLowerCase() === "cache-control") {
//// header.value = "public, max-age=31536000"; // 1년 캐시
//// found = true;
//// }
//// }
//// if (!found) {
//// headers.push({name: "Cache-Control", value: "public, max-age=31536000"});
//// }
//// return {responseHeaders: headers};
//// },
//// {urls: ["*://*/*.gif"], types: ["image"]},
//// ["blocking", "responseHeaders"]
////);
//////const originalLog = console.log;
//////console.log = function(...args) {
////// // 메시지 가공 또는 네이티브로 전달
////// originalLog.apply(console, args);
////// browser.runtime.sendNativeMessage("browser", args);
//////};
////
////browser.webRequest.onBeforeRequest.addListener(
//// function(details) {
//// return { cancel: true };
//// },
//// { urls: ["*://*/*.gif"], types: ["image"] },
//// ["blocking"]
////);
////window.addEventListener('error', function(event) {
//// if (event.message && event.message.includes('No impl for message: MozAfterPaint')) {
//// // 앱으로 오류 신호 전달
//// browser.runtime.sendMessage({ type: 'RELOAD_REQUEST' });
//// }
////});

View File

@ -22,7 +22,6 @@
"nativeMessagingFromContent", "nativeMessagingFromContent",
"geckoViewAddons", "geckoViewAddons",
"webRequest", "webRequest",
"webRequestBlocking", "webRequestBlocking"
"*://*/*.gif"
] ]
} }

View File

@ -21,9 +21,7 @@ package bums.lunatic.launcher
//import rasel.lunar.launcher.home.LauncherHome.Companion.rssSet //import rasel.lunar.launcher.home.LauncherHome.Companion.rssSet
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.NotificationManager
import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetManager
import android.content.ComponentName
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.res.Configuration import android.content.res.Configuration
@ -31,10 +29,8 @@ import android.graphics.Color
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Environment.isExternalStorageManager
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.provider.Settings
import android.view.KeyEvent import android.view.KeyEvent
import android.view.KeyEvent.ACTION_UP import android.view.KeyEvent.ACTION_UP
import android.view.KeyEvent.KEYCODE_BUTTON_A import android.view.KeyEvent.KEYCODE_BUTTON_A
@ -46,19 +42,14 @@ import android.view.KeyEvent.KEYCODE_BUTTON_Y
import android.view.KeyEvent.KEYCODE_DPAD_DOWN import android.view.KeyEvent.KEYCODE_DPAD_DOWN
import android.view.KeyEvent.KEYCODE_DPAD_UP import android.view.KeyEvent.KEYCODE_DPAD_UP
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View
import android.view.WindowInsets import android.view.WindowInsets
import android.view.WindowManager import android.view.WindowManager
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.OneTimeWorkRequest import androidx.work.OneTimeWorkRequest
@ -70,24 +61,20 @@ import bums.lunatic.launcher.tokiz.Novels
import bums.lunatic.launcher.common.CommonActivity import bums.lunatic.launcher.common.CommonActivity
import bums.lunatic.launcher.databinding.LauncherActivityBinding import bums.lunatic.launcher.databinding.LauncherActivityBinding
import bums.lunatic.launcher.feeds.WidgetHost import bums.lunatic.launcher.feeds.WidgetHost
import bums.lunatic.launcher.helpers.BluetoothManager
import bums.lunatic.launcher.helpers.Constants.Companion.KEY_APPLICATION_THEME
import bums.lunatic.launcher.helpers.Constants.Companion.KEY_STATUS_BAR import bums.lunatic.launcher.helpers.Constants.Companion.KEY_STATUS_BAR
import bums.lunatic.launcher.helpers.Constants.Companion.PREFS_SETTINGS import bums.lunatic.launcher.helpers.Constants.Companion.PREFS_SETTINGS
import bums.lunatic.launcher.helpers.Constants.Companion.widgetHostId import bums.lunatic.launcher.helpers.Constants.Companion.widgetHostId
import bums.lunatic.launcher.helpers.HeadsetActionButtonReceiver import bums.lunatic.launcher.helpers.HeadsetActionButtonReceiver
import bums.lunatic.launcher.helpers.PrefLong 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.home.RssViewBuilder
import bums.lunatic.launcher.model.RssData import bums.lunatic.launcher.model.RssData
import bums.lunatic.launcher.model.RssDataType import bums.lunatic.launcher.model.RssDataType
import bums.lunatic.launcher.receiver.NLService
import bums.lunatic.launcher.tokiz.Comics import bums.lunatic.launcher.tokiz.Comics
import bums.lunatic.launcher.tokiz.Webtoons import bums.lunatic.launcher.tokiz.Webtoons
import bums.lunatic.launcher.utils.Blog import bums.lunatic.launcher.utils.Blog
import bums.lunatic.launcher.utils.FeedParseManager import bums.lunatic.launcher.utils.FeedParseManager
import bums.lunatic.launcher.utils.getJ import bums.lunatic.launcher.utils.getJ
import bums.lunatic.launcher.view.TableRadioGroup
import bums.lunatic.launcher.workers.AppInfoGetter import bums.lunatic.launcher.workers.AppInfoGetter
import bums.lunatic.launcher.workers.ArcaGetter import bums.lunatic.launcher.workers.ArcaGetter
import bums.lunatic.launcher.workers.CalendarGetter import bums.lunatic.launcher.workers.CalendarGetter
@ -101,7 +88,6 @@ import bums.lunatic.launcher.workers.FmKoreaGetter.Companion.COMIC_WORK_TAG
import bums.lunatic.launcher.workers.LocationGetter import bums.lunatic.launcher.workers.LocationGetter
import bums.lunatic.launcher.workers.NewsFeedsGetter import bums.lunatic.launcher.workers.NewsFeedsGetter
import bums.lunatic.launcher.workers.NewsFeedsGetter.Companion.FEDDS_WORK_TAG import bums.lunatic.launcher.workers.NewsFeedsGetter.Companion.FEDDS_WORK_TAG
import bums.lunatic.launcher.workers.OpenWeatherGetter
import bums.lunatic.launcher.workers.RecentCallGetter import bums.lunatic.launcher.workers.RecentCallGetter
import bums.lunatic.launcher.workers.RecentSmsGetter import bums.lunatic.launcher.workers.RecentSmsGetter
import bums.lunatic.launcher.workers.RecentSmsGetter.Companion.SMS_WORK_TAG import bums.lunatic.launcher.workers.RecentSmsGetter.Companion.SMS_WORK_TAG
@ -627,7 +613,7 @@ internal class LauncherActivity : CommonActivity() {
when(id) { when(id) {
R.id.feeds -> { R.id.feeds -> {
supportFragmentManager.beginTransaction() supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, LauncherHome()) .replace(R.id.fragment_container, RssHome())
.commit() .commit()
} }
R.id.books ->{ R.id.books ->{
@ -743,7 +729,7 @@ internal class LauncherActivity : CommonActivity() {
override fun handleOnBackPressed() { override fun handleOnBackPressed() {
val currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container) val currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container)
when(currentFragment) { when(currentFragment) {
is LauncherHome ->{ is RssHome ->{
currentFragment.doNextPage() currentFragment.doNextPage()
} }
} }

View File

@ -1,12 +1,17 @@
package bums.lunatic.launcher.home package bums.lunatic.launcher.home
import android.app.DownloadManager
import android.content.Context import android.content.Context
import android.content.Intent 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.net.Uri
import android.os.Environment
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.os.Message import android.os.Message
import android.util.AttributeSet import android.util.AttributeSet
import android.util.Base64
import android.util.Log import android.util.Log
import android.view.KeyEvent import android.view.KeyEvent
import android.view.KeyEvent.ACTION_UP import android.view.KeyEvent.ACTION_UP
@ -18,7 +23,10 @@ import android.view.KeyEvent.KEYCODE_BUTTON_X
import android.view.KeyEvent.KEYCODE_BUTTON_Y import android.view.KeyEvent.KEYCODE_BUTTON_Y
import android.view.KeyEvent.KEYCODE_DPAD_DOWN import android.view.KeyEvent.KEYCODE_DPAD_DOWN
import android.view.KeyEvent.KEYCODE_DPAD_UP import android.view.KeyEvent.KEYCODE_DPAD_UP
import android.view.View
import android.widget.ProgressBar import android.widget.ProgressBar
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import bums.lunatic.launcher.LauncherActivity.Companion.getRuntime import bums.lunatic.launcher.LauncherActivity.Companion.getRuntime
import bums.lunatic.launcher.tokiz.view.BWebview import bums.lunatic.launcher.tokiz.view.BWebview
import bums.lunatic.launcher.tokiz.view.JxEvent import bums.lunatic.launcher.tokiz.view.JxEvent
@ -39,13 +47,18 @@ import org.mozilla.geckoview.WebExtension.MessageDelegate
import org.mozilla.geckoview.WebExtension.PortDelegate import org.mozilla.geckoview.WebExtension.PortDelegate
import org.mozilla.geckoview.WebExtensionController.AddonManagerDelegate import org.mozilla.geckoview.WebExtensionController.AddonManagerDelegate
import org.mozilla.geckoview.WebRequestError import org.mozilla.geckoview.WebRequestError
import java.io.File
import java.text.SimpleDateFormat
class GeckoWeb : BWebview { class GeckoWeb : BWebview {
constructor(context: Context?) : super(context) { constructor(context: Context?) : super(context) {
buildWeb() 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) { constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
buildWeb() buildWeb()
@ -54,6 +67,7 @@ class GeckoWeb : BWebview {
val mPortNam = "browser" val mPortNam = "browser"
val extPath = "resource://android/assets/extensions/my_extension/" val extPath = "resource://android/assets/extensions/my_extension/"
val extId = "messaging@booktoki468.com" val extId = "messaging@booktoki468.com"
private fun buildWeb() { private fun buildWeb() {
getRuntime()?.let { getRuntime()?.let {
val session: GeckoSession = GeckoSession() val session: GeckoSession = GeckoSession()
@ -87,7 +101,7 @@ class GeckoWeb : BWebview {
var lastedUrl: String? = null var lastedUrl: String? = null
var canGoBack: Boolean? = null var canGoBack: Boolean? = null
var mPort: WebExtension.Port? = null var mPort: WebExtension.Port? = null
var mCaache : WebExtension.Port? = null
object WebExtensionInfo { object WebExtensionInfo {
val mPortNam = "browser" val mPortNam = "browser"
val extPath = "resource://android/assets/extensions/my_extension/" val extPath = "resource://android/assets/extensions/my_extension/"
@ -238,6 +252,32 @@ class GeckoWeb : BWebview {
return super.onRecordMalformedConfigurationEvent(feature, part) 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()
}
val contentDelegate = object : GeckoSession.ContentDelegate { val contentDelegate = object : GeckoSession.ContentDelegate {
override fun onTitleChange( override fun onTitleChange(
session: GeckoSession, session: GeckoSession,
@ -260,6 +300,21 @@ class GeckoWeb : BWebview {
super.onFirstContentfulPaint(session) 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 { val progressDelegate = object : GeckoSession.ProgressDelegate {
override fun onSecurityChange( override fun onSecurityChange(
@ -289,6 +344,8 @@ class GeckoWeb : BWebview {
Uri.parse(url)?.let { uri -> Uri.parse(url)?.let { uri ->
context.startActivity(Intent().apply { context.startActivity(Intent().apply {
action = Intent.ACTION_VIEW action = Intent.ACTION_VIEW
flags = Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS.or(FLAG_ACTIVITY_CLEAR_TOP).or(
FLAG_ACTIVITY_NEW_TASK)
data = uri data = uri
}) })
} }
@ -328,7 +385,18 @@ class GeckoWeb : BWebview {
): GeckoResult<GeckoSession>? { ): GeckoResult<GeckoSession>? {
Blog.LOGE("GeckoView", "onNewSession: $session from WebExtension") 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) return super.onNewSession(session, uri)
} }
@ -377,17 +445,85 @@ class GeckoWeb : BWebview {
override fun onDisconnect(port: WebExtension.Port) { override fun onDisconnect(port: WebExtension.Port) {
// This port is not usable anymore. // This port is not usable anymore.
if (port === mPort) {
mPort = null 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 === 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 = val messageDelegate: MessageDelegate =
object : MessageDelegate { object : MessageDelegate {
override fun onConnect(port: WebExtension.Port) { override fun onConnect(port: WebExtension.Port) {
mPort = port Blog.LOGE("onConnect port >>> ${port.name}")
mPort!!.setDelegate(portDelegate) // if (port.name === "browser") {
// mPort = port
// mPort!!.setDelegate(portDelegate)
// }
} }
override fun onMessage( override fun onMessage(
@ -401,11 +537,8 @@ class GeckoWeb : BWebview {
) )
return super.onMessage(nativeApp, message, sender) return super.onMessage(nativeApp, message, sender)
} }
} }
fun onStart() { fun onStart() {
} }

View File

@ -20,7 +20,6 @@ package bums.lunatic.launcher.home
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
@ -30,7 +29,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.annotation.NonNull import androidx.annotation.NonNull
import androidx.annotation.RequiresApi import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
@ -38,7 +37,6 @@ import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
import bums.lunatic.launcher.R import bums.lunatic.launcher.R
import bums.lunatic.launcher.common.letTrue
import bums.lunatic.launcher.databinding.LauncherHomeBinding import bums.lunatic.launcher.databinding.LauncherHomeBinding
import bums.lunatic.launcher.helpers.Constants.Companion.PREFS_SETTINGS import bums.lunatic.launcher.helpers.Constants.Companion.PREFS_SETTINGS
import bums.lunatic.launcher.home.adapters.RssItemAdapter import bums.lunatic.launcher.home.adapters.RssItemAdapter
@ -47,7 +45,6 @@ import bums.lunatic.launcher.home.adapters.SwipeToDeleteCallback
import bums.lunatic.launcher.model.RssData import bums.lunatic.launcher.model.RssData
import bums.lunatic.launcher.model.RssDataType import bums.lunatic.launcher.model.RssDataType
import bums.lunatic.launcher.model.WeatherForcast import bums.lunatic.launcher.model.WeatherForcast
import bums.lunatic.launcher.openClient
import bums.lunatic.launcher.openReddit import bums.lunatic.launcher.openReddit
import bums.lunatic.launcher.openYouTube import bums.lunatic.launcher.openYouTube
import bums.lunatic.launcher.tokiz.view.JxEvent import bums.lunatic.launcher.tokiz.view.JxEvent
@ -59,7 +56,9 @@ import bums.lunatic.launcher.workers.WorkersDb
import com.google.android.material.imageview.ShapeableImageView import com.google.android.material.imageview.ShapeableImageView
import io.realm.kotlin.UpdatePolicy import io.realm.kotlin.UpdatePolicy
import io.realm.kotlin.ext.query import io.realm.kotlin.ext.query
import io.realm.kotlin.notifications.InitialResults
import io.realm.kotlin.notifications.ResultsChange import io.realm.kotlin.notifications.ResultsChange
import io.realm.kotlin.notifications.UpdatedResults
import io.realm.kotlin.query.RealmQuery import io.realm.kotlin.query.RealmQuery
import io.realm.kotlin.query.RealmResults import io.realm.kotlin.query.RealmResults
import io.realm.kotlin.query.Sort import io.realm.kotlin.query.Sort
@ -69,7 +68,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
internal class LauncherHome : Fragment() { internal class RssHome : Fragment() {
lateinit var binding: LauncherHomeBinding lateinit var binding: LauncherHomeBinding
private lateinit var fragManager: FragmentManager private lateinit var fragManager: FragmentManager
@ -77,11 +76,10 @@ internal class LauncherHome : Fragment() {
private var shouldResume = true private var shouldResume = true
companion object { companion object {
var home: LauncherHome? = null var home: RssHome? = null
var lastedFinishedPageUrl: String = "" var lastedFinishedPageUrl: String = ""
} }
val UPDATE_DELAY = 5L
val commandHandler = Handler(Looper.getMainLooper()) val commandHandler = Handler(Looper.getMainLooper())
val infoUpdate = Runnable { chooseAdpater() } val infoUpdate = Runnable { chooseAdpater() }
@ -245,6 +243,7 @@ internal class LauncherHome : Fragment() {
targetList.addAll(setString) targetList.addAll(setString)
binding.geckoWeb.loadUrl(rssId) binding.geckoWeb.loadUrl(rssId)
binding.vote.visibility = binding.geckoWeb.visibility
} }
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
@ -270,6 +269,42 @@ internal class LauncherHome : Fragment() {
return@setOnTouchListener false return@setOnTouchListener false
} }
} }
binding.vote.setOnClickListener {
if (binding.geckoWeb.isVisible) {
vote()
}
}
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
}
queryInfos()
}
binding.bookmark.setOnClickListener {
queryVotes()
}
queryInfos() queryInfos()
binding.geckoWeb.progress = binding.progressBar binding.geckoWeb.progress = binding.progressBar
binding.geckoWeb.jxInteface = { jxEvent -> binding.geckoWeb.jxInteface = { jxEvent ->
@ -287,9 +322,11 @@ 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.root.setPointerIcon(nullCursor)
binding.geckoWeb.decoViews.add(binding.hide)
binding.geckoWeb.decoViews.add(binding.vote)
binding.geckoWeb.decoViews.add(binding.progressBar)
return binding.root return binding.root
} }
@ -352,11 +389,24 @@ internal class LauncherHome : Fragment() {
mRssDataResult?.asFlow()?.let { flow -> mRssDataResult?.asFlow()?.let { flow ->
infosJob = CoroutineScope(Dispatchers.IO).launch { infosJob = CoroutineScope(Dispatchers.IO).launch {
flow.collect { changes: ResultsChange<RssData> -> flow.collect { changes: ResultsChange<RssData> ->
commandHandler.removeCallbacks(infoUpdate) when(changes) {
WorkersDb.getRealm().apply { is InitialResults -> {
lasted = copyFromRealm(changes.list) commandHandler.removeCallbacks(infoUpdate)
WorkersDb.getRealm().apply {
lasted = 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() infosJob?.start()

View File

@ -1166,7 +1166,6 @@ abstract class BaseToki : Fragment(), PagedTextViewInterface {
} }
} }
} }
} }
fun onLoadedContents(aContents: String) { fun onLoadedContents(aContents: String) {

View File

@ -1,6 +1,6 @@
package bums.lunatic.launcher.utils 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.MostItem import bums.lunatic.launcher.model.MostItem
import bums.lunatic.launcher.model.RssData import bums.lunatic.launcher.model.RssData
import bums.lunatic.launcher.model.RssDataType import bums.lunatic.launcher.model.RssDataType

View File

@ -1,8 +1,8 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:height="24dp" android:width="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportHeight="24" android:viewportWidth="24" android:viewportHeight="24"
android:tint="?attr/colorControlNormal"> android:tint="@color/white">
<path <path
android:fillColor="?android:attr/textColorPrimary" android:fillColor="@color/white"
android:pathData="M7.7,20.5Q6.775,20.5 6.163,19.887Q5.55,19.275 5.55,18.35V5.9H4.55V4.55H8.95V3.65H15.1V4.55H19.5V5.9H18.5V18.35Q18.5,19.275 17.888,19.887Q17.275,20.5 16.35,20.5ZM17.15,5.9H6.9V18.35Q6.9,18.7 7.125,18.925Q7.35,19.15 7.7,19.15H16.35Q16.65,19.15 16.9,18.9Q17.15,18.65 17.15,18.35ZM9.525,17.125H10.875V7.925H9.525ZM13.175,17.125H14.525V7.925H13.175ZM6.9,5.9V18.35Q6.9,18.7 6.9,18.925Q6.9,19.15 6.9,19.15Q6.9,19.15 6.9,18.925Q6.9,18.7 6.9,18.35Z"/> android:pathData="M7.7,20.5Q6.775,20.5 6.163,19.887Q5.55,19.275 5.55,18.35V5.9H4.55V4.55H8.95V3.65H15.1V4.55H19.5V5.9H18.5V18.35Q18.5,19.275 17.888,19.887Q17.275,20.5 16.35,20.5ZM17.15,5.9H6.9V18.35Q6.9,18.7 7.125,18.925Q7.35,19.15 7.7,19.15H16.35Q16.65,19.15 16.9,18.9Q17.15,18.65 17.15,18.35ZM9.525,17.125H10.875V7.925H9.525ZM13.175,17.125H14.525V7.925H13.175ZM6.9,5.9V18.35Q6.9,18.7 6.9,18.925Q6.9,19.15 6.9,19.15Q6.9,19.15 6.9,18.925Q6.9,18.7 6.9,18.35Z"/>
</vector> </vector>

View File

@ -1,45 +1,105 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout <layout xmlns:tools="http://schemas.android.com/tools">
xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:layout_margin="@dimen/default_layout_margin"
android:id="@+id/infoList"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:overScrollMode="never"
android:padding="@dimen/default_padding" <ImageButton
android:scrollbars="none" app:layout_constraintTop_toTopOf="parent"
android:visibility="visible" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent" android:id="@+id/vote"
app:layout_constraintRight_toRightOf="parent" android:scaleType="fitCenter"
app:layout_constraintTop_toTopOf="parent" android:adjustViewBounds="true"
app:layout_constraintBottom_toBottomOf="parent" android:visibility="visible"
/> android:background="@null"
android:src="@drawable/saved"
android:layout_width="40dp"
tools:ignore="ContentDescription"
android:layout_height="40dp" />
<ImageButton
android:id="@+id/hide"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toLeftOf="@id/vote"
android:src="@drawable/ic_delete"
android:tintMode="multiply"
android:layout_marginLeft="12dp"
android:scaleType="fitCenter"
android:background="@null"
android:layout_width="40dp"
android:visibility="visible"
android:adjustViewBounds="true"
tools:ignore="ContentDescription"
android:layout_height="40dp"
/>
<ImageButton
android:id="@+id/home"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:src="@drawable/home"
android:tintMode="multiply"
android:scaleType="fitCenter"
android:background="@null"
android:layout_width="40dp"
android:adjustViewBounds="true"
android:layout_height="40dp"
app:tint="@color/white"
tools:ignore="ContentDescription" />
<ImageButton
android:id="@+id/bookmark"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toRightOf="@id/home"
android:src="@drawable/bookmark"
android:scaleType="fitCenter"
android:background="@null"
android:layout_width="40dp"
android:adjustViewBounds="true"
tools:ignore="ContentDescription"
android:layout_height="40dp"/>
<androidx.recyclerview.widget.RecyclerView
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:layout_margin="@dimen/default_layout_margin"
android:id="@+id/infoList"
android:layout_width="match_parent"
android:overScrollMode="never"
android:padding="@dimen/default_padding"
android:scrollbars="none"
android:visibility="visible"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/bookmark"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
/>
<bums.lunatic.launcher.home.GeckoWeb <bums.lunatic.launcher.home.GeckoWeb
android:id="@+id/geckoWeb" android:id="@+id/geckoWeb"
android:visibility="gone" android:visibility="gone"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" app:layout_constraintBottom_toBottomOf="parent"
/> app:layout_constraintLeft_toLeftOf="parent"
<ProgressBar app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/geckoWeb" app:layout_constraintTop_toBottomOf="@id/bookmark"
app:layout_constraintLeft_toLeftOf="@id/geckoWeb" android:layout_height="0dp"
app:layout_constraintRight_toRightOf="@id/geckoWeb" />
android:id="@+id/progressBar" <ProgressBar
style="?android:attr/progressBarStyleHorizontal" app:layout_constraintTop_toTopOf="@id/geckoWeb"
android:layout_width="0dp" app:layout_constraintLeft_toLeftOf="@id/geckoWeb"
android:layout_height="4dp" app:layout_constraintRight_toRightOf="@id/geckoWeb"
android:max="100" android:id="@+id/progressBar"
android:progress="0" style="?android:attr/progressBarStyleHorizontal"
android:visibility="visible" android:layout_width="0dp"
android:indeterminate="false"/> android:layout_height="4dp"
</androidx.constraintlayout.widget.ConstraintLayout> android:max="100"
android:progress="0"
android:visibility="visible"
android:indeterminate="false"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>