diff --git a/app/src/main/cpp/MediaAsset.cpp b/app/src/main/cpp/MediaAsset.cpp index 0c744989..38d5584c 100644 --- a/app/src/main/cpp/MediaAsset.cpp +++ b/app/src/main/cpp/MediaAsset.cpp @@ -227,7 +227,7 @@ bool MediaAsset::loadVideoWithFFmpeg(int fd) { } width_ = codecCtx_->width; height_ = codecCtx_->height; - swsCtx_ = sws_getContext(width_, height_, codecCtx_->pix_fmt, width_, height_, AV_PIX_FMT_RGBA, SWS_BILINEAR, nullptr, nullptr, nullptr); + swsCtx_ = sws_getContext(width_, height_, codecCtx_->pix_fmt, width_, height_, AV_PIX_FMT_BGRA, SWS_BILINEAR, nullptr, nullptr, nullptr); if (!swsCtx_) { LOGE("Could not create SwsContext"); release(); return false; @@ -265,7 +265,7 @@ bool MediaAsset::loadVideoWithFFmpeg(const std::string& path) { frame_ = av_frame_alloc(); packet_ = av_packet_alloc(); if (!frame_ || !packet_) { release(); return false; } width_ = codecCtx_->width; height_ = codecCtx_->height; - swsCtx_ = sws_getContext(width_, height_, codecCtx_->pix_fmt, width_, height_, AV_PIX_FMT_RGBA, SWS_BILINEAR, nullptr, nullptr, nullptr); + swsCtx_ = sws_getContext(width_, height_, codecCtx_->pix_fmt, width_, height_, AV_PIX_FMT_BGRA, SWS_BILINEAR, nullptr, nullptr, nullptr); if (!swsCtx_) { release(); return false; } rgbBuffer_.resize(width_ * height_ * 4); LOGI("Successfully loaded video from path: %s", path.c_str()); diff --git a/app/src/main/cpp/Renderer.cpp b/app/src/main/cpp/Renderer.cpp index 9552cff8..2aefd6ae 100644 --- a/app/src/main/cpp/Renderer.cpp +++ b/app/src/main/cpp/Renderer.cpp @@ -252,7 +252,7 @@ void Renderer::renderFrame(ANativeWindow* window) { currentFrameDelay_ = std::chrono::milliseconds(static_cast(1000.0 / currentMedia_.getFps())); LOGI("Media type: Video. Frame delay set to %lldms for %.2f FPS", currentFrameDelay_.count(), currentMedia_.getFps()); } else { - currentFrameDelay_ = std::chrono::milliseconds(33); // 이미지일 경우 30fps + currentFrameDelay_ = std::chrono::milliseconds(70); // 이미지일 경우 30fps LOGI("Media type: Image. Frame delay set to 33ms (~30 FPS)"); } currentState_ = RenderState::ANIMATING; diff --git a/app/src/main/cpp/native_renderer.cpp b/app/src/main/cpp/native_renderer.cpp index 7bec1697..ed9e0e40 100644 --- a/app/src/main/cpp/native_renderer.cpp +++ b/app/src/main/cpp/native_renderer.cpp @@ -93,7 +93,7 @@ Java_bums_lunatic_launcher_wall_NativeRenderer_nativeStartRenderLoop(JNIEnv* env LOGE("Could not get ANativeWindow from Surface."); return; } - ANativeWindow_setBuffersGeometry(window, 0, 0, WINDOW_FORMAT_RGBA_8888); + ANativeWindow_setBuffersGeometry(window, 0, 0, WINDOW_FORMAT_RGBX_8888); renderer->startRenderLoop(window); ANativeWindow_release(window); } 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 88f7c655..3f7edf43 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/wall/MyWallpaperService.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/wall/MyWallpaperService.kt @@ -1,6 +1,8 @@ package bums.lunatic.launcher.wall +import android.app.WallpaperManager import android.content.ContentUris +import android.graphics.BitmapFactory import android.os.Environment import android.os.Handler import android.os.HandlerThread @@ -9,6 +11,8 @@ import android.provider.MediaStore import android.service.wallpaper.WallpaperService import android.util.Log import android.view.SurfaceHolder +import androidx.work.ListenableWorker +import bums.lunatic.launcher.utils.Blog import java.io.File class MyWallpaperService : WallpaperService() { @@ -26,7 +30,7 @@ class MyWallpaperService : WallpaperService() { private lateinit var holder: SurfaceHolder private var wasInPreview = false private var nativeRenderer: NativeRenderer? = null - private var mediaFiles: List = emptyList() + private var mediaFiles: MutableList = mutableListOf() private var isVisible = false private var isSurfaceValid = false @@ -187,7 +191,7 @@ class MyWallpaperService : WallpaperService() { nativeRenderer?.setTurnPageDuration(8000) nativeRenderer?.setAnimationMode(NativeRenderer.ANIMATION_MODE_PAN) nativeRenderer?.setTransitionMode(NativeRenderer.TRANSITION_MODE_FADE) - nativeRenderer?.setAnimationSpeed(2.0f) + nativeRenderer?.setAnimationSpeed(1.0f) NativeRenderer.nativeSetNextMediaCallback(nextMediaCallback) @@ -198,12 +202,45 @@ class MyWallpaperService : WallpaperService() { // ... loadMediaFiles, nextMediaCallback, getFdFromPath는 이전과 동일 ... private fun loadMediaFiles() { if (!mediaDir.exists()) mediaDir.mkdirs() - mediaFiles = mediaDir.listFiles() - ?.filter { supportedExtensions.contains(it.extension.lowercase()) } - ?.toList() ?: emptyList() +// mediaFiles = mediaDir.listFiles() +// ?.filter { supportedExtensions.contains(it.extension.lowercase()) } +// ?.toList() ?: emptyList() Log.d(TAG, "Found ${mediaFiles.size} media files.") + val allFiles = mediaDir.listFiles() + val trashFolder = File(mediaDir, "low_res_backup") + if (!trashFolder.exists()) trashFolder.mkdirs() + val invalidImages = mutableListOf() + val wm = WallpaperManager.getInstance(this@MyWallpaperService) + val minWidth = wm.desiredMinimumWidth + val minHeight = wm.desiredMinimumHeight + val requiredSize = Math.max(minWidth, minHeight).times(0.6) + for (file in allFiles) { + + if (file.isFile && (file.extension.equals("jpg", true) || + file.extension.equals("png", true) || + file.extension.equals("jpeg", true) || + file.extension.equals("bmp", true) || // BMP 추가 + file.extension.equals("webp", true))) { + + val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } + BitmapFactory.decodeFile(file.absolutePath, options) + Blog.LOGE("requiredSize ${requiredSize} w :${options.outWidth} , h : ${options.outHeight}") + if (options.outWidth >= requiredSize && options.outHeight >= requiredSize) { + mediaFiles.add(file) + } else { + invalidImages.add(file) // 조건 미달 + } + } + } + + // 2. 부적합 이미지 이동 처리 (Job 밖에서 따로 돌려도 무방) + invalidImages.forEach { file -> + val targetFile = File(trashFolder, file.name) + file.renameTo(targetFile) // 파일 이동 + } + if (mediaFiles.isNotEmpty()) { val initialFile = mediaFiles.random() Log.d(TAG, "Attempting to load initial random media via preloader: ${initialFile.absolutePath}") @@ -223,9 +260,43 @@ class MyWallpaperService : WallpaperService() { if (!mediaDir.exists()) mediaDir.mkdirs() - mediaFiles = mediaDir.listFiles() - ?.filter { supportedExtensions.contains(it.extension.lowercase()) } - ?.toList() ?: emptyList() +// mediaFiles = mediaDir.listFiles() +// ?.filter { supportedExtensions.contains(it.extension.lowercase()) } +// ?.toList() ?: emptyList() + + val allFiles = mediaDir.listFiles() + val trashFolder = File(mediaDir, "low_res_backup") + if (!trashFolder.exists()) trashFolder.mkdirs() +// val validImages = mutableListOf() + val invalidImages = mutableListOf() + val wm = WallpaperManager.getInstance(this@MyWallpaperService) + val minWidth = wm.desiredMinimumWidth + val minHeight = wm.desiredMinimumHeight + val requiredSize = Math.max(minWidth, minHeight).times(0.6) + for (file in allFiles) { + + if (file.isFile && (file.extension.equals("jpg", true) || + file.extension.equals("png", true) || + file.extension.equals("jpeg", true) || + file.extension.equals("bmp", true) || // BMP 추가 + file.extension.equals("webp", true))) { + + val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } + BitmapFactory.decodeFile(file.absolutePath, options) + Blog.LOGE("requiredSize ${requiredSize} w :${options.outWidth} , h : ${options.outHeight}") + if (options.outWidth >= requiredSize && options.outHeight >= requiredSize) { + mediaFiles.add(file) + } else { + invalidImages.add(file) // 조건 미달 + } + } + } + + // 2. 부적합 이미지 이동 처리 (Job 밖에서 따로 돌려도 무방) + invalidImages.forEach { file -> + val targetFile = File(trashFolder, file.name) + file.renameTo(targetFile) // 파일 이동 + } val nextFile = mediaFiles.random() Log.d(TAG, "Callback: Preloading next random media: ${nextFile.absolutePath}") diff --git a/app/src/main/kotlin/bums/lunatic/launcher/workers/TorrentManager.kt b/app/src/main/kotlin/bums/lunatic/launcher/workers/TorrentManager.kt index 9c1940e4..0a7e3e1b 100644 --- a/app/src/main/kotlin/bums/lunatic/launcher/workers/TorrentManager.kt +++ b/app/src/main/kotlin/bums/lunatic/launcher/workers/TorrentManager.kt @@ -1,5 +1,6 @@ package bums.lunatic.launcher.workers +import android.annotation.SuppressLint import android.app.* import android.content.* import android.net.* @@ -50,9 +51,10 @@ class TorrentService : Service() { fun getService(): TorrentService = this@TorrentService } + @SuppressLint("ServiceCast") override fun onCreate() { super.onCreate() - notificationManager = getSystemService(Context.CONNECTIVITY_SERVICE) as NotificationManager + notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager startForegroundService() initLibTorrent() @@ -219,6 +221,29 @@ class TorrentService : Service() { val status = handle.status() val state = status.state() + val hashStr = status.infoHash().toString() + var rawName = status.name() + + if (status.hasMetadata()) { + val torrentInfo = handle.torrentFile() + if (torrentInfo != null && torrentInfo.isValid) { + val realName = torrentInfo.name() + if (!realName.isNullOrEmpty()) { + rawName = realName + } + } + } + + val displayName = if (rawName.isNullOrEmpty() || rawName == hashStr) { + if (status.hasMetadata()) { + "파일 정보 분석 중..." + } else { + "메타데이터 수신 중... (${hashStr.take(6)})" + } + } else { + rawName + } + val isPaused = status.flags().and_(libtorrent.getPaused()).nonZero() val isFinished = status.isFinished val isDownloading = (state.swig() == torrent_status.state_t.downloading.swigValue()) @@ -233,7 +258,7 @@ class TorrentService : Service() { tasks.add(TorrentTask( infoHash = status.infoHash().toString(), - name = status.name() ?: "알 수 없음", + name = displayName, progress = status.progress() * 100f, isPaused = isPaused, isQueued = !isPaused && !isFinished && !isDownloading,