Coverage Summary for Class: LogxFilePathResolver (kr.open.library.simple_ui.core.logcat.internal.writer)

Class Class, % Method, % Branch, % Line, % Instruction, %
LogxFilePathResolver 100% (1/1) 100% (5/5) 37.5% (12/32) 54.1% (20/37) 56.4% (88/156)


 package kr.open.library.simple_ui.core.logcat.internal.writer
 
 import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
 import android.content.Context
 import android.content.pm.PackageManager
 import android.os.Build
 import android.os.Environment
 import android.util.Log
 import androidx.core.content.ContextCompat
 import kr.open.library.simple_ui.core.logcat.config.LogxConfigSnapshot
 import kr.open.library.simple_ui.core.logcat.internal.common.LogxPathResolver
 import java.io.File
 
 /**
  * 로그 파일 저장 경로를 계산하고 디렉터리를 보장합니다.
  *
  * Resolves log file directory paths and ensures directories exist.
  * <br><br>
  * 로그 저장 경로를 계산하고 디렉터리를 생성합니다.
  */
 internal class LogxFilePathResolver {
     /**
      * 로그 저장 디렉터리를 반환합니다.
      *
      * Resolves the log directory, creating it if needed.
      * <br><br>
      * 로그 디렉터리를 계산하고 필요 시 생성합니다.
      *
      * @param context 앱 컨텍스트.
      * @param config 현재 설정 스냅샷.
      * @param errorTag 오류 로그에 사용할 태그.
      */
     fun resolveDirectory(context: Context, config: LogxConfigSnapshot, errorTag: String): File? {
         val directoryPath = resolveDirectoryPath(context, config, errorTag) ?: return null
         val logDirectory = File(directoryPath)
 
         if (logDirectory.exists() && !logDirectory.isDirectory) {
             Log.e(errorTag, "Save directory path is not a directory: $directoryPath")
             return null
         }
 
         val blockingParent = findBlockingParent(logDirectory)
         if (blockingParent != null) {
             Log.e(errorTag, "Parent path is not a directory: ${blockingParent.absolutePath}")
             return null
         }
 
         if (!hasWritePermission(context, logDirectory, config)) {
             Log.e(errorTag, "Storage permission required for log directory: $directoryPath")
             return null
         }
 
         if (!logDirectory.exists() && !logDirectory.mkdirs()) {
             Log.e(errorTag, "Failed to create log directory: $directoryPath")
             return null
         }
         return logDirectory
     }
 
     /**
      * 디렉터리 경로 문자열을 계산합니다.
      *
      * Resolves the directory path string for log storage.
      * <br><br>
      * 로그 저장 경로 문자열을 계산합니다.
      *
      * @param context 앱 컨텍스트.
      * @param config 현재 설정 스냅샷.
      * @param errorTag 오류 로그에 사용할 태그.
      */
     private fun resolveDirectoryPath(context: Context, config: LogxConfigSnapshot, errorTag: String): String? {
         if (config.saveDirectory != null) {
             return config.saveDirectory
         }
 
         if (LogxPathResolver.requiresPermission(config.storageType) &&
             !LogxPathResolver.hasWritePermission(context, config.storageType)
         ) {
             Log.e(errorTag, "Storage permission required for PUBLIC_EXTERNAL on API 28 and below.")
             return null
         }
 
         return LogxPathResolver.resolvePath(context, config.storageType)
     }
 
     private fun findBlockingParent(directory: File): File? =
         generateSequence(directory.parentFile) { current -> current.parentFile }
             .firstOrNull { current -> current.exists() && !current.isDirectory }
 
     private fun hasWritePermission(context: Context, directory: File, config: LogxConfigSnapshot): Boolean {
         if (config.saveDirectory == null) {
             if (!LogxPathResolver.requiresPermission(config.storageType)) return true
             return LogxPathResolver.hasWritePermission(context, config.storageType)
         }
 
         if (!LogxPathResolver.isSupportedCustomDirectory(context, directory)) {
             return false
         }
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) return true
 
         @Suppress("DEPRECATION")
         val publicExternalRoot = Environment.getExternalStorageDirectory().absolutePath
         val requiresExternalPermission = directory.absolutePath.startsWith(publicExternalRoot)
         if (!requiresExternalPermission) return true
 
         return ContextCompat.checkSelfPermission(context, WRITE_EXTERNAL_STORAGE) ==
             PackageManager.PERMISSION_GRANTED
     }
 }