This commit is contained in:
lunaticbum 2025-08-22 17:06:04 +09:00
parent a11e40c56c
commit fd0d61c584
9 changed files with 75 additions and 25 deletions

View File

@ -306,15 +306,21 @@ document.addEventListener('DOMContentLoaded', function () {
} }
}) })
if(location.href.search("youtube") < 0) {
const keywords = ["youtube", "mojeek"];
const url = location.href;
if (keywords.every(keyword => !url.includes(keyword))) {
document.addEventListener('touchstart', function(e) { document.addEventListener('touchstart', function(e) {
console.log('터치 시작'); console.log('터치 시작');
}); });
document.addEventListener('touchend', function(e) { document.addEventListener('touchend', function(e) {
autoScrollAndSave()
setTimeout(() => {
autoScrollAndSave();
}, 5);
}); });
} }
function scrollToLazyImg(fastMode) { function scrollToLazyImg(fastMode) {
(function(autoScrollAndSave){ (function(autoScrollAndSave){
// 한 번에 이동할 픽셀 // 한 번에 이동할 픽셀

View File

@ -186,8 +186,6 @@ class ForeGroundService : Service() {
.setSmallIcon(R.drawable.ic_b) .setSmallIcon(R.drawable.ic_b)
.setContentIntent(pendingIntent) .setContentIntent(pendingIntent)
.addAction(android.R.drawable.ic_btn_speak_now,"퇴근", makeSendMsgAction(0,"돼지 퇴근했다요~!")) .addAction(android.R.drawable.ic_btn_speak_now,"퇴근", makeSendMsgAction(0,"돼지 퇴근했다요~!"))
.addAction(android.R.drawable.ic_btn_speak_now,"버스 탐", makeSendMsgAction(1,"돼지 버스 탔다요~!"))
.addAction(android.R.drawable.ic_btn_speak_now,"버스 내림", makeSendMsgAction(2,"돼지 버스 내린다요~!"))
.setOngoing(true) // 사용자가 알림을 스와이프로 지울 수 없게 만듦 .setOngoing(true) // 사용자가 알림을 스와이프로 지울 수 없게 만듦
.setProgress(max, progress, false) .setProgress(max, progress, false)
.build()) .build())
@ -205,7 +203,7 @@ class ForeGroundService : Service() {
Blog.LOGE("onBind intent >>> ${intent}") Blog.LOGE("onBind intent >>> ${intent}")
return null return null
} }
private val CHANNEL_ID = "ble_service_channel" private val CHANNEL_ID = "bums_service_channel"
private val CHANNEL_NAME = "BUM'S 서비스" private val CHANNEL_NAME = "BUM'S 서비스"

View File

@ -316,10 +316,11 @@ internal class RssHome : Fragment() {
override fun onSearch( override fun onSearch(
keyword: String, keyword: String,
category: List<String>, category: List<String>,
onlyVote : Boolean,
addReaded: Boolean, addReaded: Boolean,
addVoted: Boolean addVoted: Boolean
) { ) {
queryInfos(keywords = keyword.split(" "), category , addReaded, addVoted) queryInfos(keywords = keyword.split(" "), category ,onlyVote, addReaded, addVoted)
} }
} }
bottomSheet.show(childFragmentManager, "SearchBottomSheet") bottomSheet.show(childFragmentManager, "SearchBottomSheet")
@ -340,11 +341,20 @@ internal class RssHome : Fragment() {
privateMode: Boolean privateMode: Boolean
) { ) {
when (category) { when (category) {
RssDataType.ANYTHING -> {
if (binding.geckoWeb.lastedUrl?.contains("search.brave") == true) {
binding.geckoWeb.sendSearch(keyword)
} else {
binding.geckoWeb.loadUrl("https://search.brave.com/", if (keyword.length ?: 0 > 0) { "search?q=${keyword}" } else { null })
}
binding.geckoWeb.privateMode = false
}
RssDataType.GOOGLE -> { RssDataType.GOOGLE -> {
if (binding.geckoWeb.lastedUrl?.contains("www.google") == true) { if (binding.geckoWeb.lastedUrl?.contains("www.google") == true) {
binding.geckoWeb.sendSearch(keyword) binding.geckoWeb.sendSearch(keyword)
} else { } else {
binding.geckoWeb.loadUrl("https://www.google.com/", if (keyword.length ?: 0 > 0) { "search/?q=${keyword}" } else { null }) binding.geckoWeb.loadUrl("https://www.google.com/", if (keyword.length ?: 0 > 0) { "search?q=${keyword}" } else { null })
} }
binding.geckoWeb.privateMode = false binding.geckoWeb.privateMode = false
} }
@ -400,6 +410,7 @@ internal class RssHome : Fragment() {
var currentRss : RssData? = null var currentRss : RssData? = null
@SuppressLint("SimpleDateFormat") @SuppressLint("SimpleDateFormat")
fun openGecko(rssData: RssData? = null) { fun openGecko(rssData: RssData? = null) {
Blog.LOGE("rssData >>> ${rssData}")
binding.layoutRssSummary.root.visibility = View.GONE binding.layoutRssSummary.root.visibility = View.GONE
if (rssData?.category()?.equals(RssDataType.PRIVATE) == false && rssData?.originPage?.isNotEmpty() == true) { if (rssData?.category()?.equals(RssDataType.PRIVATE) == false && rssData?.originPage?.isNotEmpty() == true) {
rssData?.let { rss -> rssData?.let { rss ->
@ -692,7 +703,7 @@ internal class RssHome : Fragment() {
Blog.LOGE("updateQuery >>> ${q.description()}") Blog.LOGE("updateQuery >>> ${q.description()}")
infosJob?.cancel() infosJob?.cancel()
commandHandler.removeCallbacks(infoUpdate) commandHandler.removeCallbacks(infoUpdate)
mLastedQuery = q.query("hide != $0", true).sort("pubDate ", Sort.DESCENDING).limit(300).distinct("originPage", "title") mLastedQuery = q.query("hide != $0", true).sort("pubDate ", Sort.DESCENDING).limit(500).distinct("originPage", "title")
mRssDataResult = mLastedQuery?.find() mRssDataResult = mLastedQuery?.find()
mRssDataResult?.asFlow()?.let { flow -> mRssDataResult?.asFlow()?.let { flow ->
infosJob = CoroutineScope(Dispatchers.IO).launch { infosJob = CoroutineScope(Dispatchers.IO).launch {
@ -769,13 +780,23 @@ internal class RssHome : Fragment() {
fun queryInfos( fun queryInfos(
keywords: List<String>, keywords: List<String>,
category: List<String>, category: List<String>,
onlyVote : Boolean = false,
includeVote : Boolean = false, includeVote : Boolean = false,
includeRead : Boolean = false includeRead : Boolean = false
) { ) {
beforeQuery() beforeQuery()
var rQ = getRealm().query<RssData>().sort("read", Sort.ASCENDING) var rQ = getRealm().query<RssData>().sort("read", Sort.ASCENDING)
if (!includeRead) { rQ = rQ.query("read == $0", 0)} if (onlyVote) {
if (!includeVote) { rQ = rQ.query("vote != $0", true)} rQ = rQ.query("vote == $0", true)
} else {
if (!includeRead) {
rQ = rQ.query("read == $0", 0)
}
if (!includeVote) {
rQ = rQ.query("vote != $0", true)
}
}
category.forEach { category.forEach {
rQ = rQ.query("category != $0", it) rQ = rQ.query("category != $0", it)
} }
@ -960,7 +981,7 @@ internal class RssHome : Fragment() {
imageView.visibility = View.INVISIBLE imageView.visibility = View.INVISIBLE
} }
} }
fun randomOrNull() : RssData? = lasted.filter { it.vote == false && it.read < 2 }.randomOrNull() fun randomOrNull() : RssData? = lasted.randomOrNull()
} }
var toast: Toast? = null var toast: Toast? = null
fun Context.toast(string: String) { fun Context.toast(string: String) {

View File

@ -40,22 +40,25 @@ class SearchBottomSheet : BottomSheetDialogFragment() {
} }
interface OnSearchListener { interface OnSearchListener {
fun onSearch(keyword: String, category: List<String>, addReaded : Boolean, addVoted: Boolean) fun onSearch(keyword: String, category: List<String>, onlyVoted : Boolean, addReaded : Boolean, addVoted: Boolean)
} }
var listener: OnSearchListener? = null var listener: OnSearchListener? = null
private val categoryStates = mutableMapOf<String, Boolean>() private val categoryStates = mutableMapOf<String, Boolean>()
lateinit var onlyVote : CheckBox
lateinit var addVote : CheckBox lateinit var addVote : CheckBox
lateinit var addRead : CheckBox lateinit var addRead : CheckBox
lateinit var inputKeyword : EditText lateinit var inputKeyword : EditText
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
inputKeyword = view.findViewById<EditText>(R.id.inputKeyword) inputKeyword = view.findViewById<EditText>(R.id.inputKeyword)
val categoryContainer = view.findViewById<LinearLayout>(R.id.categoryContainer) val categoryContainer = view.findViewById<LinearLayout>(R.id.categoryContainer)
onlyVote = view.findViewById<CheckBox>(R.id.only_vote) as CheckBox
addVote = view.findViewById<CheckBox>(R.id.add_vote) as CheckBox addVote = view.findViewById<CheckBox>(R.id.add_vote) as CheckBox
addRead = view.findViewById<CheckBox>(R.id.add_read) as CheckBox addRead = view.findViewById<CheckBox>(R.id.add_read) as CheckBox
addVote.setOnCheckedChangeListener {v,b->triggerSearchWithDebounce(inputKeyword.text.toString())} addVote.setOnCheckedChangeListener {v,b->triggerSearchWithDebounce(inputKeyword.text.toString())}
addRead.setOnCheckedChangeListener {v,b->triggerSearchWithDebounce(inputKeyword.text.toString())} addRead.setOnCheckedChangeListener {v,b->triggerSearchWithDebounce(inputKeyword.text.toString())}
onlyVote.setOnCheckedChangeListener {v,b->triggerSearchWithDebounce(inputKeyword.text.toString())}
// 카테고리 목록 // 카테고리 목록
val categories = RssDataType.getAll() val categories = RssDataType.getAll()
categories.forEach { categoryStates[it.name] = true } categories.forEach { categoryStates[it.name] = true }
@ -112,10 +115,8 @@ class SearchBottomSheet : BottomSheetDialogFragment() {
inputKeyword.setOnEditorActionListener { _, actionId, _ -> inputKeyword.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_SEARCH) { if (actionId == EditorInfo.IME_ACTION_SEARCH) {
val keyword = inputKeyword.text.toString() val keyword = inputKeyword.text.toString()
if (keyword.isNotEmpty()) { triggerSearchWithDebounce(keyword)
triggerSearchWithDebounce(keyword) dismiss() // 필요 시 닫기
dismiss() // 필요 시 닫기
}
true true
} else { } else {
false false
@ -128,7 +129,7 @@ class SearchBottomSheet : BottomSheetDialogFragment() {
searchRunnable?.let { debounceHandler.removeCallbacks(it) } searchRunnable?.let { debounceHandler.removeCallbacks(it) }
searchRunnable = Runnable { searchRunnable = Runnable {
val disabledCategories = categoryStates.filter { it.value == false }.keys.toList() val disabledCategories = categoryStates.filter { it.value == false }.keys.toList()
listener?.onSearch(keyword, disabledCategories, addRead.isChecked,addVote.isChecked) listener?.onSearch(keyword, disabledCategories,onlyVote.isChecked ,addRead.isChecked,addVote.isChecked)
} }
debounceHandler.postDelayed(searchRunnable!!, debounceDelay) debounceHandler.postDelayed(searchRunnable!!, debounceDelay)
} }

View File

@ -54,9 +54,11 @@ class WebBottomSheet : BottomSheetDialogFragment() {
inputKeyword = view.findViewById<EditText>(R.id.inputKeyword) inputKeyword = view.findViewById<EditText>(R.id.inputKeyword)
val categoryContainer = view.findViewById<RadioGroup>(R.id.categoryContainer) val categoryContainer = view.findViewById<RadioGroup>(R.id.categoryContainer)
privateMode = view.findViewById<CheckBox>(R.id.check_private) as CheckBox privateMode = view.findViewById<CheckBox>(R.id.check_private) as CheckBox
privateMode.setOnCheckedChangeListener {v,b->triggerSearchWithDebounce(inputKeyword.text.toString())} privateMode.setOnCheckedChangeListener {v,b->
// triggerSearchWithDebounce(inputKeyword.text.toString())
}
// 카테고리 목록 // 카테고리 목록
val categories = listOf<RssDataType>(RssDataType.GOOGLE, RssDataType.NAVER,RssDataType.NEWS,RssDataType.NEWSFEED,RssDataType.NAMU,RssDataType.PRIVATE) val categories = listOf<RssDataType>(RssDataType.ANYTHING,RssDataType.GOOGLE, RssDataType.NAVER,RssDataType.NEWS,RssDataType.NEWSFEED,RssDataType.NAMU,RssDataType.PRIVATE)
// 버튼 동적 생성 // 버튼 동적 생성
categoryContainer.removeAllViews() categoryContainer.removeAllViews()
@ -80,7 +82,7 @@ class WebBottomSheet : BottomSheetDialogFragment() {
setTextColor(Color.WHITE) setTextColor(Color.WHITE)
setBackgroundResource(R.color.tabs_black) setBackgroundResource(R.color.tabs_black)
setOnClickListener { setOnClickListener {
triggerSearchWithDebounce(inputKeyword.text.toString()) // triggerSearchWithDebounce(inputKeyword.text.toString())
} }
// setOnLongClickListener { // setOnLongClickListener {
// categoryContainer.forEach { (it as? RadioButton)?.let { it.isChecked = false} } // categoryContainer.forEach { (it as? RadioButton)?.let { it.isChecked = false} }
@ -104,7 +106,7 @@ class WebBottomSheet : BottomSheetDialogFragment() {
// EditText 입력 실시간 감지 + debounce // EditText 입력 실시간 감지 + debounce
inputKeyword.addTextChangedListener(object : TextWatcher { inputKeyword.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) { override fun afterTextChanged(s: Editable?) {
triggerSearchWithDebounce(s.toString()) // triggerSearchWithDebounce(s.toString())
} }
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}

View File

@ -290,8 +290,12 @@ class RssData : RealmObject, RssDataInterface {
} }
override fun category(): RssDataType { override fun category(): RssDataType {
if (mRssDataType == null) try {
mRssDataType = RssDataType.valueOf(category!!.uppercase()) if (mRssDataType == null)
mRssDataType = RssDataType.valueOf(category!!.uppercase())
} catch (e: Exception) {
mRssDataType = RssDataType.NO_DATA
}
return mRssDataType!! return mRssDataType!!
} }

View File

@ -22,6 +22,7 @@ enum class RssDataType {
THEQOO, THEQOO,
NAVER, NAVER,
GOOGLE, GOOGLE,
ANYTHING,
NAMU, NAMU,
ARCA; ARCA;

View File

@ -4,6 +4,8 @@ import android.content.Context
import androidx.annotation.CallSuper import androidx.annotation.CallSuper
import androidx.work.Worker import androidx.work.Worker
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import bums.lunatic.launcher.LauncherActivity
import bums.lunatic.launcher.LunaticLauncher
import bums.lunatic.launcher.model.RssData import bums.lunatic.launcher.model.RssData
import bums.lunatic.launcher.utils.beforeDay import bums.lunatic.launcher.utils.beforeDay
import bums.lunatic.launcher.utils.beforeOneDay import bums.lunatic.launcher.utils.beforeOneDay
@ -33,6 +35,7 @@ open abstract class BaseGetter : Worker {
@CallSuper @CallSuper
override fun doWork(): Result { override fun doWork(): Result {
LunaticLauncher.mHourlyLogWriter?.writeLog("${this::class.java.simpleName} doWork()")
val currentTime = before10Min() val currentTime = before10Min()
if (lastedUpdateTime > 0L && currentTime > lastedUpdateTime) { if (lastedUpdateTime > 0L && currentTime > lastedUpdateTime) {
return Result.success().apply { return Result.success().apply {
@ -40,7 +43,7 @@ open abstract class BaseGetter : Worker {
} }
} }
return realWork().apply { return realWork().apply {
LunaticLauncher.mHourlyLogWriter?.writeLog("${this@BaseGetter::class.java.simpleName} return realWork() ")
} }
} }
abstract fun realWork() : Result abstract fun realWork() : Result

View File

@ -22,6 +22,20 @@
android:singleLine="true" /> android:singleLine="true" />
<CheckBox
app:layout_constraintRight_toLeftOf="@id/add_vote"
app:layout_constraintBottom_toTopOf="@+id/accessoryToolbar"
android:layout_margin="0dp"
android:padding="0dp"
android:minHeight="0dp"
android:includeFontPadding="false"
android:text="Only Vote"
android:textColor="@color/white"
android:id="@+id/only_vote"
android:checked="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<CheckBox <CheckBox
app:layout_constraintRight_toLeftOf="@id/add_read" app:layout_constraintRight_toLeftOf="@id/add_read"
app:layout_constraintBottom_toTopOf="@+id/accessoryToolbar" app:layout_constraintBottom_toTopOf="@+id/accessoryToolbar"