Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Permission Manager created #9

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.SmartStorage">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand Down
7 changes: 5 additions & 2 deletions app/src/main/java/com/ss/smartstorage/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.ss.smartstorage

import android.os.Build
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
Expand All @@ -14,6 +16,7 @@ import com.ss.smartstorage.ui.theme.SmartStorageTheme

class MainActivity : ComponentActivity() {

@RequiresApi(Build.VERSION_CODES.R)
harshilpadsala88 marked this conversation as resolved.
Show resolved Hide resolved
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

Expand All @@ -29,8 +32,8 @@ class MainActivity : ComponentActivity() {
SmartStorageSample(
onStoreTap = {
smartStorage.store(
location = SmartDirectory.CUSTOM,
fileName = "interstellar",
location = SmartDirectory.DOWNLOADS,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Place appropriate file name and add some comments about SmartDirectory enum usage.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done as per suggestion.

fileName = "dd",
fileType = SmartFileType.txt,
fileData = "Directed by Christopher Nolan".toByteArray()
)
Expand Down
7 changes: 0 additions & 7 deletions app/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>
10 changes: 5 additions & 5 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -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"

Expand Down
116 changes: 116 additions & 0 deletions smart-storage/src/main/java/com/ss/smart_storage/PermissionManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package com.ss.smart_storage

import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import androidx.activity.ComponentActivity
import androidx.activity.result.contract.ActivityResultContracts
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) {

if (isVersionInBetween(Build.VERSION_CODES.M, Build.VERSION_CODES.Q)) {

checkIfPermissionGranted(Manifest.permission.WRITE_EXTERNAL_STORAGE,
onPermissionGranted = {
onPermissionGranted(PermissionStatus.ACCEPTED)
})
} else if (isVersionInBetween(
Build.VERSION_CODES.R, Build.VERSION_CODES.S
) && location == SmartDirectory.EXTERNAL_PUBLIC
) {
checkIfManagePermissionGranted(Manifest.permission.MANAGE_EXTERNAL_STORAGE ,
onPermissionGranted = {
granted ->
if(granted) onPermissionGranted(PermissionStatus.ACCEPTED)
else onPermissionGranted(PermissionStatus.REDIRECT_TO_SETTINGS)
})


} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && location == SmartDirectory.EXTERNAL_PUBLIC) {
onPermissionGranted(PermissionStatus.NOT_AVAILABLE)
} else {
onPermissionGranted(PermissionStatus.ACCEPTED)
}
}

private fun checkIfPermissionGranted(permission: String, onPermissionGranted: () -> Unit) {
if (ContextCompat.checkSelfPermission(
activity, permission
) == PackageManager.PERMISSION_GRANTED
) {
onPermissionGranted()
} else {
requestPermissionLauncher.launch(permission)
}
}

private fun checkIfManagePermissionGranted(permission: String, onPermissionGranted: (Boolean) -> Unit) {
if (ContextCompat.checkSelfPermission(
activity, permission
) == PackageManager.PERMISSION_GRANTED
) {
onPermissionGranted(true)
} else {
onPermissionGranted(false)
}
}


private fun isVersionInBetween(min: Int, max: Int): Boolean {
return Build.VERSION.SDK_INT in min..max
}


}
138 changes: 69 additions & 69 deletions smart-storage/src/main/java/com/ss/smart_storage/SmartStorage.kt
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
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.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.documentfile.provider.DocumentFile
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
Expand Down Expand Up @@ -43,71 +39,60 @@ class SmartStorage(private val activity: ComponentActivity) {


private var baseDocumentTreeUri: Uri? = null
private var fileDetails : FileDetails? = null


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){
writeFileToDocumentTree(
baseDocumentTreeUri,
fileDetails!!.name,
fileDetails!!.fileData,
)
}
}
}
}
private var fileDetails: FileDetails? = null

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)
}

private val permissionManager = PermissionManager(
activity = activity,
onPermissionGranted = { status ->
when (status) {
PermissionStatus.NOT_NEEDED -> {
callFileDetails()
}

//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
) {
PermissionStatus.ACCEPTED -> {
callFileDetails()
} else {
requestPermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
} else {
callFileDetails()
}
}

private fun callFileDetails(){
if(fileDetails != null){
if(fileDetails!!.location == SmartDirectory.CUSTOM){
PermissionStatus.DENIED -> {
Toast.makeText(activity, "Permission Denied", Toast.LENGTH_SHORT).show()
Copy link
Collaborator

@amitsid1408 amitsid1408 Apr 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Define string resource and use it here. Don't use hard coded strings.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hard-coded strings removed

}

PermissionStatus.NOT_APPLICABLE -> {
launchBaseDirectoryPicker()
}

else{
storeToDirectory(
fileDetails = fileDetails!!
)
PermissionStatus.NOT_AVAILABLE -> {
Toast.makeText(activity, "Feature not Available", Toast.LENGTH_SHORT).show()
}

PermissionStatus.REDIRECT_TO_SETTINGS -> {
//Redirection
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complete redirection code here

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redirect to manage all files permission in settings done.

}
}
})


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) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FileDetails null check can be handled as follows. That way we can eliminate !! usage. :

fileDetails?.let { writeFileToDocumentTree( baseDocumentTreeUri, it.name, it.fileData, ) }

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Used lateinit to prevent null checks all over.

writeFileToDocumentTree(
baseDocumentTreeUri,
fileDetails!!.name,
fileDetails!!.fileData,
)
}
}
}
}

private fun launchBaseDirectoryPicker() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
safLauncher.launch(intent)
}


Expand All @@ -118,18 +103,26 @@ class SmartStorage(private val activity: ComponentActivity) {
if (!fileName.isNullOrEmpty()) "$fileName.$fileType" else "smartStorage.${fileType.name}"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case on null file name we can use timestamp or random alphanumeric character sequence to give name to file

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done as per suggestion

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be cautious while using "!" operator in if condition. If you do have option without using it you can check your logic please do that.

Reason behind this is Unnecessary Condition Negation operation will be performed and it'll impact time complexity.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done as per suggestion


fileDetails = FileDetails(
name = name,
location = location,
fileType = fileType,
fileData = fileData
name = name, location = location, fileType = fileType, fileData = fileData
)
isWritePermissionGranted()

permissionManager.checkLocation(location)
}


private fun callFileDetails() {
if (fileDetails != null) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do null check as above mentioned in comment.

storeToDirectory(
fileDetails = fileDetails!!
)
}
}

private fun storeToDirectory(fileDetails: FileDetails){

private fun storeToDirectory(fileDetails: FileDetails) {
val directory = handleFileCreation(fileDetails.location, activity.applicationContext)

if(directory!=null && !directory.exists()){
if (directory != null && !directory.exists()) {
directory.mkdirs()
}

Expand All @@ -146,12 +139,18 @@ class SmartStorage(private val activity: ComponentActivity) {
}
}

private fun writeFileToDocumentTree(baseDocumentTreeUri: Uri?, fileName: String, content: ByteArray) {
private fun writeFileToDocumentTree(
baseDocumentTreeUri: Uri?, fileName: String, content: ByteArray
) {
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 pfd = file?.let {
activity.applicationContext.contentResolver.openFileDescriptor(
it.uri, "w"
)
}
pfd?.use { descriptor ->
val fos = FileOutputStream(descriptor.fileDescriptor)
fos.write(content)
Expand All @@ -166,7 +165,8 @@ 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("SmartStorage")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

External public directory name should be App Name which uses our library.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done as per suggestion

SmartDirectory.SCOPED_STORAGE -> context.getExternalFilesDir(null)
else -> Environment.getExternalStoragePublicDirectory(location)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.ss.smart_storage.util

enum class PermissionStatus {
ACCEPTED, DENIED, NOT_APPLICABLE, NOT_NEEDED, NOT_AVAILABLE, REDIRECT_TO_SETTINGS
}
Loading
Loading