This commit is contained in:
lunaticbum 2026-03-27 18:08:08 +09:00
parent 99e1d49a4b
commit a7df635f66
8 changed files with 133 additions and 152 deletions

View File

@ -93,6 +93,7 @@ Java_bums_lunatic_launcher_wall_NativeRenderer_nativeStartRenderLoop(JNIEnv* env
LOGE("Could not get ANativeWindow from Surface."); LOGE("Could not get ANativeWindow from Surface.");
return; return;
} }
ANativeWindow_setBuffersGeometry(window, 0, 0, WINDOW_FORMAT_RGBA_8888);
renderer->startRenderLoop(window); renderer->startRenderLoop(window);
ANativeWindow_release(window); ANativeWindow_release(window);
} }

View File

@ -1,108 +0,0 @@
//package bums.lunatic.launcher
//
//import bums.lunatic.launcher.common.CommonActivity
//import android.Manifest
//import android.app.Activity
//import android.content.Intent
//import android.content.pm.PackageManager
//import android.graphics.Bitmap
//import android.net.Uri
//import android.os.Build
//import android.os.Bundle
//import android.provider.MediaStore
//import android.widget.Button
//import android.widget.ImageView
//import androidx.activity.result.ActivityResultLauncher
//import androidx.activity.result.contract.ActivityResultContracts
//import androidx.annotation.RequiresApi
//import androidx.appcompat.app.AppCompatActivity
//import androidx.core.app.ActivityCompat
//import androidx.core.content.ContextCompat
//import bums.lunatic.launcher.utils.Blog
//
//
//class PhotoFilter : CommonActivity() {
//// private lateinit var originalImageView: ImageView
//// private lateinit var filteredImageView: ImageView
//// private lateinit var selectImageButton: Button
//// private val PICK_IMAGE_REQUEST = 1
//// private val PERMISSION_REQUEST_CODE = 200
////
//// init {
////
//// }
////
//// override fun onCreate(savedInstanceState: Bundle?) {
//// super.onCreate(savedInstanceState)
//// setContentView(R.layout.photo_filter)
////
//// originalImageView = findViewById(R.id.originalImageView)
//// filteredImageView = findViewById(R.id.filteredImageView)
//// selectImageButton = findViewById(R.id.selectImageButton)
////
//// selectImageButton.setOnClickListener {
//// Blog.LOGE("imagePickerLauncher checkPermission() ${checkPermission()}")
//// if (checkPermission()) {
//// openGallery()
//// } else {
//// requestPermission()
//// }
//// }
////
//// imagePickerLauncher = registerForActivityResult(
//// ActivityResultContracts.GetContent(),
//// {result ->
//// Blog.LOGE("imagePickerLauncher result ${result}")
//// if (result != null) {
//// // Handle the selected image
//// FilePathUri = result;
//// try {
//// var bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), FilePathUri)
//// Blog.LOGE("imagePickerLauncher result ${result} 2")
//// originalImageView.setImageBitmap(bitmap)
//// Blog.LOGE("imagePickerLauncher result ${result} 3")
//// val filteredBitmap = applyCartoonFilter(bitmap)
//// Blog.LOGE("imagePickerLauncher result ${result} 4")
//// filteredImageView.setImageBitmap(filteredBitmap)
//// } catch (e : Exception ) {
//// e.printStackTrace();
//// }
//// }
//// }
//// );
//// }
////
//// var FilePathUri : Uri? = null
////
//// private var imagePickerLauncher : ActivityResultLauncher<String>? = null
////
////
//// private fun openGallery() {
////// val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
////// startActivityForResult(intent, PICK_IMAGE_REQUEST)
////
//// imagePickerLauncher?.launch("image/*");
//// Blog.LOGE("imagePickerLauncher ")
////
//// }
////
////
//// @RequiresApi(Build.VERSION_CODES.TIRAMISU)
//// private fun checkPermission(): Boolean {
//// return ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES) == PackageManager.PERMISSION_GRANTED
//// }
////
//// @RequiresApi(Build.VERSION_CODES.TIRAMISU)
//// private fun requestPermission() {
//// ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_MEDIA_IMAGES), PERMISSION_REQUEST_CODE)
//// }
////
//// override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
//// super.onRequestPermissionsResult(requestCode, permissions, grantResults)
//// if (requestCode == PERMISSION_REQUEST_CODE) {
//// if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//// openGallery()
//// }
//// }
//// }
//}

View File

@ -202,31 +202,31 @@ class ForeGroundService : Service() {
val filter = IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED) val filter = IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED)
registerReceiver(bluetoothreceiver, filter) registerReceiver(bluetoothreceiver, filter)
refreshFeeds() refreshFeeds()
startWallpaperTimer() // startWallpaperTimer()
startForeGround(vibrator = true) startForeGround(vibrator = true)
} }
private fun startWallpaperTimer() { private fun startWallpaperTimer() {
serviceScope.launch { // serviceScope.launch {
while (true) { // while (true) {
changeWallPaper() // changeWallPaper()
delay(TimeUnit.MINUTES.toMillis(10)) // 10분 대기 // delay(TimeUnit.MINUTES.toMillis(10)) // 10분 대기
} // }
} // }
} }
private fun changeWallPaper() { private fun changeWallPaper() {
val myFolderPath = File(File(getExternalFilesDir(null), "completed_torrents"),"이미지").absolutePath // val myFolderPath = File(File(getExternalFilesDir(null), "completed_torrents"),"이미지").absolutePath
//
val workRequest = OneTimeWorkRequestBuilder<WallpaperAutoChangeWorker>() // val workRequest = OneTimeWorkRequestBuilder<WallpaperAutoChangeWorker>()
.setInputData(workDataOf("FOLDER_PATH" to myFolderPath)) // .setInputData(workDataOf("FOLDER_PATH" to myFolderPath))
.build() // .build()
//
workmanager()?.enqueueUniqueWork( // workmanager()?.enqueueUniqueWork(
"SingleWallpaperChange", // "SingleWallpaperChange",
ExistingWorkPolicy.REPLACE, // ExistingWorkPolicy.REPLACE,
workRequest // workRequest
) // )
} }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

View File

@ -260,7 +260,7 @@ class CompletedFilesFragment : Fragment() {
private fun showUnorganizedFilesDialog(filterType: FileFilterType) { private fun showUnorganizedFilesDialog(filterType: FileFilterType) {
// 사용자가 제외하고 싶어 하는 타겟 목록 // 사용자가 제외하고 싶어 하는 타겟 목록
val excludedDirs = listOf(rootDir.name, "이미지", "비디오", "문서", "기타") val excludedDirs = listOf(rootDir.name, "Images", "Videos", "Documents", "Etc")
val unorganizedFiles = mutableListOf<File>() val unorganizedFiles = mutableListOf<File>()
rootDir.walkTopDown().onEnter { dir -> rootDir.walkTopDown().onEnter { dir ->
@ -334,10 +334,7 @@ class CompletedFilesFragment : Fragment() {
private fun showMoveDialogForFiles(filesToMove: List<File>) { private fun showMoveDialogForFiles(filesToMove: List<File>) {
// 이동 가능한 디렉토리 목록 수집 (현재 폴더는 뺄 필요가 있다면 필터링 로직 추가 가능) // 이동 가능한 디렉토리 목록 수집 (현재 폴더는 뺄 필요가 있다면 필터링 로직 추가 가능)
var target = arrayListOf<String>(rootDir.name,"이미지", var target = arrayListOf<String>(rootDir.name,"Images", "Videos", "Documents", "Etc")
"비디오",
"문서",
"기타")
val folders = rootDir.listFiles()?.filter { it.isDirectory && target.contains(it.name) }?.toMutableList() ?: mutableListOf() val folders = rootDir.listFiles()?.filter { it.isDirectory && target.contains(it.name) }?.toMutableList() ?: mutableListOf()
// 루트 폴더로 꺼내는 옵션도 추가 // 루트 폴더로 꺼내는 옵션도 추가
@ -744,10 +741,10 @@ class CompletedFilesFragment : Fragment() {
filesInRoot?.forEach { file -> filesInRoot?.forEach { file ->
val ext = file.extension.lowercase() val ext = file.extension.lowercase()
val folderName = when { val folderName = when {
extImages.contains(ext) -> "이미지" extImages.contains(ext) -> "Images"
extVideos.contains(ext) -> "비디오" extVideos.contains(ext) -> "Videos"
extDocs.contains(ext) -> "문서" extDocs.contains(ext) -> "Documents"
else -> "기타" else -> "Etc"
} }
val targetDir = File(rootDir, folderName) val targetDir = File(rootDir, folderName)
if (!targetDir.exists()) targetDir.mkdirs() if (!targetDir.exists()) targetDir.mkdirs()

View File

@ -547,6 +547,10 @@ open class NeoRssActivity : CommonActivity() {
R.id.btn_x -> TokiFragment.newInstanceX() R.id.btn_x -> TokiFragment.newInstanceX()
R.id.btn_i -> TokiFragment.newInstanceI() R.id.btn_i -> TokiFragment.newInstanceI()
R.id.btn_btsearch -> TokiFragment.newInstanceMagnet() R.id.btn_btsearch -> TokiFragment.newInstanceMagnet()
// R.id.btn_img4 -> TokiFragment.newInstanceTumblr()
R.id.btn_img3 -> TokiFragment.newInstanceTumblr()
// R.id.btn_img2 -> TokiFragment.newInstancePixiv()
// R.id.btn_img1 -> TokiFragment.newInstanceArtStation()
R.id.btn_torrent -> TorrentListFragment() R.id.btn_torrent -> TorrentListFragment()
R.id.btn_info -> SystemStatusFragment() R.id.btn_info -> SystemStatusFragment()
R.id.btn_completed_files -> CompletedFilesFragment() R.id.btn_completed_files -> CompletedFilesFragment()

View File

@ -237,6 +237,55 @@ class TokiFragment : RemoteGestureFragment(), PagedTextViewInterface,KeyEventHan
putBoolean(ARG_ENABLE_GESTURE, true) putBoolean(ARG_ENABLE_GESTURE, true)
} }
} }
fun newInstanceTumblr(): TokiFragment = TokiFragment().apply {
arguments = Bundle().apply {
putString(ARG_TYPE, "tumblr")
putInt(ARG_LAST_NUM, 0)
putString(ARG_NAME, "www.tumblr")
putString(ARG_DOT, "com")
putBoolean(ARG_USE_NUM_URL, false)
putBoolean(ARG_ENABLE_GESTURE, true) // 이미지 스와이프 탐색을 위해 활성화
}
}
// 2. Pinterest: 이미지 큐레이션 및 고화질 검색
fun newInstancePinterest(): TokiFragment = TokiFragment().apply {
arguments = Bundle().apply {
putString(ARG_TYPE, "pinterest")
putInt(ARG_LAST_NUM, 0)
putString(ARG_NAME, "www.pinterest")
putString(ARG_DOT, "com")
putBoolean(ARG_USE_NUM_URL, false)
putBoolean(ARG_ENABLE_GESTURE, true)
}
}
// 3. Pixiv: 고화질 일러스트 및 서브컬처 이미지
fun newInstancePixiv(): TokiFragment = TokiFragment().apply {
arguments = Bundle().apply {
putString(ARG_TYPE, "pixiv")
putInt(ARG_LAST_NUM, 0)
putString(ARG_NAME, "www.pixiv")
putString(ARG_DOT, "net")
putBoolean(ARG_USE_NUM_URL, false)
putBoolean(ARG_ENABLE_GESTURE, true)
putBoolean(ARG_PRIVATE, false) // 필요 시 로그인 정보 유지를 위해 false
}
}
// 4. ArtStation: 전문가급 고화질 포트폴리오 사이트
fun newInstanceArtStation(): TokiFragment = TokiFragment().apply {
arguments = Bundle().apply {
putString(ARG_TYPE, "artstation")
putInt(ARG_LAST_NUM, 0)
putString(ARG_NAME, "www.artstation")
putString(ARG_DOT, "com")
putBoolean(ARG_USE_NUM_URL, false)
putBoolean(ARG_ENABLE_GESTURE, true)
}
}
} }
override fun onRemoteCenterClick() { override fun onRemoteCenterClick() {

View File

@ -4,6 +4,7 @@ import android.content.ContentUris
import android.os.Environment import android.os.Environment
import android.os.Handler import android.os.Handler
import android.os.HandlerThread import android.os.HandlerThread
import android.os.ParcelFileDescriptor
import android.provider.MediaStore import android.provider.MediaStore
import android.service.wallpaper.WallpaperService import android.service.wallpaper.WallpaperService
import android.util.Log import android.util.Log
@ -184,8 +185,8 @@ class MyWallpaperService : WallpaperService() {
nativeRenderer?.initialize() // nativeInit() -> initialize() nativeRenderer?.initialize() // nativeInit() -> initialize()
nativeRenderer?.setFadeDuration(1500) nativeRenderer?.setFadeDuration(1500)
nativeRenderer?.setTurnPageDuration(8000) nativeRenderer?.setTurnPageDuration(8000)
nativeRenderer?.setAnimationMode(NativeRenderer.ANIMATION_MODE_RANDOM) nativeRenderer?.setAnimationMode(NativeRenderer.ANIMATION_MODE_PAN)
nativeRenderer?.setTransitionMode(NativeRenderer.TRANSITION_MODE_RANDOM) nativeRenderer?.setTransitionMode(NativeRenderer.TRANSITION_MODE_FADE)
nativeRenderer?.setAnimationSpeed(2.0f) nativeRenderer?.setAnimationSpeed(2.0f)
NativeRenderer.nativeSetNextMediaCallback(nextMediaCallback) NativeRenderer.nativeSetNextMediaCallback(nextMediaCallback)
@ -213,7 +214,7 @@ class MyWallpaperService : WallpaperService() {
} }
} }
} }
val mediaDir = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "wallPapers") val mediaDir = File(File(this@MyWallpaperService.getExternalFilesDir(null), "completed_torrents"), "Images")
val supportedExtensions = listOf("mp4", "mkv", "avi", "mov","webm", "jpg", "jpeg", "png", "bmp", "webp", "gif") val supportedExtensions = listOf("mp4", "mkv", "avi", "mov","webm", "jpg", "jpeg", "png", "bmp", "webp", "gif")
private val nextMediaCallback = object : NativeRenderer.NextMediaCallback { private val nextMediaCallback = object : NativeRenderer.NextMediaCallback {
@ -237,24 +238,37 @@ class MyWallpaperService : WallpaperService() {
} }
} }
private fun getFdFromPath(path: String): Int? { private fun getFdFromPath(filePath: String): Int? {
val uri = MediaStore.Files.getContentUri("external") // val uri = MediaStore.Files.getContentUri("external")
val projection = arrayOf(MediaStore.Files.FileColumns._ID) // val projection = arrayOf(MediaStore.Files.FileColumns._ID)
val selection = "${MediaStore.Files.FileColumns.DATA} = ?" // val selection = "${MediaStore.Files.FileColumns.DATA} = ?"
val selectionArgs = arrayOf(path) // val selectionArgs = arrayOf(path)
return try { return try {
contentResolver.query(uri, projection, selection, selectionArgs, null)?.use { cursor -> val file = File(filePath)
if (cursor.moveToFirst()) { if (file.exists() && file.canRead()) {
val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID)) // 읽기 전용으로 ParcelFileDescriptor 열기
val fileUri = ContentUris.withAppendedId(uri, id) val pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)
contentResolver.openFileDescriptor(fileUri, "r")?.use { pfd -> // int형 fd 번호만 추출
pfd.detachFd() val fd = pfd.detachFd()
}
} else null // C++로 전달
fd
} else {
Log.e(TAG, "File does not exist or is not readable: $filePath")
null
} }
// contentResolver.query(uri, projection, selection, selectionArgs, null)?.use { cursor ->
// if (cursor.moveToFirst()) {
// val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID))
// val fileUri = ContentUris.withAppendedId(uri, id)
// contentResolver.openFileDescriptor(fileUri, "r")?.use { pfd ->
// pfd.detachFd()
// }
// } else null
// }
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Error getting FD for path: $path", e) Log.e(TAG, "Error getting FD for path: $filePath", e)
null null
} }
} }

View File

@ -102,6 +102,30 @@
style="@style/CommonFabStyle" style="@style/CommonFabStyle"
android:id="@+id/btn_info" android:id="@+id/btn_info"
/> />
<!-- <bums.lunatic.launcher.view.FloatingActionButton-->
<!-- app:fab_label="⛏️"-->
<!-- android:visibility="gone"-->
<!-- style="@style/CommonFabStyle"-->
<!-- android:id="@+id/btn_img1"-->
<!-- />-->
<!-- <bums.lunatic.launcher.view.FloatingActionButton-->
<!-- app:fab_label="⛏️"-->
<!-- android:visibility="gone"-->
<!-- style="@style/CommonFabStyle"-->
<!-- android:id="@+id/btn_img2"-->
<!-- />-->
<bums.lunatic.launcher.view.FloatingActionButton
app:fab_label="🌃"
android:visibility="gone"
style="@style/CommonFabStyle"
android:id="@+id/btn_img3"
/>
<!-- <bums.lunatic.launcher.view.FloatingActionButton-->
<!-- app:fab_label="⛏️"-->
<!-- android:visibility="gone"-->
<!-- style="@style/CommonFabStyle"-->
<!-- android:id="@+id/btn_img4"-->
<!-- />-->
<bums.lunatic.launcher.view.FloatingActionButton <bums.lunatic.launcher.view.FloatingActionButton
style="@style/CommonFabStyle" style="@style/CommonFabStyle"
app:fab_label="❌" app:fab_label="❌"