11package com.celites.androidexternalfilewriter_kotlin
22
33import android.app.Activity
4+ import android.content.Context
45import android.content.Intent
56import android.content.SharedPreferences
7+ import android.net.Uri
8+ import android.os.Build
69import android.preference.PreferenceManager
10+ import android.support.annotation.RequiresApi
11+ import android.support.v4.app.Fragment
712import android.support.v4.content.ContextCompat
813import android.support.v4.provider.DocumentFile
914import android.text.TextUtils
1015import com.celites.kutils.isEmptyString
16+ import com.celites.kutils.remove
1117import java.io.FileNotFoundException
1218import java.io.FileOutputStream
1319import java.io.IOException
1420
1521/* *
1622 * Created by Prasham on 4/11/2016.
1723 */
18- public class KotlinStorageAccessFileWriter (activity : Activity , requestCode : Int ) {
1924
20- private val PARENT_URI_KEY = " APP_EXTERNAL_PARENT_FILE_URI"
21- lateinit var activity: Activity
25+ @RequiresApi(Build .VERSION_CODES .LOLLIPOP )
26+ public class KotlinStorageAccessFileWriter (requestCode : Int ) {
27+
28+
29+ public val PARENT_URI_KEY = " APP_EXTERNAL_PARENT_FILE_URI"
30+ var activity: Activity ? = null
31+ set(value) {
32+ value?.let {
33+ field = value
34+ context = value
35+ initProcessWithActivity(requestCode, value)
36+ }
37+
38+ }
39+ var fragment: Fragment ? = null
40+ set(value) {
41+ value?.let {
42+ field = value
43+ context = value.context as Context
44+ initProcessWithFragment(requestCode, value)
45+ }
46+
47+ }
48+
49+
50+ public fun startWithContext (context : Context ) {
51+ this .context = context
52+ val isExternaDirAvailable = isExternalDirAvailable()
53+ if (isExternaDirAvailable) {
54+ createAppDirectory()
55+ }
56+ }
57+
58+ lateinit var context: Context
2259 lateinit var appCacheDirectory: DocumentFile
2360 lateinit var appDirectory: DocumentFile
2461 lateinit var externalCacheDirectory: DocumentFile
@@ -28,17 +65,57 @@ public class KotlinStorageAccessFileWriter(activity: Activity, requestCode: Int)
2865 private val canNotCreateDirectory = " Can not create directory: "
2966 private val canNotWriteFile = " Can not write file: "
3067
31- /* *
32- * Inits new SAFFileWriter object, it will first check whether we already have a parent directory with proper uri access or not.
3368
34- * @param activity:
35- * * Activity for context and starting request for OPEN_DOCUMENT_TREE
36- * *
37- * @param requestCode:
38- * * Request code to listen to OPEN_DOCUMENT_TREE
39- */
40- init {
41- val dirs = ContextCompat .getExternalCacheDirs(activity)
69+ @RequiresApi(Build .VERSION_CODES .LOLLIPOP )
70+ private fun initProcessWithActivity (requestCode : Int , activity : Activity ) {
71+ initCacheDirs()
72+ preferences = PreferenceManager .getDefaultSharedPreferences(context)
73+ val isExternaDirAvailable = isExternalDirAvailable()
74+ if (! isExternaDirAvailable) {
75+
76+ val intent = Intent (Intent .ACTION_OPEN_DOCUMENT_TREE )
77+ activity.startActivityForResult(intent, requestCode)
78+ }
79+ }
80+
81+ @RequiresApi(Build .VERSION_CODES .LOLLIPOP )
82+ private fun initProcessWithFragment (requestCode : Int , fragment : Fragment ) {
83+ initCacheDirs()
84+ preferences = PreferenceManager .getDefaultSharedPreferences(context)
85+ val isExternaDirAvailable = isExternalDirAvailable()
86+ if (! isExternaDirAvailable) {
87+ val intent = Intent (Intent .ACTION_OPEN_DOCUMENT_TREE )
88+ fragment.startActivityForResult(intent, requestCode)
89+ }
90+ }
91+
92+ public fun checkIfExternalDirAvailable (context : Context ): Boolean {
93+ initCacheDirs()
94+ preferences = PreferenceManager .getDefaultSharedPreferences(context)
95+ val isExternaDirAvailable = isExternalDirAvailable()
96+ return isExternaDirAvailable
97+ }
98+
99+ fun isExternalDirAvailable (): Boolean {
100+ initCacheDirs()
101+ preferences = PreferenceManager .getDefaultSharedPreferences(context)
102+ val externalDirUrl = preferences.getString(PARENT_URI_KEY , " " )
103+ val isExternalDirEmpty = externalDirUrl.isEmptyString()
104+ if (! isExternalDirEmpty) {
105+ externalParentFile = DocumentFile .fromTreeUri(context, Uri .parse(externalDirUrl))
106+ try {
107+ createAppDirectory()
108+ } catch (e: Exception ) {
109+ preferences.remove(PARENT_URI_KEY )
110+ return false
111+ }
112+ }
113+ return ! isExternalDirEmpty
114+ }
115+
116+
117+ private fun initCacheDirs () {
118+ val dirs = ContextCompat .getExternalCacheDirs(context)
42119 if (dirs.size > 1 ) {
43120 val dir = dirs[1 ]
44121 if (dir != null ) {
@@ -49,12 +126,6 @@ public class KotlinStorageAccessFileWriter(activity: Activity, requestCode: Int)
49126 } else {
50127 externalCacheDirectory = DocumentFile .fromFile(dirs[0 ])
51128 }
52- preferences = PreferenceManager .getDefaultSharedPreferences(activity)
53- val externalDirUrl = preferences.getString(PARENT_URI_KEY , " " )
54- if (externalDirUrl.isEmptyString()) {
55- val intent = Intent (Intent .ACTION_OPEN_DOCUMENT_TREE )
56- activity.startActivityForResult(intent, requestCode)
57- }
58129 }
59130
60131
@@ -102,11 +173,28 @@ public class KotlinStorageAccessFileWriter(activity: Activity, requestCode: Int)
102173 return file != null && file.isDirectory
103174 }
104175
176+ @RequiresApi(Build .VERSION_CODES .KITKAT )
177+ fun hasPermissions (file : DocumentFile ): Boolean {
178+ val persistedUriPermissions = context.getContentResolver().persistedUriPermissions
179+ val filterForPermission = persistedUriPermissions.filter { it.uri == file.uri && it.isReadPermission && it.isWritePermission }
180+ val hasPermissions = filterForPermission.isNotEmpty()
181+ return hasPermissions
182+ }
183+
105184 /* * Creates app directory */
106185 private fun createAppDirectory () {
107- val directoryName = activity.getString(activity.applicationInfo.labelRes)
108- appDirectory = externalParentFile?.createDirectory(directoryName)
109- appCacheDirectory = externalCacheDirectory?.createDirectory(directoryName)
186+ val directoryName = context.getString(context.applicationInfo.labelRes)
187+ if (isDirectoryExists(directoryName, externalParentFile)) {
188+ appDirectory = externalParentFile.findFile(directoryName)
189+ } else {
190+ appDirectory = externalParentFile.createDirectory(directoryName)
191+ }
192+ if (isDirectoryExists(directoryName, externalCacheDirectory)) {
193+ appCacheDirectory = externalCacheDirectory.findFile(directoryName)
194+ } else {
195+ appCacheDirectory = externalCacheDirectory.createDirectory(directoryName)
196+ }
197+
110198 }
111199
112200 /* *
@@ -122,18 +210,19 @@ public class KotlinStorageAccessFileWriter(activity: Activity, requestCode: Int)
122210 * @throws ExternalFileWriterException
123211 * * if external storage is not available
124212 */
125- fun createSubdirectory (directoryName : String , inCache : Boolean ): DocumentFile ? {
213+ fun createSubdirectory (directoryName : String , inCache : Boolean = false ): DocumentFile ? {
126214 getAppDirectory()
127215 val appDirectory = getAppDirectory(inCache)
128216 if (! isDirectoryExists(directoryName, inCache)) {
129217
130- return appDirectory? .createDirectory(directoryName)
218+ return appDirectory.createDirectory(directoryName)
131219 } else {
132- return appDirectory? .findFile(directoryName)
220+ return appDirectory.findFile(directoryName)
133221 }
134222 }
135223
136224 fun getAppDirectory (inCache : Boolean = false): DocumentFile {
225+
137226 return if (inCache) appCacheDirectory else appDirectory
138227 }
139228
@@ -153,16 +242,24 @@ public class KotlinStorageAccessFileWriter(activity: Activity, requestCode: Int)
153242
154243 private fun getDocumentFile (displayName : String , inCache : Boolean ): DocumentFile ? {
155244 val appDirectory = getAppDirectory(inCache)
156- return appDirectory? .findFile(displayName)
245+ return appDirectory.findFile(displayName)
157246 }
158247
159- fun handleResult (requestCode : Int , resultCode : Int , data : Intent ) {
248+ fun handleResult (requestCode : Int , resultCode : Int , data : Intent ? , handlingFinished : () -> Unit = {}, askForPersistableUriPermission : Boolean = true ) {
160249 if (resultCode == Activity .RESULT_OK ) {
161250 if (requestCode == this .requestCode) {
162- val treeUri = data.data
163- externalParentFile = DocumentFile .fromTreeUri(activity, treeUri)
164- preferences.edit().putString(PARENT_URI_KEY , externalParentFile?.getUri().toString())
165- getAppDirectory()
251+ data?.let {
252+ val treeUri = it.data
253+ if (askForPersistableUriPermission) {
254+ val takeFlags = it.flags and (Intent .FLAG_GRANT_READ_URI_PERMISSION or Intent .FLAG_GRANT_WRITE_URI_PERMISSION )
255+ context.contentResolver.takePersistableUriPermission(treeUri, takeFlags)
256+ }
257+ externalParentFile = DocumentFile .fromTreeUri(context, treeUri)
258+ preferences.edit().putString(PARENT_URI_KEY , externalParentFile.getUri().toString()).apply ()
259+ createAppDirectory()
260+ handlingFinished()
261+ }
262+
166263
167264 }
168265 }
@@ -231,8 +328,8 @@ public class KotlinStorageAccessFileWriter(activity: Activity, requestCode: Int)
231328 */
232329 @Throws(FileNotFoundException ::class )
233330 private fun writeDataToFile (file : DocumentFile , data : ByteArray ) {
234- val fileDescriptor = activity .contentResolver.openFileDescriptor(file.uri, " w" )
235- var out : FileOutputStream ? = null
331+ val fileDescriptor = context .contentResolver.openFileDescriptor(file.uri, " w" )
332+ val out : FileOutputStream ?
236333 if (fileDescriptor != null ) {
237334 out = FileOutputStream (fileDescriptor.fileDescriptor)
238335 try {
@@ -336,4 +433,18 @@ public class KotlinStorageAccessFileWriter(activity: Activity, requestCode: Int)
336433 return createFile(fileName, getAppDirectory(inCache), mimeType)
337434 }
338435
436+ public fun moveFile (file : DocumentFile , destinationDir : DocumentFile ): Boolean {
437+ copyFile(destinationDir, file)
438+ return file.delete()
439+ }
440+
441+ public fun KotlinStorageAccessFileWriter.copyFile (destinationDir : DocumentFile , file : DocumentFile ) {
442+ val bytesFromFile = getBytesFromFile(file)
443+ writeDataToFile(destinationDir, file.name, bytesFromFile, file.type)
444+ }
445+
446+ public fun getBytesFromFile (file : DocumentFile ): ByteArray {
447+ val inputStream = context.contentResolver.openInputStream(file.uri)
448+ return inputStream.readBytes()
449+ }
339450}
0 commit comments