diff --git a/.gitignore b/.gitignore index e10ceb9..cc698b0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,6 @@ -git *.iml .gradle +.idea /local.properties -/.idea/caches -/.idea/libraries -/.idea/modules.xml -/.idea/workspace.xml -/.idea/navEditor.xml -/.idea/sonarlint -/.idea/assetWizardSettings.xml .DS_Store /build /captures diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 26d3352..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index b589d56..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml deleted file mode 100644 index 0c0c338..0000000 --- a/.idea/deploymentTargetDropDown.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index ee7b132..0000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index 44ca2d9..0000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml deleted file mode 100644 index fdf8d99..0000000 --- a/.idea/kotlinc.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml deleted file mode 100644 index f8051a6..0000000 --- a/.idea/migrations.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 0ad17cb..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/sonarlint/issuestore/0/4/042c535c429bdd8eb38e72c5823732d8e9fc56f4 b/.idea/sonarlint/issuestore/0/4/042c535c429bdd8eb38e72c5823732d8e9fc56f4 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/0/7/078508c831007ccb3938b355f0d30a8368f99a8d b/.idea/sonarlint/issuestore/0/7/078508c831007ccb3938b355f0d30a8368f99a8d deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/2/4/24ce74baca63d400e46baee54dfd1daccb063d26 b/.idea/sonarlint/issuestore/2/4/24ce74baca63d400e46baee54dfd1daccb063d26 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/2/a/2a39d64fb5e43e17f8377da18e4dd7cc29d88e6c b/.idea/sonarlint/issuestore/2/a/2a39d64fb5e43e17f8377da18e4dd7cc29d88e6c deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/2/f/2fe217d36e73005d1fe23b1af3dc3021b9b2bcb9 b/.idea/sonarlint/issuestore/2/f/2fe217d36e73005d1fe23b1af3dc3021b9b2bcb9 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/3/b/3bc4db92963be77ebed9d7a4fff09127a1110e4d b/.idea/sonarlint/issuestore/3/b/3bc4db92963be77ebed9d7a4fff09127a1110e4d deleted file mode 100644 index 70bf876..0000000 --- a/.idea/sonarlint/issuestore/3/b/3bc4db92963be77ebed9d7a4fff09127a1110e4d +++ /dev/null @@ -1,2 +0,0 @@ - -I xml:S1135"4Complete the task associated to this "TODO" comment.(ÿ°Á‡ \ No newline at end of file diff --git a/.idea/sonarlint/issuestore/4/9/49845c622cb06d895975c7b1e3f943e320bce688 b/.idea/sonarlint/issuestore/4/9/49845c622cb06d895975c7b1e3f943e320bce688 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/5/1/51e1c5d383dfaa35e0e7e5873a0a99355a86880f b/.idea/sonarlint/issuestore/5/1/51e1c5d383dfaa35e0e7e5873a0a99355a86880f deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/5/5/555c0e66444eb0c348537e60a7e7bf60f2a3f57d b/.idea/sonarlint/issuestore/5/5/555c0e66444eb0c348537e60a7e7bf60f2a3f57d deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/6/e/6ec7d34b130497a10dc92abcde9b313d370a89df b/.idea/sonarlint/issuestore/6/e/6ec7d34b130497a10dc92abcde9b313d370a89df deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/7/9/7962297d9704dd3414ea37c6c18ad6265dcefd50 b/.idea/sonarlint/issuestore/7/9/7962297d9704dd3414ea37c6c18ad6265dcefd50 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/8/c/8c55c3ccc257e5907959013f99656e4c8ec3903e b/.idea/sonarlint/issuestore/8/c/8c55c3ccc257e5907959013f99656e4c8ec3903e deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/8/e/8e27ba16c655f421171956147d0fde89b7d25d17 b/.idea/sonarlint/issuestore/8/e/8e27ba16c655f421171956147d0fde89b7d25d17 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/9/6/969faa2a5d9f5e8390e958d2c07f2cfd54676e6b b/.idea/sonarlint/issuestore/9/6/969faa2a5d9f5e8390e958d2c07f2cfd54676e6b deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/9/c/9c2a853ebd9f87844b147b05656fea1b7325609e b/.idea/sonarlint/issuestore/9/c/9c2a853ebd9f87844b147b05656fea1b7325609e deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/9/e/9e08934d811afe28fbc77aaa3c0d747b94348db9 b/.idea/sonarlint/issuestore/9/e/9e08934d811afe28fbc77aaa3c0d747b94348db9 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/a/5/a5cc2925ca8258af241be7e5b0381edf30266302 b/.idea/sonarlint/issuestore/a/5/a5cc2925ca8258af241be7e5b0381edf30266302 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/a/a/aa8f09dc818431a1d89e3112f6cfbf167cc8ce6b b/.idea/sonarlint/issuestore/a/a/aa8f09dc818431a1d89e3112f6cfbf167cc8ce6b deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/c/a/ca80fb3fe73b5d731cf9a48f29a9d84a551c1376 b/.idea/sonarlint/issuestore/c/a/ca80fb3fe73b5d731cf9a48f29a9d84a551c1376 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/d/2/d2281fbb3027de2722081a53408dd77628bf080e b/.idea/sonarlint/issuestore/d/2/d2281fbb3027de2722081a53408dd77628bf080e deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/d/2/d2a4383452aad84a18429a5844d31330a012a9e6 b/.idea/sonarlint/issuestore/d/2/d2a4383452aad84a18429a5844d31330a012a9e6 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/d/7/d7c93c97f99f4c73287b6fd9dbc30bd6a4e17884 b/.idea/sonarlint/issuestore/d/7/d7c93c97f99f4c73287b6fd9dbc30bd6a4e17884 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/d/b/dbcff70658daf80b53ce624f6adcaa529df5ed8d b/.idea/sonarlint/issuestore/d/b/dbcff70658daf80b53ce624f6adcaa529df5ed8d deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/e/0/e0bc1eda8e2c689cb79d5b6e5cfd11385f06c2b8 b/.idea/sonarlint/issuestore/e/0/e0bc1eda8e2c689cb79d5b6e5cfd11385f06c2b8 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/e/b/eb7f0f821bde169dd21862705b2b042d2cea69a3 b/.idea/sonarlint/issuestore/e/b/eb7f0f821bde169dd21862705b2b042d2cea69a3 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/issuestore/f/4/f4ccae7b16112b00a2d85965108cf28a4b17a2b4 b/.idea/sonarlint/issuestore/f/4/f4ccae7b16112b00a2d85965108cf28a4b17a2b4 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/0/4/042c535c429bdd8eb38e72c5823732d8e9fc56f4 b/.idea/sonarlint/securityhotspotstore/0/4/042c535c429bdd8eb38e72c5823732d8e9fc56f4 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/0/7/078508c831007ccb3938b355f0d30a8368f99a8d b/.idea/sonarlint/securityhotspotstore/0/7/078508c831007ccb3938b355f0d30a8368f99a8d deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/2/4/24ce74baca63d400e46baee54dfd1daccb063d26 b/.idea/sonarlint/securityhotspotstore/2/4/24ce74baca63d400e46baee54dfd1daccb063d26 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/2/a/2a39d64fb5e43e17f8377da18e4dd7cc29d88e6c b/.idea/sonarlint/securityhotspotstore/2/a/2a39d64fb5e43e17f8377da18e4dd7cc29d88e6c deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/2/f/2fe217d36e73005d1fe23b1af3dc3021b9b2bcb9 b/.idea/sonarlint/securityhotspotstore/2/f/2fe217d36e73005d1fe23b1af3dc3021b9b2bcb9 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/3/b/3bc4db92963be77ebed9d7a4fff09127a1110e4d b/.idea/sonarlint/securityhotspotstore/3/b/3bc4db92963be77ebed9d7a4fff09127a1110e4d deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/4/9/49845c622cb06d895975c7b1e3f943e320bce688 b/.idea/sonarlint/securityhotspotstore/4/9/49845c622cb06d895975c7b1e3f943e320bce688 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/5/1/51e1c5d383dfaa35e0e7e5873a0a99355a86880f b/.idea/sonarlint/securityhotspotstore/5/1/51e1c5d383dfaa35e0e7e5873a0a99355a86880f deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/5/5/555c0e66444eb0c348537e60a7e7bf60f2a3f57d b/.idea/sonarlint/securityhotspotstore/5/5/555c0e66444eb0c348537e60a7e7bf60f2a3f57d deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/6/e/6ec7d34b130497a10dc92abcde9b313d370a89df b/.idea/sonarlint/securityhotspotstore/6/e/6ec7d34b130497a10dc92abcde9b313d370a89df deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/7/9/7962297d9704dd3414ea37c6c18ad6265dcefd50 b/.idea/sonarlint/securityhotspotstore/7/9/7962297d9704dd3414ea37c6c18ad6265dcefd50 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/8/c/8c55c3ccc257e5907959013f99656e4c8ec3903e b/.idea/sonarlint/securityhotspotstore/8/c/8c55c3ccc257e5907959013f99656e4c8ec3903e deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/8/e/8e27ba16c655f421171956147d0fde89b7d25d17 b/.idea/sonarlint/securityhotspotstore/8/e/8e27ba16c655f421171956147d0fde89b7d25d17 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/9/6/969faa2a5d9f5e8390e958d2c07f2cfd54676e6b b/.idea/sonarlint/securityhotspotstore/9/6/969faa2a5d9f5e8390e958d2c07f2cfd54676e6b deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/9/c/9c2a853ebd9f87844b147b05656fea1b7325609e b/.idea/sonarlint/securityhotspotstore/9/c/9c2a853ebd9f87844b147b05656fea1b7325609e deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/9/e/9e08934d811afe28fbc77aaa3c0d747b94348db9 b/.idea/sonarlint/securityhotspotstore/9/e/9e08934d811afe28fbc77aaa3c0d747b94348db9 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/a/5/a5cc2925ca8258af241be7e5b0381edf30266302 b/.idea/sonarlint/securityhotspotstore/a/5/a5cc2925ca8258af241be7e5b0381edf30266302 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/a/a/aa8f09dc818431a1d89e3112f6cfbf167cc8ce6b b/.idea/sonarlint/securityhotspotstore/a/a/aa8f09dc818431a1d89e3112f6cfbf167cc8ce6b deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/c/3/c3c1434c45ed15ce4f358f51e25422c2f32f6c32 b/.idea/sonarlint/securityhotspotstore/c/3/c3c1434c45ed15ce4f358f51e25422c2f32f6c32 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/c/a/ca80fb3fe73b5d731cf9a48f29a9d84a551c1376 b/.idea/sonarlint/securityhotspotstore/c/a/ca80fb3fe73b5d731cf9a48f29a9d84a551c1376 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/d/2/d2281fbb3027de2722081a53408dd77628bf080e b/.idea/sonarlint/securityhotspotstore/d/2/d2281fbb3027de2722081a53408dd77628bf080e deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/d/2/d2a4383452aad84a18429a5844d31330a012a9e6 b/.idea/sonarlint/securityhotspotstore/d/2/d2a4383452aad84a18429a5844d31330a012a9e6 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/d/7/d7c93c97f99f4c73287b6fd9dbc30bd6a4e17884 b/.idea/sonarlint/securityhotspotstore/d/7/d7c93c97f99f4c73287b6fd9dbc30bd6a4e17884 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/d/b/dbcff70658daf80b53ce624f6adcaa529df5ed8d b/.idea/sonarlint/securityhotspotstore/d/b/dbcff70658daf80b53ce624f6adcaa529df5ed8d deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/e/0/e0bc1eda8e2c689cb79d5b6e5cfd11385f06c2b8 b/.idea/sonarlint/securityhotspotstore/e/0/e0bc1eda8e2c689cb79d5b6e5cfd11385f06c2b8 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/e/b/eb7f0f821bde169dd21862705b2b042d2cea69a3 b/.idea/sonarlint/securityhotspotstore/e/b/eb7f0f821bde169dd21862705b2b042d2cea69a3 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/sonarlint/securityhotspotstore/f/4/f4ccae7b16112b00a2d85965108cf28a4b17a2b4 b/.idea/sonarlint/securityhotspotstore/f/4/f4ccae7b16112b00a2d85965108cf28a4b17a2b4 deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2f46566..2354b92 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -9,6 +9,8 @@ + diff --git a/app/src/main/java/com/ss/smartstorage/MainActivity.kt b/app/src/main/java/com/ss/smartstorage/MainActivity.kt index f766303..0cb29d3 100644 --- a/app/src/main/java/com/ss/smartstorage/MainActivity.kt +++ b/app/src/main/java/com/ss/smartstorage/MainActivity.kt @@ -1,5 +1,6 @@ package com.ss.smartstorage +import android.annotation.SuppressLint import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent @@ -8,12 +9,11 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.ui.Modifier import com.ss.smart_storage.SmartStorage -import com.ss.smart_storage.util.SmartDirectory -import com.ss.smart_storage.util.SmartFileType import com.ss.smartstorage.ui.theme.SmartStorageTheme class MainActivity : ComponentActivity() { + @SuppressLint("NewApi") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -27,14 +27,14 @@ class MainActivity : ComponentActivity() { color = MaterialTheme.colorScheme.background ) { SmartStorageSample( - onStoreTap = { - smartStorage.store( - location = SmartDirectory.CUSTOM, - fileName = "interstellar", - fileType = SmartFileType.txt, - fileData = "Directed by Christopher Nolan".toByteArray() - ) - }, + onStoreTap = { fileName, fileType, location -> + smartStorage.store( + location = location, + fileType = fileType, + fileName = fileName, + fileData = "This is a sample txt file.".toByteArray() + ) + } ) } } diff --git a/app/src/main/java/com/ss/smartstorage/SmartStorageSample.kt b/app/src/main/java/com/ss/smartstorage/SmartStorageSample.kt index ac0401a..3ad8a7c 100644 --- a/app/src/main/java/com/ss/smartstorage/SmartStorageSample.kt +++ b/app/src/main/java/com/ss/smartstorage/SmartStorageSample.kt @@ -1,23 +1,266 @@ package com.ss.smartstorage + import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.material3.ElevatedButton +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.TextField +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.ss.smart_storage.util.SmartDirectory +import com.ss.smart_storage.util.SmartFileType +import kotlinx.coroutines.launch +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SmartStorageToolBar() { + TopAppBar( + title = { + Text( + text = "Smart Storage Demo", + textAlign = TextAlign.Center, + fontSize = 30.sp, + fontWeight = FontWeight.Bold + ) + }, + + ) +} + +@OptIn(ExperimentalMaterial3Api::class) @Composable fun SmartStorageSample( - onStoreTap : () -> Unit, -){ - Column( - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, - ) { - ElevatedButton(onClick = {onStoreTap()}) { - Text(text = "SAVE DUMMY") } + onStoreTap: (String, SmartFileType, String) -> Unit +) { + var showBottomSheetD by remember { mutableStateOf(false) } + + val sheetState = rememberModalBottomSheetState() + val scope = rememberCoroutineScope() + + var selectedDestination by remember { + mutableStateOf("") + } + + var isSaveButtonVisible by remember { + mutableStateOf(false) + } + + var showBottomSheetF by remember { + mutableStateOf(false) + } + var fileName by remember { + mutableStateOf("") + } + + var selectedFiletype by remember { + mutableStateOf(SmartFileType.TXT) + } + val snackbarHostState = remember { SnackbarHostState() } + + Scaffold(topBar = { SmartStorageToolBar() }, snackbarHost = { + SnackbarHost(hostState = snackbarHostState) + }, content = { contentPadding -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues = contentPadding) + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(start = 20.dp, end = 30.dp, top = 10.dp) + ) { + Column( + modifier = Modifier.fillMaxWidth() + ) { + Text(text = "File Name : ") + Spacer(modifier = Modifier.height(16.dp)) + + Row { + + TextField( + value = fileName, + onValueChange = { + fileName = it + }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text), + modifier = Modifier.fillMaxWidth() + ) + + + } + } + Spacer(modifier = Modifier.height(20.dp)) + + + Column( + modifier = Modifier.fillMaxWidth() + ) { + Text(text = "File Storage Type : ") + Spacer(modifier = Modifier.height(16.dp)) + OutlinedButton(onClick = { showBottomSheetF = true }) { + Text(text = selectedFiletype.toString().ifEmpty { "Select" }) + + } + + } + + Spacer(modifier = Modifier.height(20.dp)) + Column( + modifier = Modifier.fillMaxWidth() + ) { + Text(text = "File Storage Destination : ") + Spacer(modifier = Modifier.height(16.dp)) + OutlinedButton(onClick = { showBottomSheetD = true }) { + Text(text = selectedDestination.ifEmpty { "Select" }) + + } + + } + + Spacer(modifier = Modifier.height(40.dp)) + + + + Column( + modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center + ) { + Button( + enabled = isSaveButtonVisible, onClick = { + onStoreTap(fileName, selectedFiletype, selectedDestination) + }, modifier = Modifier.align(Alignment.CenterHorizontally) + ) { + Text(text = "Save") + } + } + } + + } + }) + if (showBottomSheetF) { + ModalBottomSheet( + onDismissRequest = { showBottomSheetF = false }, + sheetState = sheetState, + modifier = Modifier.fillMaxSize() + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Text( + text = "Select File Type to store File", + style = MaterialTheme.typography.titleMedium + ) + + Spacer(modifier = Modifier.height(16.dp)) + + val fileTypes = listOf( + SmartFileType.TXT, + SmartFileType.JPEG, + SmartFileType.PDF, + SmartFileType.PNG, + SmartFileType.WEBP + ) + + fileTypes.forEach { fileType -> + TextButton(onClick = { + selectedFiletype = fileType + scope.launch { sheetState.hide() }.invokeOnCompletion { + if (!sheetState.isVisible) { + showBottomSheetF = false + } + } + }) { + Text(text = fileType.toString()) + } + } + + } + + + } + } + + if (showBottomSheetD) { + ModalBottomSheet( + onDismissRequest = { showBottomSheetD = false }, + sheetState = sheetState, + modifier = Modifier.fillMaxSize() + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Text( + text = "Select Destination to store File", + style = MaterialTheme.typography.titleMedium + ) + + Spacer(modifier = Modifier.height(16.dp)) + + val destinations = listOf( + SmartDirectory.INTERNAL, + SmartDirectory.SCOPED_STORAGE, + SmartDirectory.CUSTOM, + SmartDirectory.DOWNLOADS, + SmartDirectory.DOCUMENTS, + SmartDirectory.EXTERNAL_PUBLIC + ) + + destinations.forEach { destination -> + TextButton(onClick = { + selectedDestination = destination + scope.launch { sheetState.hide() }.invokeOnCompletion { + if (!sheetState.isVisible) { + isSaveButtonVisible = true + showBottomSheetD = false + } + } + }) { + Text(text = destination) + } + } + + } + + + } } } + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index f8c6127..55344e5 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,10 +1,3 @@ - #FFBB86FC - #FF6200EE - #FF3700B3 - #FF03DAC5 - #FF018786 - #FF000000 - #FFFFFFFF \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 086afa8..e343d2b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,16 +1,16 @@ [versions] -agp = "8.3.1" +agp = "8.3.2" coilCompose = "2.6.0" kotlin = "1.9.0" -coreKtx = "1.12.0" +coreKtx = "1.13.0" junit = "4.13.2" junitVersion = "1.1.5" espressoCore = "3.5.1" lifecycleRuntimeKtx = "2.7.0" -activityCompose = "1.8.2" -composeBom = "2023.08.00" +activityCompose = "1.9.0" +composeBom = "2024.04.01" appcompat = "1.6.1" -lifecycleViewmodelCompose = "2.6.1" +lifecycleViewmodelCompose = "2.7.0" material = "1.11.0" documentfile = "1.0.1" diff --git a/smart-storage/build.gradle.kts b/smart-storage/build.gradle.kts index c23da6b..43b5f95 100644 --- a/smart-storage/build.gradle.kts +++ b/smart-storage/build.gradle.kts @@ -9,7 +9,6 @@ android { defaultConfig { minSdk = 23 - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") } @@ -33,11 +32,7 @@ android { } dependencies { - implementation(libs.androidx.core.ktx) implementation(libs.androidx.appcompat) implementation(libs.material) - testImplementation(libs.junit) - androidTestImplementation(libs.androidx.junit) - androidTestImplementation(libs.androidx.espresso.core) } \ No newline at end of file diff --git a/smart-storage/src/main/java/com/ss/smart_storage/PermissionManager.kt b/smart-storage/src/main/java/com/ss/smart_storage/PermissionManager.kt new file mode 100644 index 0000000..f13c97e --- /dev/null +++ b/smart-storage/src/main/java/com/ss/smart_storage/PermissionManager.kt @@ -0,0 +1,105 @@ +package com.ss.smart_storage + +import android.Manifest +import android.content.pm.PackageManager +import android.os.Build +import android.os.Build.VERSION_CODES.M +import android.os.Build.VERSION_CODES.Q +import android.os.Environment +import androidx.activity.ComponentActivity +import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.RequiresApi +import androidx.core.content.ContextCompat +import com.ss.smart_storage.util.PermissionStatus +import com.ss.smart_storage.util.SmartDirectory + + +class PermissionManager( + private val activity: ComponentActivity, val onPermissionGranted: (PermissionStatus) -> Unit +) { + private val requestPermissionLauncher = activity.registerForActivityResult( + ActivityResultContracts.RequestPermission() + ) { isGranted: Boolean -> + if (isGranted) { + onPermissionGranted(PermissionStatus.ACCEPTED) + } else { + onPermissionGranted(PermissionStatus.DENIED) + } + } + + fun checkLocation(location: String) { + when (location) { + + SmartDirectory.CUSTOM -> { + onPermissionGranted(PermissionStatus.NOT_APPLICABLE) + } + + SmartDirectory.INTERNAL -> { + onPermissionGranted(PermissionStatus.NOT_NEEDED) + } + + SmartDirectory.DOCUMENTS -> { + checkOsForPermissions(location) + } + + SmartDirectory.DOWNLOADS -> { + checkOsForPermissions(location) + } + + SmartDirectory.SCOPED_STORAGE -> { + onPermissionGranted(PermissionStatus.NOT_NEEDED) + } + + SmartDirectory.EXTERNAL_PUBLIC -> { + checkOsForPermissions(location) + } + } + } + + private fun checkOsForPermissions(location: String) { + when { + Build.VERSION.SDK_INT in M..Q -> { + checkIfPermissionGranted(onPermissionGranted = { + onPermissionGranted(PermissionStatus.ACCEPTED) + }) + } + + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) && location == SmartDirectory.EXTERNAL_PUBLIC -> { + + checkIfManagePermissionGranted(onPermissionGranted = { granted -> + if (granted) onPermissionGranted(PermissionStatus.ACCEPTED) + else onPermissionGranted(PermissionStatus.REDIRECT_TO_SETTINGS) + }) + + + } + + else -> { + onPermissionGranted(PermissionStatus.ACCEPTED) + } + } + } + + + private fun checkIfPermissionGranted(onPermissionGranted: () -> Unit) { + if (ContextCompat.checkSelfPermission( + activity, Manifest.permission.WRITE_EXTERNAL_STORAGE, + ) == PackageManager.PERMISSION_GRANTED + ) { + onPermissionGranted() + } else { + requestPermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE) + } + } + + @RequiresApi(Build.VERSION_CODES.R) + private fun checkIfManagePermissionGranted(onPermissionGranted: (Boolean) -> Unit) { + if (Environment.isExternalStorageManager()) { + onPermissionGranted(true) + } else { + onPermissionGranted(false) + } + } + + +} \ No newline at end of file diff --git a/smart-storage/src/main/java/com/ss/smart_storage/SmartStorage.kt b/smart-storage/src/main/java/com/ss/smart_storage/SmartStorage.kt index 0245dc4..36ff32d 100644 --- a/smart-storage/src/main/java/com/ss/smart_storage/SmartStorage.kt +++ b/smart-storage/src/main/java/com/ss/smart_storage/SmartStorage.kt @@ -1,19 +1,21 @@ package com.ss.smart_storage -import android.Manifest import android.annotation.SuppressLint import android.app.Activity import android.content.Context import android.content.Intent -import android.content.pm.PackageManager import android.net.Uri import android.os.Build import android.os.Environment +import android.provider.Settings +import android.util.Log import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.result.contract.ActivityResultContracts -import androidx.core.content.ContextCompat +import androidx.annotation.RequiresApi import androidx.documentfile.provider.DocumentFile +import com.ss.smart_storage.model.FileDetails +import com.ss.smart_storage.util.PermissionStatus import com.ss.smart_storage.util.SmartDirectory import com.ss.smart_storage.util.SmartFileType import java.io.File @@ -21,122 +23,109 @@ import java.io.FileOutputStream import java.io.IOException -data class FileDetails( - val name: String, - val location: String, - val fileType: SmartFileType, - val fileData: ByteArray, -) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - other as FileDetails - return fileData.contentEquals(other.fileData) - } +class SmartStorage(private val activity: ComponentActivity) { - override fun hashCode(): Int { - return fileData.contentHashCode() - } -} + private var baseDocumentTreeUri: Uri? = null + private lateinit var fileDetails: FileDetails -class SmartStorage(private val activity: ComponentActivity) { + @SuppressLint("NewApi") + private val permissionManager = + PermissionManager(activity = activity, onPermissionGranted = { status -> - private var baseDocumentTreeUri: Uri? = null - private var fileDetails : FileDetails? = null + when (status) { + PermissionStatus.NOT_NEEDED -> { + callFileDetails() + } + + PermissionStatus.ACCEPTED -> { + callFileDetails() + } + PermissionStatus.DENIED -> { + Toast.makeText( + activity, + activity.getString(R.string.perm_denied), + Toast.LENGTH_SHORT + ).show() + } - private val safLauncher = activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> - if (result.resultCode == Activity.RESULT_OK) { - val uri: Uri? = result.data?.data - if (uri != null) { - baseDocumentTreeUri = uri - if(fileDetails!=null){ + PermissionStatus.NOT_APPLICABLE -> { + launchBaseDirectoryPicker() + } + + PermissionStatus.REDIRECT_TO_SETTINGS -> { + requestFullStorageAccess() + } + } + }) + + + private val safLauncher = + activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == Activity.RESULT_OK) { + val uri: Uri? = result.data?.data + if (uri != null) { + baseDocumentTreeUri = uri writeFileToDocumentTree( baseDocumentTreeUri, - fileDetails!!.name, - fileDetails!!.fileData, ) } } } - } - - private val requestPermissionLauncher = activity.registerForActivityResult( - ActivityResultContracts.RequestPermission() - ) { isGranted: Boolean -> - if (isGranted) { - callFileDetails() - } else { - Toast.makeText(activity, "Permission denied", Toast.LENGTH_SHORT).show() - } - } private fun launchBaseDirectoryPicker() { val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) safLauncher.launch(intent) } - -//todo : Remove Supress Lint - @SuppressLint("ObsoleteSdkInt") - fun isWritePermissionGranted(){ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (ContextCompat.checkSelfPermission( - activity, - Manifest.permission.WRITE_EXTERNAL_STORAGE - ) == PackageManager.PERMISSION_GRANTED - ) { - callFileDetails() - } else { - requestPermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE) - } - } else { - callFileDetails() - } - } - - private fun callFileDetails(){ - if(fileDetails != null){ - if(fileDetails!!.location == SmartDirectory.CUSTOM){ - launchBaseDirectoryPicker() - } - - else{ - storeToDirectory( - fileDetails = fileDetails!! - ) - } - } + @RequiresApi(Build.VERSION_CODES.R) + private fun requestFullStorageAccess(){ + activity.startActivity(Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)) } fun store( location: String, fileName: String? = null, fileType: SmartFileType, fileData: ByteArray ) { + val name = - if (!fileName.isNullOrEmpty()) "$fileName.$fileType" else "smartStorage.${fileType.name}" + if(fileName.isNullOrEmpty()) randomFileName(fileType.extension) else fileName.plus(fileType.extension) fileDetails = FileDetails( - name = name, - location = location, - fileType = fileType, - fileData = fileData + name = name, location = location, fileType = fileType, fileData = fileData ) - isWritePermissionGranted() + + permissionManager.checkLocation(location) } - private fun storeToDirectory(fileDetails: FileDetails){ + + private fun callFileDetails() { + storeToDirectory( + fileDetails = fileDetails + ) + } + + private fun randomFileName(extension : String): String { + val alphanumericChars = ('a'..'z') + ('A'..'Z') + ('0'..'9') + val randomString = (1..8) + .map { alphanumericChars.random() } + .joinToString("") + return randomString.plus(extension) + } + + + private fun storeToDirectory(fileDetails: FileDetails) { val directory = handleFileCreation(fileDetails.location, activity.applicationContext) - if(directory!=null && !directory.exists()){ + if (directory != null && !directory.exists()) { directory.mkdirs() } val file = File( directory, fileDetails.name ) - + Log.d("Path to directory:", directory!!.path) try { FileOutputStream(file).use { stream -> stream.write(fileDetails.fileData) @@ -146,15 +135,22 @@ class SmartStorage(private val activity: ComponentActivity) { } } - private fun writeFileToDocumentTree(baseDocumentTreeUri: Uri?, fileName: String, content: ByteArray) { + private fun writeFileToDocumentTree( + baseDocumentTreeUri: Uri? + ) { baseDocumentTreeUri?.let { treeUri -> try { val directory = DocumentFile.fromTreeUri(activity.applicationContext, treeUri) - val file = directory?.createFile("text/*", fileName) - val pfd = file?.let { activity.applicationContext.contentResolver.openFileDescriptor(it.uri, "w") } + val file = + directory?.createFile(fileDetails.fileType.mimeType, fileDetails.fileType.name) + val pfd = file?.let { + activity.applicationContext.contentResolver.openFileDescriptor( + it.uri, "w" + ) + } pfd?.use { descriptor -> val fos = FileOutputStream(descriptor.fileDescriptor) - fos.write(content) + fos.write(fileDetails.fileData) fos.close() } } catch (e: IOException) { @@ -166,11 +162,18 @@ class SmartStorage(private val activity: ComponentActivity) { private fun handleFileCreation(location: String, context: Context): File? { return when (location) { SmartDirectory.INTERNAL -> context.filesDir - SmartDirectory.EXTERNAL_APP -> context.getExternalFilesDir(null) + SmartDirectory.EXTERNAL_PUBLIC -> Environment.getExternalStoragePublicDirectory( + getPackage() + ) + + SmartDirectory.SCOPED_STORAGE -> context.getExternalFilesDir(null) else -> Environment.getExternalStoragePublicDirectory(location) } } + private fun getPackage(): String = activity.packageName.substringAfterLast('.') + + } diff --git a/smart-storage/src/main/java/com/ss/smart_storage/model/FileDetails.kt b/smart-storage/src/main/java/com/ss/smart_storage/model/FileDetails.kt new file mode 100644 index 0000000..4c81544 --- /dev/null +++ b/smart-storage/src/main/java/com/ss/smart_storage/model/FileDetails.kt @@ -0,0 +1,21 @@ +package com.ss.smart_storage.model + +import com.ss.smart_storage.util.SmartFileType + +data class FileDetails( + val name: String, + val location: String, + val fileType: SmartFileType, + val fileData: ByteArray, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + other as FileDetails + return fileData.contentEquals(other.fileData) + } + + override fun hashCode(): Int { + return fileData.contentHashCode() + } +} diff --git a/smart-storage/src/main/java/com/ss/smart_storage/util/PermissionStatus.kt b/smart-storage/src/main/java/com/ss/smart_storage/util/PermissionStatus.kt new file mode 100644 index 0000000..60b8a87 --- /dev/null +++ b/smart-storage/src/main/java/com/ss/smart_storage/util/PermissionStatus.kt @@ -0,0 +1,5 @@ +package com.ss.smart_storage.util + +enum class PermissionStatus { + ACCEPTED, DENIED, NOT_APPLICABLE, NOT_NEEDED, REDIRECT_TO_SETTINGS +} \ No newline at end of file diff --git a/smart-storage/src/main/java/com/ss/smart_storage/util/SmartDirectory.kt b/smart-storage/src/main/java/com/ss/smart_storage/util/SmartDirectory.kt index 3c587cf..fd7465b 100644 --- a/smart-storage/src/main/java/com/ss/smart_storage/util/SmartDirectory.kt +++ b/smart-storage/src/main/java/com/ss/smart_storage/util/SmartDirectory.kt @@ -1,14 +1,10 @@ package com.ss.smart_storage.util - -class SmartDirectory{ - companion object { - const val INTERNAL: String = "Internal" - const val EXTERNAL_APP : String = "External_App" - const val CUSTOM : String = "Custom" - const val DOWNLOADS: String = "Download" - const val DOCUMENTS: String = "Documents" - } -} - - +object SmartDirectory{ + const val INTERNAL: String = "Internal" + const val SCOPED_STORAGE : String = "Scoped_Storage" + const val CUSTOM : String = "Custom" + const val DOWNLOADS: String = "Download" + const val DOCUMENTS: String = "Documents" + const val EXTERNAL_PUBLIC = "External_Public" +} \ No newline at end of file diff --git a/smart-storage/src/main/java/com/ss/smart_storage/util/SmartFileType.kt b/smart-storage/src/main/java/com/ss/smart_storage/util/SmartFileType.kt index 657fbbd..78a86b6 100644 --- a/smart-storage/src/main/java/com/ss/smart_storage/util/SmartFileType.kt +++ b/smart-storage/src/main/java/com/ss/smart_storage/util/SmartFileType.kt @@ -1,9 +1,12 @@ package com.ss.smart_storage.util -enum class SmartFileType { - txt, - jpeg, - png, - pdf, - webp, -} \ No newline at end of file + +enum class SmartFileType(val extension: String, val mimeType: String) { + TXT(extension = ".txt", mimeType = "text/plain"), JPEG( + extension = ".jpeg", mimeType = "image/jpeg" + ), + PNG(extension = ".png", mimeType = "image/png"), PDF( + extension = ".pdf", mimeType = "application/pdf" + ), + WEBP(extension = ".webp", mimeType = "image/webp"), +} diff --git a/smart-storage/src/main/res/values/strings.xml b/smart-storage/src/main/res/values/strings.xml new file mode 100644 index 0000000..0e1b6de --- /dev/null +++ b/smart-storage/src/main/res/values/strings.xml @@ -0,0 +1,6 @@ + + + Permission Denied + Feature not Available + + \ No newline at end of file