This commit is contained in:
lunaticbum 2025-08-31 13:07:04 +09:00
parent 32e38fda59
commit 520350f9d1
9 changed files with 113 additions and 40 deletions

View File

@ -149,6 +149,7 @@ dependencies {
implementation("io.github.junkfood02.youtubedl-android:ffmpeg:0.17.4") implementation("io.github.junkfood02.youtubedl-android:ffmpeg:0.17.4")
implementation("io.github.junkfood02.youtubedl-android:aria2c:0.17.4") implementation("io.github.junkfood02.youtubedl-android:aria2c:0.17.4")
implementation ("com.squareup.okhttp3:logging-interceptor:4.11.0")
// implementation ("com.arthenica:ffmpeg-kit-full:4.5.LTS") // implementation ("com.arthenica:ffmpeg-kit-full:4.5.LTS")
// implementation ("com.arthenica:ffmpeg-kit-full:6.0-2") // implementation ("com.arthenica:ffmpeg-kit-full:6.0-2")

View File

@ -22,6 +22,7 @@ import android.app.Application
import android.content.ComponentCallbacks2 import android.content.ComponentCallbacks2
import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteDatabase
import android.net.Uri import android.net.Uri
import android.view.PixelCopy.request
import bums.lunatic.launcher.helpers.HourlyLogWriter import bums.lunatic.launcher.helpers.HourlyLogWriter
import bums.lunatic.launcher.helpers.PrefHelper import bums.lunatic.launcher.helpers.PrefHelper
import bums.lunatic.launcher.utils.Blog import bums.lunatic.launcher.utils.Blog
@ -30,6 +31,7 @@ import com.squareup.picasso.Picasso
import kr.lunaticbum.Base import kr.lunaticbum.Base
import okhttp3.Cache import okhttp3.Cache
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.net.HttpURLConnection import java.net.HttpURLConnection
@ -55,25 +57,44 @@ internal class LunaticLauncher : Application() {
// dir.listFiles().forEach { Blog.LOGE("child -> ${it.absolutePath}") } // dir.listFiles().forEach { Blog.LOGE("child -> ${it.absolutePath}") }
} }
mHourlyLogWriter = HourlyLogWriter(dir) mHourlyLogWriter = HourlyLogWriter(dir)
val cacheSize = 1024L * 1024 * 1024 // 60MB val logging = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.HEADERS
}
val cacheSize = 1024L * 1024 * 1024 * 6 // 60MB
val cache = Cache(File(this.filesDir, "picasso-cache"), cacheSize) val cache = Cache(File(this.filesDir, "picasso-cache"), cacheSize)
val okHttpClient = OkHttpClient.Builder() val okHttpClient = OkHttpClient.Builder()
.cache(cache) .cache(cache)
.addInterceptor(logging)
.addInterceptor { chain -> .addInterceptor { chain ->
val newRequest = chain.request().newBuilder() var request = chain.request()
.addHeader("Host",chain.request().url().host()) val cacheControl = request.header("Cache-Control")
if (cacheControl?.contains("only-if-cached") == true) {
val newCacheControl = cacheControl.replace("only-if-cached", "")
.trim()
.ifEmpty { "max-stale=2419200" }
request = request.newBuilder()
.header("Cache-Control", newCacheControl)
.build()
}
val host = chain.request().url.host
val newRequest = chain.request().newBuilder().addHeader("authority",host)
.addHeader("Host",host)
.addHeader("User-Agent","Mozilla/5.0 (Android 15; Mobile; rv:139.0) Gecko/139.0 Firefox/139.0") .addHeader("User-Agent","Mozilla/5.0 (Android 15; Mobile; rv:139.0) Gecko/139.0 Firefox/139.0")
.addHeader("Accept","image/avif,image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5") .addHeader("Accept","image/avif,image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5")
.addHeader("Accept-Language","ko-KR,en-US;q=0.5") .addHeader("Accept-Language","ko-KR,en-US;q=0.5")
.addHeader("Accept-Encoding","gzip, deflate, br, zstd") .addHeader("Accept-Encoding","gzip, deflate, br, zstd")
.addHeader("Referer",chain.request().url().host()) .addHeader("Referer",host)
.build() .build()
Blog.LOGE("chain.request().url() >>> ${chain.request().url()}")
chain.proceed(newRequest) Blog.LOGE("chain.request().url() >>> ${chain.request().url} : host : $host")
val response = chain.proceed(newRequest)
Blog.LOGE("응답 코드: ${response.code}:${response.message}")
response
} }
.connectTimeout(30, TimeUnit.SECONDS) // 연결 타임아웃 .callTimeout(60, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS) // 읽기 타임아웃 .connectTimeout(60, TimeUnit.SECONDS) // 연결 타임아웃
.writeTimeout(30, TimeUnit.SECONDS) // 쓰기 타임아웃 .readTimeout(60, TimeUnit.SECONDS) // 읽기 타임아웃
.writeTimeout(60, TimeUnit.SECONDS) // 쓰기 타임아웃
.build() .build()
val picasso = Picasso.Builder(this) val picasso = Picasso.Builder(this)

View File

@ -33,6 +33,7 @@ import bums.lunatic.launcher.databinding.ListItemWithBinding
import bums.lunatic.launcher.home.adapters.RssItemDiffUtil import bums.lunatic.launcher.home.adapters.RssItemDiffUtil
import bums.lunatic.launcher.model.RssDataInterface import bums.lunatic.launcher.model.RssDataInterface
import bums.lunatic.launcher.model.RssDataType import bums.lunatic.launcher.model.RssDataType
import com.squareup.picasso.NetworkPolicy
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import java.net.URLEncoder import java.net.URLEncoder
import java.nio.charset.Charset import java.nio.charset.Charset
@ -81,9 +82,11 @@ internal class RssAdapter<T : RssDataInterface>(private val context: Context) :
holder.view.date.visibility = View.VISIBLE holder.view.date.visibility = View.VISIBLE
} }
if (!item.category().equals(RssDataType.PRIVATE)) {
if (item.thumbnailUrl().length ?: 0 > 6) { if (item.thumbnailUrl().length ?: 0 > 6) {
Picasso.get().load(item.thumbnailUrl()).into(holder.view.circlePreview) Picasso.get().load(item.thumbnailUrl()).networkPolicy(NetworkPolicy.NO_CACHE)
.into(holder.view.circlePreview)
}
} }
holder.view.root.setOnClickListener { holder.view.root.setOnClickListener {

View File

@ -320,8 +320,8 @@ class ForeGroundService : Service() {
.connectionPool(ConnectionPool(5, 60, TimeUnit.SECONDS)) .connectionPool(ConnectionPool(5, 60, TimeUnit.SECONDS))
.build().newCall(Request.Builder().url("https://api.telegram.org/bot7934509464:AAE_xUbICxMdywLGnxo7BkeIqA1nVza4P9w/sendMessage?chat_id=${PrefString.telegramSendTarget.get()}&text=${msg}") .build().newCall(Request.Builder().url("https://api.telegram.org/bot7934509464:AAE_xUbICxMdywLGnxo7BkeIqA1nVza4P9w/sendMessage?chat_id=${PrefString.telegramSendTarget.get()}&text=${msg}")
.addHeader("Content-Type", "application/json").get().build()).execute()?.let { response -> .addHeader("Content-Type", "application/json").get().build()).execute()?.let { response ->
if (response.isSuccessful()) { if (response.isSuccessful) {
val body: ResponseBody? = response.body() val body: ResponseBody? = response.body
if (body != null) { } if (body != null) { }
} else Blog.LOGE("sendToI telegram Error Occurred") } else Blog.LOGE("sendToI telegram Error Occurred")
} }
@ -344,9 +344,9 @@ class ForeGroundService : Service() {
.connectionPool(ConnectionPool(5, 60, TimeUnit.SECONDS)) .connectionPool(ConnectionPool(5, 60, TimeUnit.SECONDS))
.build().newCall(Request.Builder().url(url) .build().newCall(Request.Builder().url(url)
.addHeader("Content-Type", "application/json").get().build()).execute() .addHeader("Content-Type", "application/json").get().build()).execute()
if (response.isSuccessful()) { if (response.isSuccessful) {
// 응답 받아서 처리 // 응답 받아서 처리
val body: ResponseBody? = response.body() val body: ResponseBody? = response.body
if (body != null) { if (body != null) {
} }

View File

@ -476,7 +476,7 @@ class GeckoWeb : BWebview {
val client = OkHttpClient() val client = OkHttpClient()
val request = Request.Builder() val request = Request.Builder()
.url(url) .url(url)
.addHeader("Referer", lastedUrl) .addHeader("Referer", lastedUrl ?: "")
.addHeader("User-Agent", "Mozilla/5.0") .addHeader("User-Agent", "Mozilla/5.0")
// 필요시 Referer, 쿠키 등 헤더 추가 // 필요시 Referer, 쿠키 등 헤더 추가
.build() .build()
@ -484,7 +484,7 @@ class GeckoWeb : BWebview {
Thread { // 네트워크로 인한 별도 스레드 필요(코루틴도 가능) Thread { // 네트워크로 인한 별도 스레드 필요(코루틴도 가능)
val responseOk = client.newCall(request).execute() val responseOk = client.newCall(request).execute()
if (responseOk.isSuccessful) { if (responseOk.isSuccessful) {
responseOk.body()?.byteStream()?.use { input -> responseOk.body?.byteStream()?.use { input ->
FileOutputStream(savePath).use { output -> FileOutputStream(savePath).use { output ->
input.copyTo(output) input.copyTo(output)
} }
@ -496,7 +496,7 @@ class GeckoWeb : BWebview {
} }
} else { } else {
Handler(Looper.getMainLooper()).post { Handler(Looper.getMainLooper()).post {
Toast.makeText(context, "다운로드 실패: ${responseOk.message()}", Toast.LENGTH_SHORT).show() Toast.makeText(context, "다운로드 실패: ${responseOk.message}", Toast.LENGTH_SHORT).show()
} }
} }
dialog?.dismiss() dialog?.dismiss()

View File

@ -23,6 +23,7 @@ import android.content.Context
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.graphics.BitmapFactory
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
@ -47,6 +48,7 @@ import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.work.NetworkType
import bums.lunatic.launcher.LauncherActivity import bums.lunatic.launcher.LauncherActivity
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
import bums.lunatic.launcher.LunaticLauncher import bums.lunatic.launcher.LunaticLauncher
@ -71,6 +73,7 @@ import bums.lunatic.launcher.workers.WorkersDb
import bums.lunatic.launcher.workers.WorkersDb.getRealm import bums.lunatic.launcher.workers.WorkersDb.getRealm
import com.google.android.material.imageview.ShapeableImageView import com.google.android.material.imageview.ShapeableImageView
import com.google.gson.Gson import com.google.gson.Gson
import com.squareup.picasso.NetworkPolicy
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import io.realm.kotlin.UpdatePolicy import io.realm.kotlin.UpdatePolicy
import io.realm.kotlin.ext.query import io.realm.kotlin.ext.query
@ -82,6 +85,12 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import okhttp3.Call
import okhttp3.Callback
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okio.IOException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
@ -483,17 +492,19 @@ internal class RssHome : Fragment() {
Blog.LOGE(it) Blog.LOGE(it)
binding.layoutRssSummary.link.text = it binding.layoutRssSummary.link.text = it
} }
binding.layoutRssSummary.coverLink.text = it.thumbnailUrl()
binding.layoutRssSummary.cover.tag = it binding.layoutRssSummary.cover.tag = it
it.thumbnailUrl().let { it.thumbnailUrl().let {
Blog.LOGE(it) Blog.LOGE(it)
loadImage(binding.layoutRssSummary.cover, it) loadImage(binding.layoutRssSummary.cover, it, retryCount = 3, isMain = true)
binding.layoutRssSummary.coverLink.text = it
} }
binding.layoutRssSummary.screenLink.text = it.getScreen()
binding.layoutRssSummary.screen.tag = it binding.layoutRssSummary.screen.tag = it
it.getScreen().let { it.getScreen().let {
Blog.LOGE(it) Blog.LOGE(it)
loadImage(binding.layoutRssSummary.screen, it) loadImage(binding.layoutRssSummary.screen, it, retryCount = 2, isMain = false)
binding.layoutRssSummary.screenLink.text = it binding.layoutRssSummary.screenLink.text = it
} }
if (it.getMagnet().length < 10) { if (it.getMagnet().length < 10) {
@ -941,7 +952,7 @@ internal class RssHome : Fragment() {
} }
} }
val mainHandler = Handler(Looper.getMainLooper()) val mainHandler = Handler(Looper.getMainLooper())
fun loadImage(imageView: ImageView, url: String?, retryCount: Int = 5) { fun loadImage(imageView: ImageView, url: String?, retryCount: Int = 2, isMain : Boolean = false) {
with(imageView) { with(imageView) {
Picasso.get().cancelRequest(this) Picasso.get().cancelRequest(this)
// setOnTouchListener(null) // setOnTouchListener(null)
@ -957,6 +968,7 @@ internal class RssHome : Fragment() {
Blog.LOGE("loadImage >>> $url") Blog.LOGE("loadImage >>> $url")
Picasso.get() Picasso.get()
.load(url) .load(url)
.networkPolicy(NetworkPolicy.NO_CACHE)
.into(imageView, object : com.squareup.picasso.Callback { .into(imageView, object : com.squareup.picasso.Callback {
override fun onSuccess() { override fun onSuccess() {
imageView.visibility = View.VISIBLE imageView.visibility = View.VISIBLE
@ -972,14 +984,24 @@ internal class RssHome : Fragment() {
if (binding.layoutRssSummary.root.isVisible) { if (binding.layoutRssSummary.root.isVisible) {
mainHandler.postDelayed({ mainHandler.postDelayed({
if (binding.layoutRssSummary.root.isVisible && imageView.visibility == View.INVISIBLE) { if (binding.layoutRssSummary.root.isVisible && imageView.visibility == View.INVISIBLE) {
loadImage(imageView, url, retryCount - 1) loadImage(imageView, url, retryCount - 1, isMain)
} }
}, 3000L) }, 6000L)
} }
} else { } else {
// 3회 모두 실패: 대체 이미지 표시 // // 3회 모두 실패: 대체 이미지 표시
imageView.setImageResource(R.drawable.ic_info) // imageView.setImageResource(R.drawable.ic_info)
imageView.setAlpha(1f) // imageView.setAlpha(1f)
if (isMain) {
mainHandler.postDelayed({
binding.layoutRssSummary.root.visibility = View.GONE
currentRss?.originPage?.let {
binding.geckoWeb.loadUrl(
it
)
}
}, 6000L)
}
} }
} }
}) })
@ -995,6 +1017,29 @@ internal class RssHome : Fragment() {
} }
} }
fun randomOrNull() : RssData? = lasted.randomOrNull() fun randomOrNull() : RssData? = lasted.randomOrNull()
fun rett(imageView: ImageView,imageUrl: String){
// OkHttp로 직접 이미지 다운로드 후
val request = Request.Builder().url(imageUrl).build()
val client = OkHttpClient()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
// 실패 시 기본 이미지 처리 or 로깅
mainHandler.post({
// imageView.setImageResource(R.drawable.error_image)
})
}
override fun onResponse(call: Call, response: Response) {
response.body?.byteStream()?.let { inputStream ->
val bitmap = BitmapFactory.decodeStream(inputStream)
mainHandler.post({
imageView.setImageBitmap(bitmap)
})
}
}
})
}
} }
var toast: Toast? = null var toast: Toast? = null
fun Context.toast(string: String) { fun Context.toast(string: String) {

View File

@ -44,6 +44,7 @@ import bums.lunatic.launcher.utils.Blog
import bums.lunatic.launcher.utils.SimpleFingerGestures import bums.lunatic.launcher.utils.SimpleFingerGestures
import bums.lunatic.launcher.workers.WorkersDb import bums.lunatic.launcher.workers.WorkersDb
import com.google.android.material.imageview.ShapeableImageView import com.google.android.material.imageview.ShapeableImageView
import com.squareup.picasso.NetworkPolicy
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import io.realm.kotlin.UpdatePolicy import io.realm.kotlin.UpdatePolicy
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -213,14 +214,15 @@ internal class RssItemAdapter (
holder.view.circlePreview.setImageResource(rssData.category().getResId()) holder.view.circlePreview.setImageResource(rssData.category().getResId())
} }
if (rssData.thumbnailUrl()?.length ?: 0 > 6) { if(rssData.category().equals(RssDataType.PRIVATE) == false) {
Blog.LOGE("rssData.thumbnailUrl() >>> ${rssData.thumbnailUrl()}") if (rssData.thumbnailUrl()?.length ?: 0 > 6) {
Picasso.get().load(rssData.thumbnailUrl().replace("&amp;", "&").toUri()) Blog.LOGE("rssData.thumbnailUrl() >>> ${rssData.thumbnailUrl()}")
.into(holder.view.circlePreview) Picasso.get().load(rssData.thumbnailUrl().replace("&amp;", "&").toUri()).networkPolicy(
NetworkPolicy.NO_CACHE)
.into(holder.view.circlePreview)
}
} }
// else {
// holder.view.circlePreview.setImageDrawable(null)
// }
holder.itemView.tag = rssData holder.itemView.tag = rssData
holder.itemView.setOnClickListener(dateViewClick) holder.itemView.setOnClickListener(dateViewClick)

View File

@ -134,7 +134,7 @@ object CommonUtils {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
android.widget.Toast.makeText( android.widget.Toast.makeText(
context, context,
"다운로드 실패!\n${fileUrl}\n${response.message()}", "다운로드 실패!\n${fileUrl}\n${response.message}",
android.widget.Toast.LENGTH_SHORT android.widget.Toast.LENGTH_SHORT
).show() ).show()
} }
@ -162,7 +162,7 @@ object CommonUtils {
}_${System.currentTimeMillis()}.$extension" }_${System.currentTimeMillis()}.$extension"
val file = File(dir, fileName) val file = File(dir, fileName)
response.body()?.byteStream()?.use { input -> response.body?.byteStream()?.use { input ->
FileOutputStream(file).use { output -> FileOutputStream(file).use { output ->
input.copyTo(output) input.copyTo(output)
} }

View File

@ -26,6 +26,7 @@ import io.realm.kotlin.ext.query
import io.realm.kotlin.query.Sort import io.realm.kotlin.query.Sort
import okhttp3.ConnectionPool import okhttp3.ConnectionPool
import okhttp3.MediaType import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody import okhttp3.RequestBody
@ -76,7 +77,7 @@ class LocationUpdateService : Service(), LocationListener {
.addHeader("Content-Type", "application/json").get() .addHeader("Content-Type", "application/json").get()
builder.method( builder.method(
"POST", RequestBody.create( "POST", RequestBody.create(
MediaType.parse("application/text"), "application/text".toMediaTypeOrNull(),
Base64.getEncoder().encode( Base64.getEncoder().encode(
Gson().toJson(loc).toByteArray() Gson().toJson(loc).toByteArray()
) )
@ -87,9 +88,9 @@ class LocationUpdateService : Service(), LocationListener {
// Blog.LOGE("telegram before request ") // Blog.LOGE("telegram before request ")
// OkHttp 클라이언트로 GET 요청 객체 전송 // OkHttp 클라이언트로 GET 요청 객체 전송
val response: Response = client.newCall(request).execute() val response: Response = client.newCall(request).execute()
if (response.isSuccessful()) { if (response.isSuccessful) {
// 응답 받아서 처리 // 응답 받아서 처리
val body: ResponseBody? = response.body() val body: ResponseBody? = response.body
if (body != null) { if (body != null) {
} }