diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 38a19d85..b4c9a6a1 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -54,6 +54,9 @@
tools:ignore="ScopedStorage" />
+
+
+
@@ -101,6 +104,10 @@
+
1) {
+ document.querySelectorAll('[class^="w-full flex"]').forEach(function (e) {
+
+
+ var description = ""
+ if(e.querySelector('[class^="pb-2 w-100 text-gray-500"]')){
+ description = e.querySelector('[class^="pb-2 w-100 text-gray-500"]').textContent.replace(/\s+/g, ' ').trim();
+ }
+ console.log(description)
+ })
+}
\ No newline at end of file
diff --git a/app/src/main/assets/extensions/my_extension/fdfdfdf.html b/app/src/main/assets/extensions/my_extension/fdfdfdf.html
index 7c2a90fc..e69de29b 100644
--- a/app/src/main/assets/extensions/my_extension/fdfdfdf.html
+++ b/app/src/main/assets/extensions/my_extension/fdfdfdf.html
@@ -1,93 +0,0 @@
-video-item ::>
-
-
-
- |
-
-
-
-
-
-
-
-
-
-
-
-
- 2025-07-23 11:22:20.062 26023-26023 Lunatic bums.lunatic.launcher E | Download Magnet |
- 3.4gb |
- S: 41 |
- L: 667 |
- 23/07/25 |
- Decen |
-
-
- | Download Magnet |
- 1.3gb |
- S: 30 |
- L: 6 |
- 22/07/25 |
-
-
- 2025-07-23 11:22:20.062 26023-26023 Lunatic bums.lunatic.launcher E | Download Magnet |
- 5.3gb |
- S: 60 |
- L: 23 |
- 22/07/25 |
-
-
- |
- |
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/assets/extensions/my_extension/messaging.js b/app/src/main/assets/extensions/my_extension/messaging.js
index a8e26c16..ec87b460 100644
--- a/app/src/main/assets/extensions/my_extension/messaging.js
+++ b/app/src/main/assets/extensions/my_extension/messaging.js
@@ -291,6 +291,180 @@ if (port) {
e.remove()
)
}
+ if (document.querySelectorAll('[class^="code-block"]')) {
+ document.querySelectorAll('[class^="code-block"]').forEach(e => e.remove())
+ }
+ if (document.querySelectorAll('.ad-template')) {
+ document.querySelectorAll('.ad-template').forEach(function (e) {
+ e.remove()
+ })
+ }
+ if (document.querySelectorAll('[class^="popupBanner_w popupOpen"]')) {
+ document.querySelectorAll('[class^="popupBanner_w popupOpen"]').forEach(function (e) {
+ e.remove()
+ })
+ }
+
+ if (document.querySelectorAll('iframe')) {
+ document.querySelectorAll('iframe').forEach(function (e) {
+ if (e.getAttribute("src") != null && (e.getAttribute("src").search("ads") > -1 || e.getAttribute("src").search("coupang") > -1)) {
+ e.remove()
+ }
+ })
+ }
+ if (location.href.search("x.com") > -1) {
+ var mainClass = document.querySelector('section').className
+
+ document.querySelector('section').querySelectorAll('div[class="'+ mainClass +'"]').forEach(function (e) {
+ if (e.hasAttribute("data-testid") && e.querySelector('video')) {
+ e.querySelectorAll('[poster]').forEach(function (e) {
+
+ })
+ console.log(e.innerHTML)
+ }
+ })
+
+ }
+
+ if (document.querySelectorAll('[data-banner^="coupang-"]')) {
+ document.querySelectorAll('[data-banner^="coupang-"]').forEach(e => e.remove())
+ }
+
+ if (document.querySelectorAll('[id^="mobonDivBanner"]')) {
+ document.querySelectorAll('[id^="mobonDivBanner"]').forEach(e => e.remove())
+ }
+ if (document.querySelectorAll('[id^="pnlTopAd"]')) {
+ document.querySelectorAll('[id^="pnlTopAd"]').forEach(e => e.remove())
+ }
+ if (document.querySelectorAll('[class^="ad-tag"]')) {
+ document.querySelectorAll('[class^="ad-tag"]').forEach(e => e.remove())
+ }
+ if (document.querySelectorAll('[class^="science-banner-area"]')) {
+ document.querySelectorAll('[class^="science-banner-area"]').forEach(e=> e.remove())
+ }
+ if (location.href.search("torrentzota") > -1 && document.querySelectorAll('a')) {
+ document.querySelectorAll('a').forEach(function (e) {
+ if (e.getAttribute('href') != null && e.getAttribute('href').startsWith("/adver-")) {
+ e.remove()
+ }
+ }
+ )
+ if(document.querySelectorAll('[class^="py-4 flex flex-row border-b topic-item"]').length > 1) {
+ var datas = []
+ document.querySelectorAll('[class^="py-4 flex flex-row border-b topic-item"]').forEach(function (e) {
+ var title = ""
+ var link = ""
+ e.querySelectorAll("a").forEach(function (e){
+ if(e.getAttribute("class") === "item-link"){
+ title = e.getAttribute('title')
+ }
+ if(e.getAttribute("class") === "item-link"){
+ link = e.getAttribute('href')
+ if (link.length > 0) {
+ link = location.protocol + '//' + location.hostname + link
+ }
+ }
+ })
+ var description = e.querySelector('.flex-none w-16 text-center').textContent + e.querySelector('.badge badge-third w-auto px-1 flex-none mr-2 float-left').textContent
+ var md = e.querySelector(".flex-none w-14 text-center hidden md:block").textContent
+
+ let todayYear = new Date().getFullYear();
+ let now = new Date();
+ let hh = String(now.getHours()).padStart(2, '0'); // 시 (2자리)
+ let mm = String(now.getMinutes()).padStart(2, '0'); // 분 (2자리)
+ let ss = String(now.getSeconds()).padStart(2, '0'); // 초 (2자리)
+
+ let time = `${hh}:${mm}:${ss}`;
+ let combinedStr = todayYear + "-" + md + " " + time;
+ let dateObj = new Date(combinedStr);
+
+ datas.push({
+ "title" : title,
+ "description" : description,
+ "originPage" : link,
+ "pubDate" : dateObj.getTime(),
+ "category" : "TORRENT"
+ });
+ })
+ if(datas.length > 0) {
+ sendMessage(
+ {
+ type: "PRIVATES",
+ privates: datas,
+ currentPage : location.href
+ }
+ );
+ }
+ }
+ if (document.querySelectorAll('[class^="w-full flex"]').length > 1) {
+ var datas = []
+ document.querySelectorAll('[class^="w-full flex"]').forEach(function (e) {
+ var thumbnail = ""
+ e.querySelectorAll("img").forEach(function (e){
+ if(e.getAttribute("class") === "border"){
+ thumbnail = e.getAttribute('src')
+ }
+ })
+
+ var title = ""
+ var link = ""
+ e.querySelectorAll("a").forEach(function (e){
+ if(e.getAttribute("class") === "item-link"){
+ title = e.getAttribute('title')
+ }
+ if(e.getAttribute("class") === "item-link"){
+ link = e.getAttribute('href')
+ if (link.length > 0) {
+ link = location.protocol + '//' + location.hostname + link
+ }
+ }
+ })
+
+
+ var description = ""
+ var time = ""
+ var md = ""
+ if(e.querySelector('[class^="pb-2 w-100 text-gray-500"]')){
+ description = e.querySelector('[class^="pb-2 w-100 text-gray-500"]').textContent.replace(/\s+/g, ' ').trim();
+ md = description.split(" ")[1].trim();
+ time = description.split(" ")[2].trim();
+ description = description.split(" ")[0].trim();
+ }
+ console.log(link)
+ console.log(thumbnail)
+ console.log(title)
+ console.log(description)
+ console.log(md)
+ console.log(time)
+
+
+
+ let todayYear = new Date().getFullYear();
+ let combinedStr = todayYear + "-" + md + " " + time;
+ let dateObj = new Date(combinedStr);
+
+ datas.push({
+ "title" : title,
+ "description" : description,
+ "originPage" : link,
+ "thumbnail" : thumbnail,
+ "pubDate" : dateObj.getTime(),
+ "category" : "TORRENT"
+ });
+ })
+ if(datas.length > 0) {
+ sendMessage(
+ {
+ type: "PRIVATES",
+ privates: datas,
+ currentPage : location.href
+ }
+ );
+ }
+
+ }
+
+ }
if (document.querySelectorAll('[class^="col-md-4 mb-4 video-item"]').length > 1) {
var datas = []
@@ -440,18 +614,4 @@ function gotoNext() {
} catch (e) {
}
-}
-//
-// if (document.readyState === "complete" || document.readyState === "interactive") {
-// reportCookiesToNative();
-// } else {
-// window.addEventListener("DOMContentLoaded", reportCookiesToNative);
-// }
-// function reportCookiesToNative() {
-// // 모든 쿠키를 읽어 네이티브로 전달
-// sendMessage({type: "MSG", cookies: "DOMContentLoaded try to cookies send"});
-// window.cookies.getAll({url: window.location.href}).then((cookies) => {
-// // 쿠키 데이터를 네이티브(Android)로 전송
-// sendMessage({type: "Cookies", cookies: cookies});
-// });
-// }
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/LauncherActivity.kt b/app/src/main/kotlin/bums/lunatic/launcher/LauncherActivity.kt
index 1b9cbc09..4465807f 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/LauncherActivity.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/LauncherActivity.kt
@@ -46,6 +46,7 @@ import android.view.WindowInsets
import android.view.WindowManager
import androidx.activity.OnBackPressedCallback
import androidx.annotation.RequiresApi
+import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.ViewCompat
@@ -62,6 +63,7 @@ import bums.lunatic.launcher.tokiz.Novels
import bums.lunatic.launcher.common.CommonActivity
import bums.lunatic.launcher.databinding.LauncherActivityBinding
import bums.lunatic.launcher.feeds.WidgetHost
+import bums.lunatic.launcher.helpers.BluetoothManager
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.widgetHostId
@@ -72,7 +74,9 @@ import bums.lunatic.launcher.home.RssViewBuilder
import bums.lunatic.launcher.model.RssData
import bums.lunatic.launcher.model.RssDataType
import bums.lunatic.launcher.tokiz.Comics
+import bums.lunatic.launcher.tokiz.Twitter
import bums.lunatic.launcher.tokiz.Webtoons
+import bums.lunatic.launcher.tokiz.Zota
import bums.lunatic.launcher.utils.Blog
import bums.lunatic.launcher.utils.FeedParseManager
import bums.lunatic.launcher.utils.getJ
@@ -115,6 +119,7 @@ import java.util.Calendar
import java.util.Date
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
+import kotlin.jvm.java
internal class LauncherActivity : CommonActivity() {
@@ -130,41 +135,40 @@ internal class LauncherActivity : CommonActivity() {
return sRuntime
}
- private var mWorkManager: WorkManager? = null
+
var isOpendFold = false
val qDayPeriod = 60L * 8L
@JvmStatic var lActivity: LauncherActivity? = null
- @JvmStatic var appWidgetManager: AppWidgetManager? = null
- @JvmStatic var appWidgetHost: WidgetHost? = null
- fun refreshDeviceData()
- {
- mWorkManager?.cancelAllWorkByTag(SMS_WORK_TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- SMS_WORK_TAG,
- ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.longTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(SMS_WORK_TAG)
- .build())
- mWorkManager?.cancelAllWorkByTag(RecentCallGetter.TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- RecentCallGetter.TAG,
- ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.longTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(RecentCallGetter.TAG)
- .build())
-
- mWorkManager?.cancelAllWorkByTag(ContactInfoGetter.TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- ContactInfoGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(12, TimeUnit.HOURS)
- .addTag(ContactInfoGetter.TAG)
- .build())
-
- mWorkManager?.enqueue(OneTimeWorkRequest.from(AppInfoGetter::class.java))
-
- }
+// fun refreshDeviceData()
+// {
+//
+// mWorkManager?.cancelAllWorkByTag(SMS_WORK_TAG)
+// mWorkManager?.enqueueUniquePeriodicWork(
+// SMS_WORK_TAG,
+// ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
+// PeriodicWorkRequestBuilder(PrefLong.longTimePeriod.get(), TimeUnit.MINUTES)
+// .addTag(SMS_WORK_TAG)
+// .build())
+// mWorkManager?.cancelAllWorkByTag(RecentCallGetter.TAG)
+// mWorkManager?.enqueueUniquePeriodicWork(
+// RecentCallGetter.TAG,
+// ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
+// PeriodicWorkRequestBuilder(PrefLong.longTimePeriod.get(), TimeUnit.MINUTES)
+// .addTag(RecentCallGetter.TAG)
+// .build())
+//
+// mWorkManager?.cancelAllWorkByTag(ContactInfoGetter.TAG)
+// mWorkManager?.enqueueUniquePeriodicWork(
+// ContactInfoGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
+// PeriodicWorkRequestBuilder(12, TimeUnit.HOURS)
+// .addTag(ContactInfoGetter.TAG)
+// .build())
+//
+// mWorkManager?.enqueue(OneTimeWorkRequest.from(AppInfoGetter::class.java))
+//
+// }
// fun runWeatherGetter() {
// Executors.newSingleThreadScheduledExecutor().schedule({
@@ -172,92 +176,14 @@ internal class LauncherActivity : CommonActivity() {
// }, 200L, TimeUnit.MILLISECONDS)
// }
- fun getCal() {
- Executors.newSingleThreadScheduledExecutor().schedule({
- mWorkManager?.enqueue(OneTimeWorkRequest.from(CalendarGetter::class.java))
- }, 5, TimeUnit.SECONDS)
- }
-
- val defaultDelay = 10L
-
- fun refreshFeeds() {
- mWorkManager?.cancelAllWork()
- mWorkManager?.cancelAllWorkByTag(RuliWebGetter.TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- RuliWebGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(RuliWebGetter.TAG)
- .build())
-
- mWorkManager?.cancelAllWorkByTag(FEDDS_WORK_TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- FEDDS_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.shortTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(FEDDS_WORK_TAG)
- .build())
- mWorkManager?.cancelAllWorkByTag(YT_WORK_TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- YT_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.longTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(YT_WORK_TAG)
- .build())
- mWorkManager?.cancelAllWorkByTag(REDDIT_WORK_TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- REDDIT_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(REDDIT_WORK_TAG)
- .build())
- mWorkManager?.cancelAllWorkByTag(FM_WORK_TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- FM_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(FM_WORK_TAG)
- .build())
- mWorkManager?.cancelAllWorkByTag(COMIC2_WORK_TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- COMIC2_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(COMIC2_WORK_TAG)
- .build())
- mWorkManager?.cancelAllWorkByTag(ClienGetter.TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- ClienGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(ClienGetter.TAG)
- .build())
- mWorkManager?.cancelAllWorkByTag(DCGetter.TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- DCGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(DCGetter.TAG)
- .build())
- mWorkManager?.cancelAllWorkByTag(TheQooGetter.TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- TheQooGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(TheQooGetter.TAG)
- .build())
- mWorkManager?.cancelAllWorkByTag(ArcaGetter.TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- ArcaGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(ArcaGetter.TAG)
- .build())
- mWorkManager?.cancelAllWorkByTag(LocationGetter.TAG)
- mWorkManager?.enqueueUniquePeriodicWork(
- LocationGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
- PeriodicWorkRequestBuilder(PrefLong.locationTimePeriod.get(), TimeUnit.MINUTES)
- .addTag(LocationGetter.TAG)
- .build())
- }
+// fun getCal() {
+// Executors.newSingleThreadScheduledExecutor().schedule({
+// mWorkManager?.enqueue(OneTimeWorkRequest.from(CalendarGetter::class.java))
+// }, 5, TimeUnit.SECONDS)
+// }
+
- fun workmanager() : WorkManager? {
- if (mWorkManager == null && lActivity != null) {
- mWorkManager = WorkManager.getInstance(lActivity!!)
- }
- return mWorkManager
- }
}
@@ -305,15 +231,6 @@ internal class LauncherActivity : CommonActivity() {
when(ev.keyCode) {
KEYCODE_BUTTON_Y->{
- CoroutineScope(Dispatchers.IO).launch {
- String(Base64.getMimeDecoder().decode("aHR0cHM6Ly9qYXZtb3N0LnRvL2xhdGVzdC11cGRhdGVzCg==".toByteArray())).getJ().let { doc -> FeedParseManager.parse(doc){Blog.LOGE(it)} }
- }
- CoroutineScope(Dispatchers.IO).launch {
- String.format(String(Base64.getMimeDecoder().decode("aHR0cHM6Ly9qYXZtb3N0LnRvL3NlYXJjaC9tb3ZpZS8lcw==".toByteArray())),"").getJ().let { doc -> FeedParseManager.parse(doc){Blog.LOGE(it)} }
- }
- CoroutineScope(Dispatchers.IO).launch {
- String.format(String(Base64.getMimeDecoder().decode("aHR0cHM6Ly9rcjcxLnNvZ2lybC5zby8/cz0lcw==".toByteArray())),"").getJ().let { doc -> FeedParseManager.parse(doc){Blog.LOGE(it)} }
- }
}
KEYCODE_BUTTON_X->{
@@ -332,11 +249,7 @@ internal class LauncherActivity : CommonActivity() {
}
}
KEYCODE_BUTTON_B->{
-// RssViewBuilder(lActivity!!)
-// .setRssId("https://jav.guru")
-// .webViewJavaScriptEnabled(true)
-// .showIconClose(true).showIconBack(false).showProgressBar(true).backPressToClose(false).webViewMixedContentMode(1)
-// .show("https://jav.guru")
+
}
KEYCODE_DPAD_DOWN->{
@@ -411,7 +324,6 @@ internal class LauncherActivity : CommonActivity() {
}
fun onClickCenterButton() {
-
WorkersDb.getRealm().apply {
writeBlocking {
delete(
@@ -584,7 +496,7 @@ internal class LauncherActivity : CommonActivity() {
installSplashScreen()
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
lActivity = this
- mWorkManager = WorkManager.getInstance(this)
+
DynamicColors.applyToActivityIfAvailable(this)
@@ -594,9 +506,6 @@ internal class LauncherActivity : CommonActivity() {
binding = LauncherActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
- appWidgetManager = AppWidgetManager.getInstance(applicationContext)
- appWidgetHost = WidgetHost(applicationContext, widgetHostId)
- appWidgetHost?.startListening()
HeadsetActionButtonReceiver.register(this)
@@ -617,6 +526,9 @@ internal class LauncherActivity : CommonActivity() {
handleBackPress()
updateLocationService()
binding.feeds.isChecked = true
+
+ val intent = Intent(this, BluetoothManager::class.java)
+ ContextCompat.startForegroundService(this, intent)
}
fun showContents(id : Int) {
@@ -641,6 +553,16 @@ internal class LauncherActivity : CommonActivity() {
.replace(R.id.fragment_container, Comics())
.commit()
}
+ R.id.zota ->{
+ supportFragmentManager.beginTransaction()
+ .replace(R.id.fragment_container, Zota())
+ .commit()
+ }
+ R.id.twitter ->{
+ supportFragmentManager.beginTransaction()
+ .replace(R.id.fragment_container, Twitter())
+ .commit()
+ }
else -> {}
}
}
@@ -665,7 +587,6 @@ internal class LauncherActivity : CommonActivity() {
override fun onDestroy() {
- appWidgetHost?.stopListening()
try {
sRuntime?.shutdown()
sRuntime = null
@@ -689,8 +610,6 @@ internal class LauncherActivity : CommonActivity() {
@RequiresApi(Build.VERSION_CODES.O_MR1)
override fun onResume() {
super.onResume()
- refreshFeeds()
-// blutoothManager?.getPairedDevices()
Blog.LOGE("LauncherActivity onResume")
}
@@ -746,6 +665,9 @@ internal class LauncherActivity : CommonActivity() {
currentFragment.doNextPage()
}
}
+ is Novels -> {
+ currentFragment.actionNextEvent(false)
+ }
}
}
})
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/helpers/BluetoothManager.kt b/app/src/main/kotlin/bums/lunatic/launcher/helpers/BluetoothManager.kt
index 5afd84ee..d2d32723 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/helpers/BluetoothManager.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/helpers/BluetoothManager.kt
@@ -2,6 +2,10 @@ package bums.lunatic.launcher.helpers
import android.Manifest
import android.annotation.SuppressLint
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.Service
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.content.BroadcastReceiver
@@ -9,8 +13,33 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
+import android.os.Build
+import android.os.IBinder
import androidx.core.app.ActivityCompat
+import androidx.core.app.NotificationCompat
+import androidx.work.ExistingPeriodicWorkPolicy
+import androidx.work.PeriodicWorkRequestBuilder
+import androidx.work.WorkManager
+import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
+import bums.lunatic.launcher.R
+import bums.lunatic.launcher.home.GeckoWeb
import bums.lunatic.launcher.utils.Blog
+import bums.lunatic.launcher.workers.ArcaGetter
+import bums.lunatic.launcher.workers.ClienGetter
+import bums.lunatic.launcher.workers.DCGetter
+import bums.lunatic.launcher.workers.DotaxGetter
+import bums.lunatic.launcher.workers.DotaxGetter.Companion.COMIC2_WORK_TAG
+import bums.lunatic.launcher.workers.FmKoreaGetter
+import bums.lunatic.launcher.workers.FmKoreaGetter.Companion.FM_WORK_TAG
+import bums.lunatic.launcher.workers.LocationGetter
+import bums.lunatic.launcher.workers.NewsFeedsGetter
+import bums.lunatic.launcher.workers.NewsFeedsGetter.Companion.FEDDS_WORK_TAG
+import bums.lunatic.launcher.workers.RedditGetter
+import bums.lunatic.launcher.workers.RedditGetter.Companion.REDDIT_WORK_TAG
+import bums.lunatic.launcher.workers.RuliWebGetter
+import bums.lunatic.launcher.workers.TheQooGetter
+import bums.lunatic.launcher.workers.YoutubeGetter
+import bums.lunatic.launcher.workers.YoutubeGetter.Companion.YT_WORK_TAG
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -22,7 +51,7 @@ import okhttp3.ResponseBody
import java.util.concurrent.TimeUnit
-class BluetoothManager {
+class BluetoothManager : Service() {
enum class BLUETOOTH_STATE(val statestr: String) {
ENABLED("enabledBlutooth"),
@@ -30,50 +59,61 @@ class BluetoothManager {
NOT_SUPPORT("notSupport")
}
- lateinit var context: Context
var blueToothAdapter:BluetoothAdapter? = null
+ private var mWorkManager: WorkManager? = null
- constructor(context: Context) {
- this.context = context
- }
- constructor() {
-
- }
-
-// init {
-// this.context = context
-// }
-
- fun initBluetoothAdapter(){
- if ( blueToothAdapter == null ){
- val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as android.bluetooth.BluetoothManager
- blueToothAdapter = bluetoothManager.getAdapter()
+ val NOTIF_ID = 830721
+ override fun onCreate() {
+ super.onCreate()
+ Blog.LOGE("onCreate")
+ mWorkManager = WorkManager.getInstance(this)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ startForeground(NOTIF_ID, createNotification(this))
}
+ val filter = IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED)
+ registerReceiver(bluetoothreceiver, filter)
+ refreshFeeds()
+// GeckoWeb(applicationContext).apply {
+// loadUrl("aHR0cHM6Ly9pamF2dG9ycmVudC5jb20=")
+// }
}
- fun register(){
- if (context == null) return
- context.registerReceiver(bluetoothreceiver, addFilterAction())
+ override fun onBind(intent: Intent?): IBinder? {
+ Blog.LOGE("intent >>> ${intent}")
+ return null
+ }
+ private val CHANNEL_ID = "ble_service_channel"
+ private val CHANNEL_NAME = "BLE 서비스"
+
+ fun createNotification(context: Context): Notification {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val channel = NotificationChannel(
+ CHANNEL_ID,
+ CHANNEL_NAME,
+ NotificationManager.IMPORTANCE_HIGH // 중요도 낮게 (필요시 변경)
+ )
+ val notificationManager =
+ context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ notificationManager.createNotificationChannel(channel)
+ }
+ return NotificationCompat.Builder(context, CHANNEL_ID)
+ .setContentTitle("BLE 서비스")
+ .setContentText("실행중입니다.")
+ .setPriority(NotificationCompat.PRIORITY_MAX)
+ .setSmallIcon(R.drawable.ic_b)
+ .setOngoing(true) // 사용자가 알림을 스와이프로 지울 수 없게 만듦
+ .build()
}
- fun unregister(){
- if (context == null) return
- context.unregisterReceiver(bluetoothreceiver)
- }
//페어링된 디바이스 정보 가져오기
-
fun getPairedDevices() {
- val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as android.bluetooth.BluetoothManager
+ val bluetoothManager = applicationContext.getSystemService(Context.BLUETOOTH_SERVICE) as android.bluetooth.BluetoothManager
blueToothAdapter = bluetoothManager.adapter
- if (ActivityCompat.checkSelfPermission(context,Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) return
+ if (ActivityCompat.checkSelfPermission(applicationContext,Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) return
var pairedDevices = blueToothAdapter?.bondedDevices
if (pairedDevices?.size ?: 0 > 0) {
pairedDevices?.forEach { i ->
- //bondState : 12 (페어링 등록된 상태)
- //bondState : 10 (페어링 등록 안됨)
-// BLog.LOGE("getPairedDevices() / name : ${i.name}")
-// BLog.LOGE("getPairedDevices() / bondState : ${i.bondState}")
val isConnected = isConnected(i)
if(PrefString.carName.get().length > 2 && i.name.equals(PrefString.carName.get()) && isConnected != PrefBoolean.isConnectedCar.get()) {
PrefBoolean.isConnectedCar.set(isConnected)
@@ -83,6 +123,84 @@ class BluetoothManager {
}
}
+ fun refreshFeeds() {
+ mWorkManager?.cancelAllWork()
+ mWorkManager?.cancelAllWorkByTag(RuliWebGetter.TAG)
+ mWorkManager?.enqueueUniquePeriodicWork(
+ RuliWebGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
+ PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
+ .addTag(RuliWebGetter.TAG)
+ .build())
+
+ mWorkManager?.cancelAllWorkByTag(FEDDS_WORK_TAG)
+ mWorkManager?.enqueueUniquePeriodicWork(
+ FEDDS_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
+ PeriodicWorkRequestBuilder(PrefLong.shortTimePeriod.get(), TimeUnit.MINUTES)
+ .addTag(FEDDS_WORK_TAG)
+ .build())
+ mWorkManager?.cancelAllWorkByTag(YT_WORK_TAG)
+ mWorkManager?.enqueueUniquePeriodicWork(
+ YT_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
+ PeriodicWorkRequestBuilder(PrefLong.longTimePeriod.get(), TimeUnit.MINUTES)
+ .addTag(YT_WORK_TAG)
+ .build())
+ mWorkManager?.cancelAllWorkByTag(REDDIT_WORK_TAG)
+ mWorkManager?.enqueueUniquePeriodicWork(
+ REDDIT_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
+ PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
+ .addTag(REDDIT_WORK_TAG)
+ .build())
+ mWorkManager?.cancelAllWorkByTag(FM_WORK_TAG)
+ mWorkManager?.enqueueUniquePeriodicWork(
+ FM_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
+ PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
+ .addTag(FM_WORK_TAG)
+ .build())
+ mWorkManager?.cancelAllWorkByTag(COMIC2_WORK_TAG)
+ mWorkManager?.enqueueUniquePeriodicWork(
+ COMIC2_WORK_TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
+ PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
+ .addTag(COMIC2_WORK_TAG)
+ .build())
+ mWorkManager?.cancelAllWorkByTag(ClienGetter.TAG)
+ mWorkManager?.enqueueUniquePeriodicWork(
+ ClienGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
+ PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
+ .addTag(ClienGetter.TAG)
+ .build())
+ mWorkManager?.cancelAllWorkByTag(DCGetter.TAG)
+ mWorkManager?.enqueueUniquePeriodicWork(
+ DCGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
+ PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
+ .addTag(DCGetter.TAG)
+ .build())
+ mWorkManager?.cancelAllWorkByTag(TheQooGetter.TAG)
+ mWorkManager?.enqueueUniquePeriodicWork(
+ TheQooGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
+ PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
+ .addTag(TheQooGetter.TAG)
+ .build())
+ mWorkManager?.cancelAllWorkByTag(ArcaGetter.TAG)
+ mWorkManager?.enqueueUniquePeriodicWork(
+ ArcaGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
+ PeriodicWorkRequestBuilder(PrefLong.midTimePeriod.get(), TimeUnit.MINUTES)
+ .addTag(ArcaGetter.TAG)
+ .build())
+ mWorkManager?.cancelAllWorkByTag(LocationGetter.TAG)
+ mWorkManager?.enqueueUniquePeriodicWork(
+ LocationGetter.TAG, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
+ PeriodicWorkRequestBuilder(PrefLong.locationTimePeriod.get(), TimeUnit.MINUTES)
+ .addTag(LocationGetter.TAG)
+ .build())
+ }
+
+
+ fun workmanager() : WorkManager? {
+ if (mWorkManager == null && lActivity != null) {
+ mWorkManager = WorkManager.getInstance(lActivity!!)
+ }
+ return mWorkManager
+ }
fun sendToI(boolean: Boolean) {
if (PrefString.telegramSendTarget.get().length > 5) {
@@ -94,20 +212,10 @@ class BluetoothManager {
"${PrefString.carName.get()}의 시동이 꺼졌다요."
}}"
//7068729507
- // OkHttp 클라이언트 객체 생성
- val client = OkHttpClient.Builder()
+ val response: Response = OkHttpClient.Builder()
.connectionPool(ConnectionPool(5, 60, TimeUnit.SECONDS))
- .build()
-
- // GET 요청 객체 생성
- val builder: Request.Builder = Request.Builder().url(url)
- .addHeader("Content-Type", "application/json").get()
-
- val request: Request = builder.build()
-
-// BLog.LOGE("sendToI telegram before request ")
- // OkHttp 클라이언트로 GET 요청 객체 전송
- val response: Response = client.newCall(request).execute()
+ .build().newCall(Request.Builder().url(url)
+ .addHeader("Content-Type", "application/json").get().build()).execute()
if (response.isSuccessful()) {
// 응답 받아서 처리
val body: ResponseBody? = response.body()
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/helpers/PrefHelper.kt b/app/src/main/kotlin/bums/lunatic/launcher/helpers/PrefHelper.kt
index ecd98da5..d96c1b5b 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/helpers/PrefHelper.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/helpers/PrefHelper.kt
@@ -29,7 +29,7 @@ enum class PrefLong(val def : Long) : PrefKey {
maxQueryCount(18L);
override fun set(value : Long) { PrefHelper.putLong(this.name, value) }
override fun get(def : Long?) : Long {
- val value = PrefHelper.getLong(this.name, def ?: this.def) ?: this.def
+ val value = Math.max(15,PrefHelper.getLong(this.name, def ?: this.def) ?: this.def)
// Blog.LOGE("$name : $value")
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/home/GeckoWeb.kt b/app/src/main/kotlin/bums/lunatic/launcher/home/GeckoWeb.kt
index 462197f8..40db6bff 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/home/GeckoWeb.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/home/GeckoWeb.kt
@@ -286,6 +286,7 @@ class GeckoWeb : BWebview {
}
fun downloadImage(context: Context, url: Uri) {
+ Blog.LOGE("url.lastPathSegment ${url.lastPathSegment}")
val fileName = url.lastPathSegment ?: "${SimpleDateFormat("yyyyMMddHHmmsss")}.jpg"
val request = DownloadManager.Request(url)
request.setTitle(fileName)
@@ -687,9 +688,13 @@ class GeckoWeb : BWebview {
var nUrl = url
Blog.LOGE("url >>>> ${url}")
if (url.endsWith("=")) {
- nUrl = String(java.util.Base64.getMimeDecoder().decode(url.toByteArray()))
- param?.let {
- nUrl = nUrl.plus(param)
+ try {
+ nUrl = String(java.util.Base64.getMimeDecoder().decode(url.toByteArray()))
+ param?.let {
+ nUrl = nUrl.plus(param)
+ }
+ }catch (e: Exception) {
+ nUrl = url
}
} else if (url.startsWith("http") == false) {
nUrl = lastDomain
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/home/RssHome.kt b/app/src/main/kotlin/bums/lunatic/launcher/home/RssHome.kt
index b1120d14..bef2568f 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/home/RssHome.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/home/RssHome.kt
@@ -50,7 +50,6 @@ import bums.lunatic.launcher.common.letTrue
import bums.lunatic.launcher.databinding.LauncherHomeBinding
import bums.lunatic.launcher.helpers.Constants.Companion.PREFS_SETTINGS
import bums.lunatic.launcher.home.adapters.RssItemAdapter
-import bums.lunatic.launcher.home.adapters.RssItemAdapter.Companion.rssList
import bums.lunatic.launcher.home.adapters.SwipeToDeleteCallback
import bums.lunatic.launcher.model.RssData
import bums.lunatic.launcher.model.RssDataType
@@ -101,7 +100,7 @@ internal class RssHome : Fragment() {
fun rssStateVote() = (lasted?.filter { it.vote == true }?.size ?: -1) == (lasted?.size ?: 0)
var lasted: ArrayList = arrayListOf()
var infosJob: Job? = null
- var rssId = ""
+// var rssId = ""
lateinit var mRssAdapter: RssItemAdapter
var mRssDataResult: RealmResults? = null
@@ -336,25 +335,32 @@ internal class RssHome : Fragment() {
builder.show()
}
-
+ var currentRss : RssData? = null
@SuppressLint("SimpleDateFormat")
fun openGecko(rssData: RssData? = null) {
binding.layoutRssSummary.root.visibility = View.GONE
if (rssData?.category()?.equals(RssDataType.PRIVATE) == false && rssData?.originPage?.isNotEmpty() == true) {
- rssData?.originPage?.let {
+ rssData?.let { rss ->
+ currentRss = rss
binding.geckoWeb.privateMode = false
- rssId = it
- targetList.clear()
- var setString = hashSetOf()
- setString.addAll(rssList)
- setString.removeAll { it.equals(rssId) }
- targetList.addAll(setString)
- binding.geckoWeb.loadUrl(rssId)
+ appendReadCount(rss, 1, false)
+ rss?.originPage?.let { rssId->
+ synchronized(lasted) {
+ if (lasted.isNotEmpty()) {
+ lasted.removeAll { target -> target.originPage.equals(rssId) }
+ }
+ }
+ binding.geckoWeb.loadUrl(rssId)
+ }
}
} else if (rssData?.category()?.equals(RssDataType.PRIVATE) == true){
rssData?.let {
binding.geckoWeb.privateMode = true
- lasted.removeAll { target -> target.originPage.equals(it.originPage) }
+ synchronized(lasted) {
+ if (lasted.isNotEmpty()) {
+ lasted.removeAll { target -> target.originPage.equals(it.originPage) }
+ }
+ }
appendReadCount(it, 1, false)
Blog.LOGE("removeFirst >>> ${Gson().toJson(it)}")
binding.layoutRssSummary.title.tag = it
@@ -477,11 +483,13 @@ internal class RssHome : Fragment() {
if (binding.geckoWeb.isVisible) {
WorkersDb.getRealm().apply {
writeBlocking {
- val result = query().query("originPage == $0", rssId).find()
- if (result.size > 0) {
- result.forEach {
- if (it.vote) {
- it.vote = false
+ currentRss?.originPage?.let {
+ val result = query().query("originPage == $0", it).find()
+ if (result.size > 0) {
+ result.forEach {
+ if (it.vote) {
+ it.vote = false
+ }
}
}
}
@@ -552,45 +560,40 @@ internal class RssHome : Fragment() {
}
fun vote() {
- Blog.LOGE("Arrow Center Click")
- WorkersDb.getRealm().apply {
- writeBlocking {
- val result = query().query(
- if (imageView) "thumbnail == $0" else "originPage == $0",
- rssId
- ).find()
- if (result.size > 0) {
- result.forEach { it.vote = true }
+ currentRss?.originPage.let {
+ Blog.LOGE("Arrow Center Click")
+ WorkersDb.getRealm().apply {
+ writeBlocking {
+ val result = query().query(
+ if (imageView) "thumbnail == $0" else "originPage == $0",
+ it
+ ).find()
+ if (result.size > 0) {
+ result.forEach { it.vote = true }
+ }
}
}
+ doNextPage()
}
- doNextPage()
}
@SuppressLint("NewApi")
fun doNextPage() {
- WorkersDb.getRealm().apply {
- writeBlocking {
- val result = query().query(
- if (imageView) "thumbnail == $0" else "originPage == $0",
- rssId
- ).find()
- if (result.size > 0) {
- result.forEach {
- it.read = it.read + nomoreShowCount
+ currentRss?.originPage.let {
+ WorkersDb.getRealm().apply {
+ writeBlocking {
+ val result = query().query(
+ if (imageView) "thumbnail == $0" else "originPage == $0",
+ it
+ ).find()
+ if (result.size > 0) {
+ result.forEach { it.read = it.read + nomoreShowCount }
}
}
}
}
- targetList.removeAll { it.equals(rssId) }
- if (targetList.size > 0) {
- rssId = targetList.removeFirst()
- binding.geckoWeb.loadUrl(rssId)
- } else {
- Toast.makeText(requireContext(), "없어 끄자", Toast.LENGTH_LONG).show()
- binding.geckoWeb.visibility = View.GONE
- }
+ openGecko(randomOrNull())
}
fun clearJob(job: Job?) {
@@ -616,7 +619,7 @@ internal class RssHome : Fragment() {
.query(
"category != $0 AND category != $1 ",
RssDataType.PRIVATE.name,
- RssDataType.REDDIT_NSFW.name
+ RssDataType.TORRENT.name
)
.query("vote != $0", true).find()
)
@@ -634,12 +637,14 @@ internal class RssHome : Fragment() {
flow.collect { changes: ResultsChange ->
// when (changes) {
// is InitialResults -> {
- commandHandler.removeCallbacks(infoUpdate)
- WorkersDb.getRealm().apply {
- lasted.clear()
- lasted.addAll(copyFromRealm(changes.list))
+ synchronized(lasted) {
+ commandHandler.removeCallbacks(infoUpdate)
+ WorkersDb.getRealm().apply {
+ lasted.clear()
+ lasted.addAll(copyFromRealm(changes.list))
+ }
+ commandHandler.post(infoUpdate)
}
- commandHandler.post(infoUpdate)
// }
//
// is UpdatedResults -> {
@@ -714,9 +719,13 @@ internal class RssHome : Fragment() {
fun queryInfos(
keywords: List,
+ includeVote : Boolean = false,
+ includeRead : Boolean = false
) {
beforeQuery()
- var rQ = getRealm().query().sort("pubDate", Sort.DESCENDING)
+ var rQ = getRealm().query().sort("read", Sort.ASCENDING)
+ if (!includeRead) { rQ = rQ.query("read == $0", 0)}
+ if (!includeVote) { rQ = rQ.query("vote != $0", true)}
// 사용 예시
val (queryStr, queryArgs) = buildMultiFieldOrQuery(
listOf("title", "description"),
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/home/adapters/RssItemAdapter.kt b/app/src/main/kotlin/bums/lunatic/launcher/home/adapters/RssItemAdapter.kt
index 84dbb7bc..f1cbf664 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/home/adapters/RssItemAdapter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/home/adapters/RssItemAdapter.kt
@@ -61,7 +61,7 @@ internal class RssItemAdapter (
@SuppressLint("SimpleDateFormat")
val dateFormat = SimpleDateFormat("a HH:mm / yy - MM - dd")
val emptyDate = " - "
- var rssList: ArrayList = ArrayList()
+// var rssList: ArrayList = ArrayList()
val webViewListener = object : WebViewListener() {
override fun onCustomMenuClick(menuCode: String?) {
super.onCustomMenuClick(menuCode)
@@ -188,51 +188,60 @@ internal class RssItemAdapter (
@SuppressLint("SetTextI18n", "ClickableViewAccessibility")
override fun onBindViewHolder(holder: RssHolder, position: Int) {
- val rssData = rssDataItemLis[position]
- if (rssData.pubDate() > 1000L) {
- holder.view.date.text = dateFormat.format(Date(rssData.pubDate()))
- } else {
- holder.view.date.text = emptyDate
- }
-
-
- holder.view.title.text = "".plus(if(rssData.vote) " * " else "").plus(rssData.title().plus("[R:${rssData.read}]"))
- holder.view.desc.text = rssData.description()
-
- var param = holder.view.circlePreview.layoutParams
- holder.view.circlePreview.layoutParams = ConstraintLayout.LayoutParams(rssData.category().defaultImgSize(), param.height)
- holder.view.circlePreview.visibility = rssData.category().getDefaultVisibiliy()
- Picasso.get().cancelRequest(holder.view.circlePreview)
-
- if(rssData.thumbnailUrl()?.length ?: 0 > 6) {
- Picasso.get().load(rssData.thumbnailUrl().replace("&","&").toUri()).into(holder.view.circlePreview)
- } else if (rssData.category().getResId() > 0 ) {
- holder.view.circlePreview.setImageResource(rssData.category().getResId())
- } else {
- holder.view.circlePreview.setImageDrawable(null)
- }
-
- holder.itemView.tag = rssData
- holder.itemView.setOnClickListener(dateViewClick)
- holder.itemView.setOnTouchListener(object : View.OnTouchListener{
- override fun onTouch(
- v: View,
- event: MotionEvent
- ): Boolean {
- 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
- }
+ if (rssDataItemLis.isNotEmpty() && rssDataItemLis.size > position) {
+ val rssData = rssDataItemLis[position]
+ if (rssData.pubDate() > 1000L) {
+ holder.view.date.text = dateFormat.format(Date(rssData.pubDate()))
+ } else {
+ holder.view.date.text = emptyDate
}
- })
+
+
+ holder.view.title.text = "".plus(if (rssData.vote) " * " else "")
+ .plus(rssData.title().plus("[R:${rssData.read}]"))
+ holder.view.desc.text = rssData.description()
+
+ var param = holder.view.circlePreview.layoutParams
+ holder.view.circlePreview.layoutParams =
+ ConstraintLayout.LayoutParams(rssData.category().defaultImgSize(), param.height)
+ holder.view.circlePreview.visibility = rssData.category().getDefaultVisibiliy()
+ Picasso.get().cancelRequest(holder.view.circlePreview)
+
+ if (rssData.thumbnailUrl()?.length ?: 0 > 6) {
+ Picasso.get().load(rssData.thumbnailUrl().replace("&", "&").toUri())
+ .into(holder.view.circlePreview)
+ } else if (rssData.category().getResId() > 0) {
+ holder.view.circlePreview.setImageResource(rssData.category().getResId())
+ } else {
+ holder.view.circlePreview.setImageDrawable(null)
+ }
+
+ holder.itemView.tag = rssData
+ holder.itemView.setOnClickListener(dateViewClick)
+ holder.itemView.setOnTouchListener(object : View.OnTouchListener {
+ override fun onTouch(
+ v: View,
+ event: MotionEvent
+ ): Boolean {
+ 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
+ }
+ }
+ })
// v.setOnLongClickListener {
// WorkersDb.getRealm().apply {
// copyFromRealm(rss)
// }
// }
- holder.itemView.setOnLongClickListener(mLongClickListener)
+ holder.itemView.setOnLongClickListener(mLongClickListener)
+ }
}
var layoutManager : LinearLayoutManager? = null
@@ -242,24 +251,29 @@ internal class RssItemAdapter (
this.recyclerView = recyclerView
}
+ @SuppressLint("NotifyDataSetChanged")
fun updateData(newList: List) {
try {
- DiffUtil.calculateDiff(RssItemDiffUtil(rssDataItemLis, newList)).apply {
+// DiffUtil.calculateDiff(RssItemDiffUtil(rssDataItemLis, newList)).apply {
+//
+// }.dispatchUpdatesTo(this).apply {
+// val visibleItemCount = (layoutManager?.findLastVisibleItemPosition() ?: 0) - (layoutManager?.findFirstVisibleItemPosition() ?: 0)
+// val first = layoutManager?.findLastVisibleItemPosition() ?: 0
+// if (visibleItemCount > 0) {
+// this@RssItemAdapter.notifyItemRangeChanged(first, visibleItemCount)
+//// recyclerView?.scrollToPosition(0)
+// }
+// }
+ synchronized(rssDataItemLis) {
+ rssDataItemLis.clear()
+ rssDataItemLis.addAll(newList)
+ }
+ notifyDataSetChanged()
- }.dispatchUpdatesTo(this).apply {
- val visibleItemCount = (layoutManager?.findLastVisibleItemPosition() ?: 0) - (layoutManager?.findFirstVisibleItemPosition() ?: 0)
- val first = layoutManager?.findLastVisibleItemPosition() ?: 0
- if (visibleItemCount > 0) {
- this@RssItemAdapter.notifyItemRangeChanged(first, visibleItemCount)
-// recyclerView?.scrollToPosition(0)
- }
- }
- rssDataItemLis.clear()
- rssDataItemLis.addAll(newList)
- CoroutineScope(Dispatchers.IO).launch {
- rssList.clear()
- rssList.addAll(newList.map { it.originPage() })
- }
+// CoroutineScope(Dispatchers.IO).launch {
+// rssList.clear()
+// rssList.addAll(newList.map { it.originPage() })
+// }
} catch (e: Exception) {
e.printStackTrace()
}
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/model/RssDataInterface.kt b/app/src/main/kotlin/bums/lunatic/launcher/model/RssDataInterface.kt
index e11c681a..9570e16f 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/model/RssDataInterface.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/model/RssDataInterface.kt
@@ -7,10 +7,9 @@ import bums.lunatic.launcher.helpers.PrefHelper
enum class RssDataType {
NO_DATA,
PRIVATE,
+ TORRENT,
YOUTUBE,
NEWSFEED,
-// GURU,
-// MOST,
TAGS,
REDDIT,
REDDIT_NSFW,
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/tokiz/BaseToki.kt b/app/src/main/kotlin/bums/lunatic/launcher/tokiz/BaseToki.kt
index 9fc9626a..46ced71f 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/tokiz/BaseToki.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/tokiz/BaseToki.kt
@@ -54,9 +54,11 @@ import bums.lunatic.launcher.tokiz.view.JxEvent
import bums.lunatic.launcher.tokiz.view.PagedTextLayout
import bums.lunatic.launcher.tokiz.view.PagedTextViewInterface
import bums.lunatic.launcher.databinding.BooktokiBinding
+import bums.lunatic.launcher.home.toast
import bums.lunatic.launcher.tokiz.data.model.FakeSession
import bums.lunatic.launcher.tokiz.data.model.FakeSessions
import bums.lunatic.launcher.utils.Blog
+import bums.lunatic.launcher.workers.WorkersDb
import com.google.gson.Gson
import io.realm.kotlin.Realm
import io.realm.kotlin.UpdatePolicy
@@ -130,7 +132,7 @@ abstract class BaseToki : Fragment(), PagedTextViewInterface {
abstract var lastNumber : Int
abstract val webcontentsName : String
abstract val afterDot : String
- fun getLastedDoamin(): String {
+ open fun getLastedDoamin(): String {
return String.format("https://%s%d.%s", webcontentsName , lastNumber, afterDot)
}
val OnTouchListener = object : OnTouchListener {
@@ -454,6 +456,12 @@ abstract class BaseToki : Fragment(), PagedTextViewInterface {
"SHOWVIEWER" -> {
binding.progress.visibility = GONE
}
+ "PRIVATES"->{
+ lPortMessage.privates?.let {
+ requireContext().toast("Received Msg privates form ${lPortMessage.currentPage} data => ${it?.size ?: 0}")
+ WorkersDb.insertBulkData(it)
+ }
+ }
else -> {
}
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/tokiz/Twitter.kt b/app/src/main/kotlin/bums/lunatic/launcher/tokiz/Twitter.kt
new file mode 100644
index 00000000..72b67e72
--- /dev/null
+++ b/app/src/main/kotlin/bums/lunatic/launcher/tokiz/Twitter.kt
@@ -0,0 +1,185 @@
+package bums.lunatic.launcher.tokiz
+
+import android.content.DialogInterface
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.res.Configuration
+import android.graphics.Bitmap
+import android.graphics.Color
+import android.net.Uri
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.os.Message
+import android.text.InputType
+import android.text.SpannableStringBuilder
+import android.text.style.RelativeSizeSpan
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.MotionEvent
+import android.view.View
+import android.view.View.GONE
+import android.view.View.OnTouchListener
+import android.view.View.VISIBLE
+import android.view.View.inflate
+import android.view.ViewGroup
+import android.webkit.WebView
+import android.webkit.WebViewClient
+import android.widget.ArrayAdapter
+import android.widget.EditText
+import android.widget.TextView
+import android.widget.Toast
+import androidx.appcompat.app.AlertDialog
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.net.toUri
+import androidx.core.view.isVisible
+import androidx.fragment.app.Fragment
+import bums.lunatic.launcher.LauncherActivity.Companion.getRuntime
+import bums.lunatic.launcher.R
+import bums.lunatic.launcher.tokiz.common.PairArray
+import bums.lunatic.launcher.tokiz.common.TouchArea
+import bums.lunatic.launcher.tokiz.common.colorz
+import bums.lunatic.launcher.tokiz.common.getIndex
+import bums.lunatic.launcher.tokiz.common.typesfacez
+import bums.lunatic.launcher.tokiz.data.HistoryManager
+import bums.lunatic.launcher.tokiz.data.model.ContentsPageInfo
+import bums.lunatic.launcher.tokiz.data.model.ContentsCollection
+import bums.lunatic.launcher.tokiz.data.model.PageInfosJ
+import bums.lunatic.launcher.tokiz.data.model.HistoryItem
+import bums.lunatic.launcher.tokiz.data.model.LastInfo
+import bums.lunatic.launcher.tokiz.data.model.PortMessage
+import bums.lunatic.launcher.tokiz.data.model.ReaderConfig
+import bums.lunatic.launcher.tokiz.dialog.DefaultList
+import bums.lunatic.launcher.tokiz.view.JxEvent
+import bums.lunatic.launcher.tokiz.view.PagedTextLayout
+import bums.lunatic.launcher.tokiz.view.PagedTextViewInterface
+import bums.lunatic.launcher.databinding.BooktokiBinding
+import bums.lunatic.launcher.utils.Blog
+import com.google.gson.Gson
+import io.realm.kotlin.Realm
+import io.realm.kotlin.UpdatePolicy
+import io.realm.kotlin.ext.copyFromRealm
+import io.realm.kotlin.ext.query
+import org.json.JSONException
+import org.json.JSONObject
+import org.mozilla.gecko.util.ThreadUtils
+import org.mozilla.geckoview.GeckoResult
+import org.mozilla.geckoview.GeckoSession
+import org.mozilla.geckoview.MediaSession
+import org.mozilla.geckoview.WebExtension
+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.lang.System.currentTimeMillis
+import java.text.SimpleDateFormat
+import java.util.Date
+import kotlin.collections.ArrayList
+import kotlin.collections.List
+import kotlin.collections.MutableList
+import kotlin.collections.arrayListOf
+import kotlin.collections.first
+import kotlin.collections.isNotEmpty
+import kotlin.collections.last
+import kotlin.collections.sortBy
+import kotlin.random.Random
+import kotlin.text.contains
+import kotlin.text.endsWith
+import kotlin.text.equals
+import kotlin.text.replace
+import kotlin.text.split
+import kotlin.text.startsWith
+import kotlin.text.toInt
+import kotlin.text.toRegex
+import kotlin.text.trim
+
+
+class Twitter : BaseToki(), PagedTextViewInterface {
+
+ override val contentsType = "twitter"
+ override var lastNumber : Int = 143
+ override val webcontentsName : String = "twitter"
+ override val afterDot = "com"
+ override fun getLastedDoamin(): String {
+ return String.format("https://%s.%s", webcontentsName, afterDot)
+ }
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ super.onCreateView(inflater, container, savedInstanceState)
+ return binding.root
+ }
+
+ override fun onStart() {
+ super.onStart()
+ }
+
+ override fun onResume() {
+ super.onResume()
+ loadLastInfo()
+ }
+
+ override fun onTouch(touchArea: TouchArea) {
+ Blog.LOGD(log = "onTouch ${touchArea}")
+ when (touchArea) {
+ TouchArea.Center -> {
+
+ }
+
+ TouchArea.Right -> {
+ actionNextEvent()
+ }
+
+ TouchArea.Left -> {
+ actionPrevEvent()
+ }
+
+ TouchArea.DoubleRight -> {
+ actionNextEvent(true)
+ }
+
+ TouchArea.DoubleLeft -> {
+ actionPrevEvent(true)
+ }
+ else -> {
+
+ }
+ }
+
+
+ }
+
+ override fun onLongClick() {
+ Blog.LOGD(log = "onLongClick")
+
+ }
+
+ override fun onSwipeLeft(count: Int) {
+ Blog.LOGD(log = "onSwipeLeft ${count}")
+ actionNextEvent(count > 1)
+
+ }
+
+ override fun onSwipeRight(count: Int) {
+ Blog.LOGD(log = "onSwipeRight ${count}")
+ actionPrevEvent(count > 1)
+ }
+
+ override fun onSwipeUp(touchCount: Int) {
+
+ }
+
+ override fun onSwipeDown(touchCount: Int) {
+ if (touchCount == 2) {
+ if (binding.pagedLayer.isVisible) {
+ binding.pagedLayer.visibility = GONE
+ }
+ }
+ }
+
+ override fun onTimeoverTouch() {
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/tokiz/Zota.kt b/app/src/main/kotlin/bums/lunatic/launcher/tokiz/Zota.kt
new file mode 100644
index 00000000..dd13e92a
--- /dev/null
+++ b/app/src/main/kotlin/bums/lunatic/launcher/tokiz/Zota.kt
@@ -0,0 +1,182 @@
+package bums.lunatic.launcher.tokiz
+
+import android.content.DialogInterface
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.res.Configuration
+import android.graphics.Bitmap
+import android.graphics.Color
+import android.net.Uri
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.os.Message
+import android.text.InputType
+import android.text.SpannableStringBuilder
+import android.text.style.RelativeSizeSpan
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.MotionEvent
+import android.view.View
+import android.view.View.GONE
+import android.view.View.OnTouchListener
+import android.view.View.VISIBLE
+import android.view.View.inflate
+import android.view.ViewGroup
+import android.webkit.WebView
+import android.webkit.WebViewClient
+import android.widget.ArrayAdapter
+import android.widget.EditText
+import android.widget.TextView
+import android.widget.Toast
+import androidx.appcompat.app.AlertDialog
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.net.toUri
+import androidx.core.view.isVisible
+import androidx.fragment.app.Fragment
+import bums.lunatic.launcher.LauncherActivity.Companion.getRuntime
+import bums.lunatic.launcher.R
+import bums.lunatic.launcher.tokiz.common.PairArray
+import bums.lunatic.launcher.tokiz.common.TouchArea
+import bums.lunatic.launcher.tokiz.common.colorz
+import bums.lunatic.launcher.tokiz.common.getIndex
+import bums.lunatic.launcher.tokiz.common.typesfacez
+import bums.lunatic.launcher.tokiz.data.HistoryManager
+import bums.lunatic.launcher.tokiz.data.model.ContentsPageInfo
+import bums.lunatic.launcher.tokiz.data.model.ContentsCollection
+import bums.lunatic.launcher.tokiz.data.model.PageInfosJ
+import bums.lunatic.launcher.tokiz.data.model.HistoryItem
+import bums.lunatic.launcher.tokiz.data.model.LastInfo
+import bums.lunatic.launcher.tokiz.data.model.PortMessage
+import bums.lunatic.launcher.tokiz.data.model.ReaderConfig
+import bums.lunatic.launcher.tokiz.dialog.DefaultList
+import bums.lunatic.launcher.tokiz.view.JxEvent
+import bums.lunatic.launcher.tokiz.view.PagedTextLayout
+import bums.lunatic.launcher.tokiz.view.PagedTextViewInterface
+import bums.lunatic.launcher.databinding.BooktokiBinding
+import bums.lunatic.launcher.utils.Blog
+import com.google.gson.Gson
+import io.realm.kotlin.Realm
+import io.realm.kotlin.UpdatePolicy
+import io.realm.kotlin.ext.copyFromRealm
+import io.realm.kotlin.ext.query
+import org.json.JSONException
+import org.json.JSONObject
+import org.mozilla.gecko.util.ThreadUtils
+import org.mozilla.geckoview.GeckoResult
+import org.mozilla.geckoview.GeckoSession
+import org.mozilla.geckoview.MediaSession
+import org.mozilla.geckoview.WebExtension
+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.lang.System.currentTimeMillis
+import java.text.SimpleDateFormat
+import java.util.Date
+import kotlin.collections.ArrayList
+import kotlin.collections.List
+import kotlin.collections.MutableList
+import kotlin.collections.arrayListOf
+import kotlin.collections.first
+import kotlin.collections.isNotEmpty
+import kotlin.collections.last
+import kotlin.collections.sortBy
+import kotlin.random.Random
+import kotlin.text.contains
+import kotlin.text.endsWith
+import kotlin.text.equals
+import kotlin.text.replace
+import kotlin.text.split
+import kotlin.text.startsWith
+import kotlin.text.toInt
+import kotlin.text.toRegex
+import kotlin.text.trim
+
+
+class Zota : BaseToki(), PagedTextViewInterface {
+
+ override val contentsType = "Torrent"
+ override var lastNumber : Int = 143
+ override val webcontentsName : String = "torrentzota"
+ override val afterDot = "com"
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ super.onCreateView(inflater, container, savedInstanceState)
+ return binding.root
+ }
+
+ override fun onStart() {
+ super.onStart()
+ }
+
+ override fun onResume() {
+ super.onResume()
+ loadLastInfo()
+ }
+
+ override fun onTouch(touchArea: TouchArea) {
+ Blog.LOGD(log = "onTouch ${touchArea}")
+ when (touchArea) {
+ TouchArea.Center -> {
+
+ }
+
+ TouchArea.Right -> {
+ actionNextEvent()
+ }
+
+ TouchArea.Left -> {
+ actionPrevEvent()
+ }
+
+ TouchArea.DoubleRight -> {
+ actionNextEvent(true)
+ }
+
+ TouchArea.DoubleLeft -> {
+ actionPrevEvent(true)
+ }
+ else -> {
+
+ }
+ }
+
+
+ }
+
+ override fun onLongClick() {
+ Blog.LOGD(log = "onLongClick")
+
+ }
+
+ override fun onSwipeLeft(count: Int) {
+ Blog.LOGD(log = "onSwipeLeft ${count}")
+ actionNextEvent(count > 1)
+
+ }
+
+ override fun onSwipeRight(count: Int) {
+ Blog.LOGD(log = "onSwipeRight ${count}")
+ actionPrevEvent(count > 1)
+ }
+
+ override fun onSwipeUp(touchCount: Int) {
+
+ }
+
+ override fun onSwipeDown(touchCount: Int) {
+ if (touchCount == 2) {
+ if (binding.pagedLayer.isVisible) {
+ binding.pagedLayer.visibility = GONE
+ }
+ }
+ }
+
+ override fun onTimeoverTouch() {
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/wall/MyWallpaperService.kt b/app/src/main/kotlin/bums/lunatic/launcher/wall/MyWallpaperService.kt
index 00265ff3..a457abff 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/wall/MyWallpaperService.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/wall/MyWallpaperService.kt
@@ -6,6 +6,8 @@ import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.media.MediaCodec
+import android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar
+import android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar
import android.media.MediaExtractor
import android.media.MediaFormat
import android.renderscript.Allocation
@@ -18,6 +20,8 @@ import android.util.DisplayMetrics
import android.view.SurfaceHolder
import android.view.WindowManager
import bums.lunatic.launcher.R
+import bums.lunatic.launcher.utils.Blog
+import java.nio.ByteBuffer
class MyWallpaperService : WallpaperService() {
@@ -51,6 +55,8 @@ class MyWallpaperService : WallpaperService() {
screenWidth = metrics.widthPixels
screenHeight = metrics.heightPixels
}
+
+
}
override fun onSurfaceCreated(holder: SurfaceHolder) {
@@ -98,6 +104,7 @@ class RenderThread(
private var renderStartY = 0f
private var maxOffset = 0f
private var moveXAxis = false
+ private var keyFrameRate = 60
override fun run() {
try {
@@ -110,9 +117,16 @@ class RenderThread(
for (i in 0 until trackCount) {
val format = getTrackFormat(i)
val mime = format.getString(MediaFormat.KEY_MIME) ?: ""
+
+ if (format.containsKey(MediaFormat.KEY_FRAME_RATE)) {
+ keyFrameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE)
+ } else {
+ keyFrameRate = 60
+ }
if (mime.startsWith("video/")) {
videoTrackIndex = i
selectTrack(i)
+
videoWidth = format.getInteger(MediaFormat.KEY_WIDTH)
videoHeight = format.getInteger(MediaFormat.KEY_HEIGHT)
break
@@ -144,8 +158,8 @@ class RenderThread(
}
renderWidth = videoWidth * scale
renderHeight = videoHeight * scale
- renderStartX = (screenWidth - renderWidth) / 2f
- renderStartY = (screenHeight - renderHeight) / 2f
+ renderStartX = 0f;//(screenWidth - renderWidth) / 2f
+ renderStartY = 0f;//(screenHeight - renderHeight) / 2f
moveXAxis = renderWidth > screenWidth
maxOffset = if (moveXAxis) renderWidth - screenWidth else if (renderHeight > screenHeight) renderHeight - screenHeight else 0f
@@ -161,9 +175,12 @@ class RenderThread(
if (inputBuffer != null) {
val sampleSize = extractor?.readSampleData(inputBuffer, 0) ?: -1
if (sampleSize < 0) {
- codec?.queueInputBuffer(
- inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM)
- isEOS = true
+// codec?.queueInputBuffer(
+// inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM)
+// isEOS = true
+ extractor?.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC)
+ isEOS = false // false로 재설정해서 계속 받게
+ continue
} else {
val pts = extractor?.sampleTime ?: 0L
codec?.queueInputBuffer(inputBufferIndex, 0, sampleSize, pts, 0)
@@ -176,13 +193,35 @@ class RenderThread(
val outputBufferIndex = codec?.dequeueOutputBuffer(bufferInfo, 10000) ?: -1
if (outputBufferIndex >= 0) {
val outputBuffer = codec?.getOutputBuffer(outputBufferIndex)
+ Blog.LOGE("codec.outputFormat >>> ${codec?.outputFormat}")
if (bufferInfo.size > 0 && outputBuffer != null) {
- val yuvData = ByteArray(bufferInfo.size)
- outputBuffer.get(yuvData)
+ val format = codec?.outputFormat
- val bitmap = convertYUVToBitmap(yuvData, videoWidth, videoHeight)
+ val colorFormat = format?.getInteger(MediaFormat.KEY_COLOR_FORMAT) ?: /*기본값*/0
+ val stride = format?.getInteger(MediaFormat.KEY_STRIDE) ?: videoWidth
+ val sliceHeight = format?.getInteger(MediaFormat.KEY_SLICE_HEIGHT) ?: videoHeight
+// outputBuffer.get(yuvData)
+ val expectedSize = videoWidth * videoHeight * 3 / 2
+ val yuvData = ByteArray(expectedSize)
+ val rawYUV = ByteArray(bufferInfo.size)
+ outputBuffer.get(rawYUV)
+ outputBuffer.position(0)
+
+// 포맷별 변환
+ val nv21Data = when (colorFormat) {
+ COLOR_FormatYUV420SemiPlanar -> nv12ToNv21(rawYUV, videoWidth, videoHeight)
+ COLOR_FormatYUV420Planar -> i420ToNv21(rawYUV, videoWidth, videoHeight)
+ else -> rawYUV // 이미 NV21 등
+ }
+
+// NV21 데이터 → Bitmap
+ val bitmap = convertYUVToBitmap(nv21Data, videoWidth, videoHeight)
+// outputBuffer.get(yuvData, 0, minOf(bufferInfo.size, expectedSize))
+// getYUVDataWithStride(outputBuffer, videoWidth, videoHeight, stride, height)
+// val bitmap = convertYUVToBitmap(yuvData, videoWidth, videoHeight)
// 6. 왕복 이동 애니메이션 offset 계산
+// Blog.LOGE("maxOffset >>> $maxOffset , offset >>> $offset , direction >> $direction , keyFrameRate >>> $keyFrameRate")
if (maxOffset > 0f) {
offset += direction * speed
if (offset < 0f) { offset = 0f; direction = 1 }
@@ -215,7 +254,7 @@ class RenderThread(
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// 필요하다면 포맷 변경 처리
}
- sleep(1000L/45L) // 약 30fps
+ sleep(1000L / keyFrameRate) // 약 30fps
}
} catch (e: Exception) {
e.printStackTrace()
@@ -229,13 +268,17 @@ class RenderThread(
}
private fun convertYUVToBitmap(yuvByteArray: ByteArray, width: Int, height: Int): Bitmap {
- val yuvType = Type.Builder(rs, Element.U8(rs)).setX(yuvByteArray.size)
+ // YUV420の場合, 바이트 수는 width * height * 3 / 2 여야 함
+ val minSize = width * height * 3 / 2
+ val realData = if (yuvByteArray.size >= minSize) yuvByteArray.copyOf(minSize) else yuvByteArray
+
+ val yuvType = Type.Builder(rs, Element.U8(rs)).setX(minSize)
val inAllocation = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT)
val rgbaType = Type.Builder(rs, Element.RGBA_8888(rs)).setX(width).setY(height)
val outAllocation = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT)
- inAllocation.copyFrom(yuvByteArray)
+ inAllocation.copyFrom(realData)
yuvToRgb?.setInput(inAllocation)
yuvToRgb?.forEach(outAllocation)
@@ -247,4 +290,91 @@ class RenderThread(
return bitmap
}
+
+ private fun getYUVDataWithStride(
+ buffer: ByteBuffer,
+ width: Int,
+ height: Int,
+ stride: Int,
+ sliceHeight: Int
+ ): ByteArray {
+ val yuvData = ByteArray(width * height * 3 / 2)
+ // Y plane 복사
+ for (y in 0 until height) {
+ buffer.position(y * stride)
+ buffer.get(yuvData, y * width, width)
+ }
+ // UV plane... (NV21 등 포맷 따라 별도 구현 필요)
+ // 이 영역은 포맷에 따라 다름, 기본은 Y만 참고!
+ return yuvData
+ }
+
+ // NV12 to NV21 변환
+ fun nv12ToNv21(nv12: ByteArray, width: Int, height: Int): ByteArray {
+ val frameSize = width * height
+ val nv21 = ByteArray(frameSize * 3 / 2)
+
+ // Y는 그대로 복사
+ System.arraycopy(nv12, 0, nv21, 0, frameSize)
+
+ // UV를 VU로 뒤집어서 복사
+ var i = 0
+ while (i < frameSize / 2 - 1) {
+ nv21[frameSize + i] = nv12[frameSize + i + 1]
+ nv21[frameSize + i + 1] = nv12[frameSize + i]
+ i += 2
+ }
+ return nv21
+ }
+
+ // I420(YUV420Planar) to NV21 변환
+ fun i420ToNv21(i420: ByteArray, width: Int, height: Int): ByteArray {
+ val frameSize = width * height
+ val qFrameSize = frameSize / 4
+ val nv21 = ByteArray(frameSize * 3 / 2)
+
+ // Y 복사
+ System.arraycopy(i420, 0, nv21, 0, frameSize)
+
+ // VU interleave
+ val u = frameSize
+ val v = frameSize + qFrameSize
+
+ for (i in 0 until qFrameSize) {
+ nv21[frameSize + i * 2] = i420[v + i] // V
+ nv21[frameSize + i * 2 + 1] = i420[u + i] // U
+ }
+ return nv21
+ }
+ fun i420ToNv21WithStride(src: ByteBuffer, width: Int, height: Int, stride: Int, sliceHeight: Int): ByteArray {
+ val frameSize = width * height
+ val qFrameSize = frameSize / 4
+ val nv21 = ByteArray(frameSize * 3 / 2)
+
+ // 1. Y plane 복사
+ for (y in 0 until height) {
+ src.position(y * stride)
+ src.get(nv21, y * width, width)
+ }
+
+ // 2. U/V plane 복사, 각각 stride/2씩 적용
+ val uOffsetBuf = sliceHeight * stride
+ val vOffsetBuf = uOffsetBuf + (sliceHeight / 2) * (stride / 2)
+
+ for (y in 0 until height/2) {
+ src.position(uOffsetBuf + y * (stride / 2))
+ src.get(nv21, frameSize + y * width, width/2) // 임시: U만
+
+ src.position(vOffsetBuf + y * (stride / 2))
+ for (x in 0 until width/2) {
+ // NV21 순서: V, U
+ val v = src.get()
+ val u = nv21[frameSize + y * width + x] // U
+ nv21[frameSize + y * width + x*2] = v
+ nv21[frameSize + y * width + x*2 + 1] = u
+ }
+ }
+
+ return nv21
+ }
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/DCGetter.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/DCGetter.kt
index 15c3c3b7..b9ca070d 100644
--- a/app/src/main/kotlin/bums/lunatic/launcher/workers/DCGetter.kt
+++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/DCGetter.kt
@@ -77,12 +77,12 @@ class DCGetter : BaseGetter {
Blog.LOGE("realWork() ${this::class.simpleName}")
temp.clear()
// https://m.dcinside.com/board/singlebungle1472
+ //"https://m.dcinside.com/board/programming",
+ //"https://m.dcinside.com/board/reading",
var urls = arrayListOf(
"https://m.dcinside.com",
"https://m.dcinside.com/board/singlebungle1472",
- "https://m.dcinside.com/board/programming",
"https://m.dcinside.com/board/cartoon",
- "https://m.dcinside.com/board/reading",
"https://m.dcinside.com/board/hit",
"https://m.dcinside.com/board/dcbest"
)
diff --git a/app/src/main/res/drawable/ic_b.png b/app/src/main/res/drawable/ic_b.png
new file mode 100644
index 00000000..8ee30611
Binary files /dev/null and b/app/src/main/res/drawable/ic_b.png differ
diff --git a/app/src/main/res/layout/launcher_activity.xml b/app/src/main/res/layout/launcher_activity.xml
index d1361b29..3ed31dcd 100644
--- a/app/src/main/res/layout/launcher_activity.xml
+++ b/app/src/main/res/layout/launcher_activity.xml
@@ -59,6 +59,17 @@
android:id="@+id/comics"
style="@style/tabItem"
android:layout_height="match_parent"/>
+
+