diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4ffba9b..027b28a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,10 @@ + + = Build.VERSION_CODES.R) { + checkStoragePermissionApi30() + } else { + checkStoragePermissionApi19() + } + + @RequiresApi(Build.VERSION_CODES.R) + private fun ComponentActivity.checkStoragePermissionApi30(): Boolean { + val appOps = getSystemService(AppOpsManager::class.java) + val mode = appOps.unsafeCheckOpNoThrow( + MANAGE_EXTERNAL_STORAGE_PERMISSION, + applicationInfo.uid, + packageName + ) + + return mode == AppOpsManager.MODE_ALLOWED + } + + private fun ComponentActivity.checkStoragePermissionApi19(): Boolean { + val status = + ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) + + return status == PackageManager.PERMISSION_GRANTED + } + + internal fun ComponentActivity.requestStoragePermission() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + requestStoragePermissionApi30() + } else { + requestStoragePermissionApi19() + } + } + + @RequiresApi(Build.VERSION_CODES.R) + private fun ComponentActivity.requestStoragePermissionApi30() { + val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION) + + startActivityForResult(intent, MANAGE_EXTERNAL_STORAGE_PERMISSION_REQUEST) + } + + private fun ComponentActivity.requestStoragePermissionApi19() { + val permissions = arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE) + ActivityCompat.requestPermissions( + this, + permissions, + READ_EXTERNAL_STORAGE_PERMISSION_REQUEST + ) + } + + private companion object { + private const val MANAGE_EXTERNAL_STORAGE_PERMISSION = "android:manage_external_storage" + private const val MANAGE_EXTERNAL_STORAGE_PERMISSION_REQUEST = 100 + private const val READ_EXTERNAL_STORAGE_PERMISSION_REQUEST = 101 + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/kr/co/ui/widget/FileBox.kt b/core/ui/src/main/java/kr/co/ui/widget/FileBox.kt index ef44f57..449deff 100644 --- a/core/ui/src/main/java/kr/co/ui/widget/FileBox.kt +++ b/core/ui/src/main/java/kr/co/ui/widget/FileBox.kt @@ -36,8 +36,8 @@ fun FileBox( vertical = 12.dp, horizontal = 8.dp ) - .semantics { - contentDescription = name + .semantics { + contentDescription = "$name" }, verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(24.dp) @@ -51,20 +51,14 @@ fun FileBox( ) Column( - modifier = Modifier - .clearAndSetSemantics { }, verticalArrangement = Arrangement.spacedBy(4.dp) ) { Text( - modifier = Modifier - .clearAndSetSemantics { }, text = name, style = Theme.typography.body2sb, color = Theme.colors.text ) Text( - modifier = Modifier - .clearAndSetSemantics { }, text = "2023.01.01", style = Theme.typography.caption1r, color = Theme.colors.grayText diff --git a/feature/bookmark/src/main/java/kr/co/bookmark/BookmarkScreen.kt b/feature/bookmark/src/main/java/kr/co/bookmark/BookmarkScreen.kt index b5206bf..53b9c58 100644 --- a/feature/bookmark/src/main/java/kr/co/bookmark/BookmarkScreen.kt +++ b/feature/bookmark/src/main/java/kr/co/bookmark/BookmarkScreen.kt @@ -57,6 +57,7 @@ private fun BookmarkScreen( ) } + //TODO 로컬에 저장된 북마크 데이터를 불러와 파일목록을 보여줄 예정 items(listOf("Effective Kotlin", "Android Developer")) { file -> FileBox( name = file diff --git a/feature/explore/src/main/java/kr/co/explore/ExploreScreen.kt b/feature/explore/src/main/java/kr/co/explore/ExploreScreen.kt index 26afa58..fe5466d 100644 --- a/feature/explore/src/main/java/kr/co/explore/ExploreScreen.kt +++ b/feature/explore/src/main/java/kr/co/explore/ExploreScreen.kt @@ -22,29 +22,39 @@ import kr.co.ui.theme.SeeDocsTheme import kr.co.ui.theme.Theme import kr.co.ui.widget.FileBox import kr.co.widget.FolderBox +import java.io.File @Composable internal fun ExploreRoute( + path: String, padding: PaddingValues, - navigateToPdf: () -> Unit = {} + navigateToFolder: (String) -> Unit = {}, + navigateToPdf: () -> Unit = {}, ) { + val files = readPDFOrDirectory(path) ExploreScreen( + path = path.replace("/storage/emulated/0", "내장 저장 공간"), + files = files, padding = padding, + onFolderClick = { folderPath -> navigateToFolder(folderPath) }, onFileClick = navigateToPdf ) } @Composable private fun ExploreScreen( + path: String, + files: List = emptyList(), padding: PaddingValues, + onFolderClick: (String) -> Unit = {}, onFileClick: () -> Unit = {} ) { Box( modifier = Modifier .fillMaxSize() .padding(padding) - .background(color = Theme.colors.bg) + .background(color = Theme.colors.bg), ) { LazyVerticalGrid( contentPadding = PaddingValues( @@ -69,12 +79,13 @@ private fun ExploreScreen( ) Text( text = buildAnnotatedString { - append("규상의 S24 >") + append(">${path.split("/").dropLast(1).joinToString(separator = "/")}") + append("/") withStyle( Theme.typography.caption1r.copy(color = Theme.colors.highlight) .toSpanStyle() ) { - append("Download") + append(path.split("/").last()) } }, style = Theme.typography.caption1r, @@ -83,18 +94,19 @@ private fun ExploreScreen( } } - items(listOf("Download", "Documents", "DCIM")) { folder -> + items(files.filter { it.isDirectory }) { folder -> FolderBox( - name = folder + name = folder.name, + onClick = { onFolderClick(folder.path) } ) } items( - items = listOf("Effective Kotlin", "Android Developer"), + items = files.filter { !it.isDirectory }, span = { GridItemSpan(maxLineSpan) } ) { file -> FileBox( - name = file, + name = file.name, onFileClick = onFileClick ) } @@ -102,11 +114,31 @@ private fun ExploreScreen( } } +private fun readPDFOrDirectory( + path: String, +): List = + File(path).listFiles()?.filter { !it.isHidden && (it.isDirectory || it.extension == "pdf") }?.map { + Item( + name = it.name, + path = it.path, + type = it.extension, + isDirectory = it.isDirectory + ) + }?: emptyList() + +private data class Item( + val name: String, + val path: String, + val type: String, + val isDirectory: Boolean, +) + @Preview @Composable private fun Preview() { SeeDocsTheme { ExploreScreen( + path = "/storage/emulated/0/Download", padding = PaddingValues(), ) } diff --git a/feature/explore/src/main/java/kr/co/navigation/ExploreNavGraph.kt b/feature/explore/src/main/java/kr/co/navigation/ExploreNavGraph.kt index f6786d3..37d3e90 100644 --- a/feature/explore/src/main/java/kr/co/navigation/ExploreNavGraph.kt +++ b/feature/explore/src/main/java/kr/co/navigation/ExploreNavGraph.kt @@ -1,17 +1,22 @@ package kr.co.navigation +import android.os.Environment import androidx.compose.foundation.layout.PaddingValues import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable +import androidx.navigation.toRoute import kr.co.explore.ExploreRoute fun NavGraphBuilder.exploreNavGraph( padding: PaddingValues, + navigateToFolder: (String) -> Unit = {}, navigateToPdf: () -> Unit = {} ) { composable { ExploreRoute( + path = it.toRoute().path?: Environment.getExternalStorageDirectory().absolutePath, padding = padding, + navigateToFolder = navigateToFolder, navigateToPdf = navigateToPdf ) } diff --git a/feature/explore/src/main/java/kr/co/widget/FolderBox.kt b/feature/explore/src/main/java/kr/co/widget/FolderBox.kt index 953d25a..1122a9f 100644 --- a/feature/explore/src/main/java/kr/co/widget/FolderBox.kt +++ b/feature/explore/src/main/java/kr/co/widget/FolderBox.kt @@ -2,6 +2,7 @@ package kr.co.widget import androidx.compose.foundation.background import androidx.compose.foundation.border +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row @@ -13,6 +14,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -25,9 +27,12 @@ import kr.co.ui.theme.Theme internal fun FolderBox( name: String, modifier: Modifier = Modifier, + onClick: () -> Unit = {} ) { Box( modifier = modifier + .clip(RoundedCornerShape(8.dp)) + .clickable(onClick = onClick) .border( width = 1.dp, color = Theme.colors.grayText, diff --git a/feature/main/src/main/java/kr/co/main/MainBottomBar.kt b/feature/main/src/main/java/kr/co/main/MainBottomBar.kt index 8b9db57..2029561 100644 --- a/feature/main/src/main/java/kr/co/main/MainBottomBar.kt +++ b/feature/main/src/main/java/kr/co/main/MainBottomBar.kt @@ -55,7 +55,7 @@ internal fun MainBottomBar( ), ) { tabs.forEach { tab -> - Item( + BottomItem( tab = tab, selected = tab == currentTab, onClick = { onTabSelected(tab) } @@ -67,7 +67,7 @@ internal fun MainBottomBar( } @Composable -private fun RowScope.Item( +private fun RowScope.BottomItem( tab: MainTab, selected: Boolean, onClick: () -> Unit, diff --git a/feature/main/src/main/java/kr/co/main/navigation/MainNavHost.kt b/feature/main/src/main/java/kr/co/main/navigation/MainNavHost.kt index 5eafe6d..5608628 100644 --- a/feature/main/src/main/java/kr/co/main/navigation/MainNavHost.kt +++ b/feature/main/src/main/java/kr/co/main/navigation/MainNavHost.kt @@ -3,6 +3,7 @@ package kr.co.main.navigation import androidx.compose.foundation.layout.PaddingValues import androidx.compose.runtime.Composable import androidx.navigation.compose.NavHost +import kr.co.navigation.MainNavigation import kr.co.navigation.Route import kr.co.navigation.bookmarkNavGraph import kr.co.navigation.exploreNavGraph @@ -20,6 +21,7 @@ internal fun MainNavHost( ) { exploreNavGraph( padding = padding, + navigateToFolder = { navigator.navigate(MainNavigation.Explore(it)) }, navigateToPdf = { navigator.navigate(Route.Pdf) } ) diff --git a/feature/main/src/main/java/kr/co/main/navigation/MainNavigator.kt b/feature/main/src/main/java/kr/co/main/navigation/MainNavigator.kt index ebfc763..a770dbb 100644 --- a/feature/main/src/main/java/kr/co/main/navigation/MainNavigator.kt +++ b/feature/main/src/main/java/kr/co/main/navigation/MainNavigator.kt @@ -20,7 +20,7 @@ internal class MainNavigator( @Composable get() = navController .currentBackStackEntryAsState().value?.destination - val startDestination: MainNavigation = MainNavigation.Explore + val startDestination: MainNavigation = MainNavigation.Explore() @Composable fun currentTab(): MainTab? = diff --git a/feature/main/src/main/java/kr/co/main/navigation/MainTab.kt b/feature/main/src/main/java/kr/co/main/navigation/MainTab.kt index 0736fe1..81aac3f 100644 --- a/feature/main/src/main/java/kr/co/main/navigation/MainTab.kt +++ b/feature/main/src/main/java/kr/co/main/navigation/MainTab.kt @@ -13,7 +13,7 @@ internal enum class MainTab( Explore( icon = SeeDocsIcon.Explore, contentDescription = "Explore", - route = MainNavigation.Explore, + route = MainNavigation.Explore(), ), Recent( icon = SeeDocsIcon.RecentFill,