├── .gitignore ├── LICENSE.txt ├── README.md ├── androidexternalfilewriter-kotlin ├── .gitignore ├── build.gradle ├── gradle.properties ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── celites │ │ └── androidexternalfilewriter_kotlin │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── celites │ │ │ └── androidexternalfilewriter_kotlin │ │ │ ├── KotlinExternalFileWriter.kt │ │ │ └── KotlinStorageAccessFileWriter.kt │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── celites │ └── androidexternalfilewriter_kotlin │ └── ExampleUnitTest.java ├── androidexternalfilewriter ├── .gitignore ├── build.gradle ├── gradle.properties ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── celites │ │ └── androidexternalfilewriter │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── celites │ │ └── androidexternalfilewriter │ │ ├── AppExternalFileWriter.java │ │ ├── ExternalFileWriterException.java │ │ └── SAFFileWriter.java │ └── res │ └── values │ └── strings.xml ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── celites │ │ └── appexternalfilewriter │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── celites │ │ └── appexternalfilewriter │ │ └── MainActivity.java │ └── res │ ├── layout │ └── activity_main.xml │ ├── menu │ ├── main.xml │ └── menu_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | ### Android ### 2 | # built application files 3 | *.apk 4 | *.ap_ 5 | 6 | # files for the dex VM 7 | *.dex 8 | 9 | # Java class files 10 | *.class 11 | 12 | # generated files 13 | bin/ 14 | gen/ 15 | 16 | # Local configuration file (sdk path, etc) 17 | local.properties 18 | 19 | # Eclipse project files 20 | .classpath 21 | .project 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Intellij project files 27 | *.iml 28 | *.ipr 29 | *.iws 30 | .idea/ 31 | 32 | 33 | 34 | /build 35 | /gradlew 36 | /gradlew.bat 37 | /gradle 38 | /.gradle -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2015 Prasham Trivedi 190 | Licensed under the Apache License, Version 2.0 (the "License"); 191 | you may not use this file except in compliance with the License. 192 | You may obtain a copy of the License at 193 | 194 | http://www.apache.org/licenses/LICENSE-2.0 195 | 196 | Unless required by applicable law or agreed to in writing, software 197 | distributed under the License is distributed on an "AS IS" BASIS, 198 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 199 | See the License for the specific language governing permissions and 200 | limitations under the License. 201 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | AndroidExternalFileWriter 2 | ========================= 3 | 4 | [![Download](https://api.bintray.com/packages/prashamtrivedi/maven/AndroidExternalFileWriter/images/download.svg)](https://bintray.com/prashamtrivedi/maven/AndroidExternalFileWriter/_latestVersion) 5 | 6 | [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-AndroidExternalFileWriter-blue.svg?style=flat)](http://android-arsenal.com/details/1/1796) 7 | 8 | A helper class to write files in external android storage, along with it's demo application. 9 | 10 | Class is separately located at https://gist.github.com/PrashamTrivedi/6121924 11 | 12 | Maven Availibility 13 | ================== 14 | This library is available in JCenter(latest version is 1.4 which is in sync at the time of writing). In your gradle file enter 15 | 16 | ```groovy 17 | compile 'com.creativeelites:androidexternalfilewriter:1.4' 18 | ``` 19 | 20 | Upcoming 21 | ========================= 22 | We are preparing for storage access framework. Please check Releases page for progress. 23 | 24 | How does it work 25 | ========================= 26 | 1. Create AppExternalFileWriter object with passing context to it. 27 | 28 | 2. Use writeDataToFile or writeDataToTimeStampedFile variants as per your wish. 29 | 30 | 3. If you want to write a data where a file name should be a time stamp use writeDataToTimeStampedFile variants. 31 | 32 | 4. If you want to create a subdirectory use suitable createSubDirectory variants. 33 | 34 | 5. If anything is wrong with external storage, like storage not mounted, corrupt, shared as mass storage, not enough space available, or even trying to create a library already created. The class will throw ExternalFileWriterException with the message stating what happened. 35 | 36 | 6. If you want to write a data in external cache memory do following. 37 | * Check the variants of all the methods where it asks for a boolean variable, if you pass true the file operation is done in external cache , otherwise it will be done in normal external memory. 38 | * If have already created a directory in cache memory get it from createDirectory method, and pass this directory to any method where a parent is required. These methods work same regardless of parent is in external memory or in cache memory. 39 | 40 | 7. Checks whether certain directory or file exists on certain location or not with help of isFileExists or isDirectoryExists variants 41 | 42 | 8. Deletes entire directory with deleteDirectory method 43 | (Note : This method only cares about removing entire directory with its subcontents, if you want to check whether directory is empty or not and use some error message, I recommend to use File.delete() method.) 44 | 45 | Description of Variants 46 | ========================= 47 | 48 | - writeDataToFile Variants 49 | - Without parent directories 50 | ```java 51 | writeDataToFile(String fileName, byte[] data,boolean inCache); 52 | writeDataToFile(String fileName, String data,boolean inCache); 53 | ``` 54 | - With parent directories 55 | ```java 56 | writeDataToFile(File parent, String fileName, byte[] data); 57 | writeDataToFile(File parent, String fileName, String data); 58 | ``` 59 | 60 | 61 | - writeDataToTimeStampedFile variants 62 | - Without parent directories 63 | ```java 64 | writeDataToTimeStampedFile(String extension, byte[] data,boolean inCache) 65 | writeDataToTimeStampedFile(String extension, String data,boolean inCache) 66 | ``` 67 | - With parent directories 68 | ```java 69 | writeDataToTimeStampedFile(String extension, byte[] data) 70 | writeDataToTimeStampedFile(String extension, String data) 71 | ``` 72 | 73 | - createSubDirectory variants 74 | 75 | - Creates subdirectory in any other directory 76 | ```java 77 | createSubDirectory(File parent, String directoryName) 78 | ``` 79 | - Creates subdirectory in application directory. 80 | ```java 81 | createSubDirectory(String directoryName,boolean inCache) 82 | ``` 83 | 84 | 85 | - isDirectoryExists variants 86 | 87 | - Checks whether directory with given name exists in AppDirectory Or Cache directory 88 | ```java 89 | isDirectoryExists(String directoryName, boolean checkInCache) 90 | ``` 91 | - Checks whether directory with given name exists in parentDirectory or not. 92 | ```java 93 | isDirectoryExists(String directoryName, File parentDirectory) 94 | ``` 95 | 96 | - isFileExists variants 97 | - Checks whether file with given name exists in AppDirectory 98 | ```java 99 | isFileExists(String fileName, boolean checkInCache) 100 | ``` 101 | - Check whether directory with given name exists in parentDirectory or not. 102 | ```java 103 | isFileExists(String fileName, File parentDirectory) 104 | ``` 105 | - Delete Directory 106 | - Deletes given directory with all its subdirectories and its files. 107 | ```java 108 | deleteDirectory(File directory) 109 | ``` 110 | 111 | 112 | Some goodies 113 | ========================= 114 | 115 | 1. ```getAppDirectory()``` : File object of created app directory 116 | 117 | 2. ```getExternalStorageDirectory()``` : File object of external storage directory 118 | 119 | 3. ```getExternalCacheDirectory()``` : File object of external cache directory 120 | 121 | # License 122 | Copyright 2015 Prasham Trivedi 123 | Licensed under the Apache License, Version 2.0 (the "License"); 124 | you may not use this file except in compliance with the License. 125 | You may obtain a copy of the License at 126 | 127 | http://www.apache.org/licenses/LICENSE-2.0 128 | 129 | Unless required by applicable law or agreed to in writing, software 130 | distributed under the License is distributed on an "AS IS" BASIS, 131 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132 | See the License for the specific language governing permissions and 133 | limitations under the License. 134 | -------------------------------------------------------------------------------- /androidexternalfilewriter-kotlin/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /androidexternalfilewriter-kotlin/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply from: 'https://raw.githubusercontent.com/PrashamTrivedi/JCenter/master/installv1.gradle' 4 | apply from: 'https://raw.githubusercontent.com/PrashamTrivedi/JCenter/master/bintrayv1.gradle' 5 | android { 6 | compileSdkVersion rootProject.ext.compileSdkVersion 7 | buildToolsVersion rootProject.ext.buildToolsVersion 8 | 9 | defaultConfig { 10 | minSdkVersion 17 11 | targetSdkVersion 26 12 | versionCode 1 13 | versionName "1.0" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | sourceSets { 22 | main.java.srcDirs += 'src/main/kotlin' 23 | } 24 | } 25 | 26 | dependencies { 27 | compile fileTree(dir: 'libs', include: ['*.jar']) 28 | testCompile 'junit:junit:4.12' 29 | compile "com.android.support:appcompat-v7:$rootProject.ext.supportLibVersion" 30 | compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 31 | compile 'com.creativeelites:kutils:0.8@aar' 32 | } 33 | -------------------------------------------------------------------------------- /androidexternalfilewriter-kotlin/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=androidexternalfilewriter-kotlin 2 | POM_ARTIFACT_ID=androidexternalfilewriter-kotlin 3 | POM_PACKAGING=aar 4 | VERSION_NAME=0.4 5 | VERSION_CODE=4 6 | GROUP=com.creativeelites 7 | POM_DESCRIPTION=Android External FileWriter 8 | POM_URL=https://github.com/PrashamTrivedi/AndroidExternalFileWriter 9 | POM_SCM_URL=https://github.com/PrashamTrivedi/AndroidExternalFileWriter.git 10 | POM_SCM_CONNECTION=scm:git@github.com:PrashamTrivedi/AndroidExternalFileWriter.git 11 | POM_SCM_DEV_CONNECTION=scm:git@github.com:PrashamTrivedi/AndroidExternalFileWriter.git 12 | POM_LICENCE_NAME=The Apache Software License, Version 2.0 13 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt 14 | POM_LICENCE_DIST=repo 15 | POM_DEVELOPER_ID=PrashamTrivedi 16 | POM_DEVELOPER_NAME=Prasham Trivedi 17 | -------------------------------------------------------------------------------- /androidexternalfilewriter-kotlin/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\android-sdks/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /androidexternalfilewriter-kotlin/src/androidTest/java/com/celites/androidexternalfilewriter_kotlin/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.celites.androidexternalfilewriter_kotlin; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /androidexternalfilewriter-kotlin/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /androidexternalfilewriter-kotlin/src/main/java/com/celites/androidexternalfilewriter_kotlin/KotlinExternalFileWriter.kt: -------------------------------------------------------------------------------- 1 | package com.celites.androidexternalfilewriter_kotlin 2 | 3 | import android.content.Context 4 | import android.os.Environment 5 | import android.os.StatFs 6 | import android.text.TextUtils 7 | import java.io.File 8 | import java.io.FileOutputStream 9 | import java.io.IOException 10 | 11 | /** 12 | * Created by Prasham on 4/11/2016. 13 | */ 14 | public class KotlinExternalFileWriter { 15 | lateinit var appDirectory: File 16 | lateinit var appCacheDirectory: File 17 | 18 | 19 | /** 20 | * Created by Prasham on 4/6/2016. 21 | */ 22 | public fun Context.createAppDirectory() { 23 | val directoryName = this.getString(this.applicationInfo.labelRes) 24 | 25 | if (isExternalStorageAvailable(false)) { 26 | 27 | appDirectory = File(Environment.getExternalStorageDirectory().toString(), directoryName) 28 | createDirectory(appDirectory) 29 | 30 | appCacheDirectory = File(this.externalCacheDir, directoryName) 31 | createDirectory(appCacheDirectory) 32 | 33 | } 34 | } 35 | 36 | private fun getAppDirectory(inCache: Boolean = false): File { 37 | return if (inCache) this.appCacheDirectory else this.appDirectory 38 | } 39 | 40 | public fun createFile(fileName: String, parent: File = appDirectory): File { 41 | if (isExternalStorageAvailable(true)) { 42 | 43 | try { 44 | 45 | if (parent.isDirectory) { 46 | 47 | val detailFile = File(parent, fileName) 48 | if (!detailFile.exists()) detailFile.createNewFile() 49 | else { 50 | val message = "File already there " 51 | throwException(message) 52 | } 53 | return detailFile 54 | } else { 55 | throwException("$parent should be a directory") 56 | } 57 | } catch (e: IOException) { 58 | e.printStackTrace() 59 | val errorMessage = "IOException $e" 60 | throwException(errorMessage) 61 | } catch (e: Exception) { 62 | e.printStackTrace() 63 | val errorMessage = "Exception $e" 64 | throwException(errorMessage) 65 | } 66 | 67 | } 68 | return File(parent, fileName) 69 | } 70 | 71 | /** 72 | * Creates subdirectory in parent directory 73 | 74 | * @param parent 75 | * * : Parent directory where directory with "directoryName" should be created 76 | * * 77 | * @param directoryName 78 | * * name of subdirectory 79 | * * 80 | * * 81 | * @return File object of created subdirectory 82 | * * 83 | * * 84 | * @throws ExternalFileWriterException 85 | * * if external storage is not available 86 | */ 87 | @Throws(ExternalFileWriterException::class) 88 | fun createSubDirectory(parent: File = appDirectory, directoryName: String): File? { 89 | return if (isExternalStorageAvailable(false)) { 90 | 91 | getAppDirectory() 92 | 93 | if (!parent.isDirectory) throwException("$parent.name Must be a directory ") 94 | 95 | val subDirectory = File(parent, directoryName) 96 | 97 | createDirectory(subDirectory) 98 | } else null 99 | } 100 | 101 | /** 102 | * Deletes given directory with all its subdirectories and its files. 103 | 104 | * @param directory 105 | * * : Directory to delete 106 | */ 107 | fun deleteDirectory(directory: File?) { 108 | if (directory != null) { 109 | if (directory.isDirectory) directory.listFiles().filterNotNull().forEach { 110 | if (it.isDirectory) deleteDirectory(it) 111 | else it.delete() 112 | } 113 | 114 | directory.delete() 115 | } 116 | // return false; 117 | } 118 | 119 | /** 120 | * Write data in file of a parent directory 121 | 122 | * @param parent 123 | * * parent directory 124 | * * 125 | * @param fileName 126 | * * desired filename 127 | * * 128 | * @param data 129 | * * data 130 | * * 131 | * * 132 | * @throws ExternalFileWriterException 133 | * * if external storage is not available or free space is 134 | * * less than size of the data 135 | */ 136 | @Throws(ExternalFileWriterException::class) 137 | fun writeDataToFile(parent: File = appDirectory, fileName: String, data: ByteArray, 138 | onFileWritten: (File?) -> Unit = {}) { 139 | if (isExternalStorageAvailable(true)) { 140 | getAppDirectory() 141 | 142 | val file = createFile(fileName, parent) 143 | 144 | writeDataToFile(file = file, data = data, onFileWritten = onFileWritten) 145 | } 146 | } 147 | 148 | /** 149 | * Write byte array to file. Will show error if given file is a directory. 150 | 151 | * @param file 152 | * * : File where data is to be written. 153 | * * 154 | * @param data 155 | * * String which you want to write a file. If size of this is 156 | * * greater than size available, it will show error. 157 | */ 158 | @Throws(ExternalFileWriterException::class) private fun writeDataToFile(file: File, 159 | data: String, 160 | onFileWritten: (File?) -> Unit = {}) { 161 | val stringBuffer = data.toByteArray() 162 | writeDataToFile(file = file, data = stringBuffer, onFileWritten = onFileWritten) 163 | } 164 | 165 | private fun getAvailableSpace(): Double { 166 | val stat = StatFs(Environment.getExternalStorageDirectory().path) 167 | return stat.availableBlocks.toDouble() * stat.blockSize.toDouble() 168 | } 169 | 170 | 171 | /** 172 | * Write byte array to file. Will show error if given file is a directory. 173 | 174 | * @param file 175 | * * : File where data is to be written. 176 | * * 177 | * @param data 178 | * * byte array which you want to write a file. If size of this is 179 | * * greater than size available, it will show error. 180 | */ 181 | @Throws(ExternalFileWriterException::class) private fun writeDataToFile(file: File?, 182 | data: ByteArray?, 183 | onFileWritten: (File?) -> Unit = {}) { 184 | if (isExternalStorageAvailable(true)) { 185 | if (file?.isDirectory == true) { 186 | throwException("$file is not a file, can not write data in it") 187 | } else { 188 | if (data != null) { 189 | val dataSize = data.size.toDouble() 190 | val remainingSize = getAvailableSpace() 191 | if (dataSize >= remainingSize) { 192 | throwException("Not enough size available") 193 | } else { 194 | try { 195 | val out = FileOutputStream(file) 196 | out.write(data) 197 | out.close() 198 | onFileWritten(file) 199 | } catch (e: IOException) { 200 | e.printStackTrace() 201 | } catch (e: Exception) { 202 | e.printStackTrace() 203 | } 204 | 205 | } 206 | } 207 | 208 | } 209 | } 210 | } 211 | 212 | 213 | /** 214 | * Checks whether directory with given name exists in AppDirectory 215 | 216 | * @param directoryName 217 | * * : Name of the directory to check. 218 | * * 219 | * * 220 | * @return true if a directory with "directoryName" exists, false otherwise 221 | */ 222 | fun isDirectoryExists(directoryName: String, checkInCache: Boolean): Boolean { 223 | val parentDirectory = if (checkInCache) appCacheDirectory else appDirectory 224 | return isDirectoryExists(directoryName, parentDirectory) 225 | } 226 | 227 | /** 228 | * Writes data to the file. The file will be created in the directory name 229 | * same as app. 230 | * 231 | * 232 | * Name of the file will be the timestamp.extension 233 | * 234 | 235 | * @param extension 236 | * * extension of the file, pass null if you don't want to have 237 | * * extension. 238 | * * 239 | * @param data 240 | * * data to write 241 | * * 242 | * @param inCache 243 | * * Pass true if you want to write data in External Cache. false if you want to write data in external directory. 244 | * * 245 | * * 246 | * @throws ExternalFileWriterException 247 | * * if external storage is not available or free space is 248 | * * less than size of the data 249 | */ 250 | @Throws(ExternalFileWriterException::class) 251 | fun writeDataToTimeStampedFile(extension: String, data: String, inCache: Boolean, 252 | onFileWritten: (File?) -> Unit = {}) { 253 | if (isExternalStorageAvailable(true)) { 254 | getAppDirectory() 255 | 256 | val fileExtension = if (TextUtils.isEmpty(extension)) "" else "." + extension 257 | val fileName = "${System.currentTimeMillis()}$fileExtension" 258 | 259 | val file = createFile(fileName, getAppDirectory(inCache)) 260 | 261 | writeDataToFile(file = file, data = data, onFileWritten = onFileWritten) 262 | } 263 | } 264 | 265 | /** 266 | * Writes data to the file. The file will be created in the directory name 267 | * same as app. 268 | * 269 | * 270 | * Name of the file will be the timestamp.extension 271 | * 272 | 273 | * @param parent 274 | * * parent directory path 275 | * * 276 | * @param extension 277 | * * extension of the file, pass null if you don't want to have 278 | * * extension. 279 | * * 280 | * @param data 281 | * * data to write 282 | * * 283 | * * 284 | * @throws ExternalFileWriterException 285 | * * if external storage is not available or free space is 286 | * * less than size of the data 287 | */ 288 | @Throws(ExternalFileWriterException::class) 289 | fun writeDataToTimeStampedFile(parent: File, extension: String, data: String, 290 | onFileWritten: (File?) -> Unit = {}) { 291 | if (isExternalStorageAvailable(true)) { 292 | getAppDirectory() 293 | 294 | val fileExtension = if (TextUtils.isEmpty(extension)) "" else "." + extension 295 | val fileName = "${System.currentTimeMillis()}$fileExtension" 296 | 297 | val file = createFile(fileName, parent) 298 | 299 | writeDataToFile(file = file, data = data, onFileWritten = onFileWritten) 300 | } 301 | } 302 | 303 | /** 304 | * Writes data to the file. The file will be created in the directory name 305 | * same as app. 306 | * 307 | * 308 | * Name of the file will be the timestamp.extension 309 | * 310 | 311 | * @param extension 312 | * * extension of the file, pass null if you don't want to have 313 | * * extension. 314 | * * 315 | * @param data 316 | * * data to write 317 | * * 318 | * * 319 | * @throws ExternalFileWriterException 320 | * * if external storage is not available or free space is 321 | * * less than size of the data 322 | */ 323 | @Throws(ExternalFileWriterException::class) 324 | fun writeDataToTimeStampedFile(extension: String, data: ByteArray, inCache: Boolean, 325 | onFileWritten: (File?) -> Unit = {}) { 326 | if (isExternalStorageAvailable(true)) { 327 | getAppDirectory() 328 | 329 | val fileExtension = if (TextUtils.isEmpty(extension)) "" else "." + extension 330 | val fileName = "${System.currentTimeMillis()}$fileExtension" 331 | 332 | val file = createFile(fileName, getAppDirectory(inCache)) 333 | 334 | writeDataToFile(file = file, data = data, onFileWritten = onFileWritten) 335 | } 336 | } 337 | 338 | /** 339 | * Writes data to the file. The file will be created in the directory name 340 | * same as app. 341 | * 342 | * 343 | * Name of the file will be the timestamp.extension 344 | * 345 | 346 | * @param parent 347 | * * parent directory path 348 | * * 349 | * @param extension 350 | * * extension of the file, pass null if you don't want to have 351 | * * extension. 352 | * * 353 | * @param data 354 | * * data to write 355 | * * 356 | * * 357 | * @throws ExternalFileWriterException 358 | * * if external storage is not available or free space is 359 | * * less than size of the data 360 | */ 361 | @Throws(ExternalFileWriterException::class) 362 | fun writeDataToTimeStampedFile(parent: File, extension: String, data: ByteArray, 363 | onFileWritten: (File?) -> Unit = {}) { 364 | if (isExternalStorageAvailable(true)) { 365 | getAppDirectory() 366 | 367 | val fileExtension = if (TextUtils.isEmpty(extension)) "" else "." + extension 368 | val fileName = "${System.currentTimeMillis()}$fileExtension" 369 | 370 | val file = createFile(fileName, parent) 371 | 372 | writeDataToFile(file = file, data = data, onFileWritten = onFileWritten) 373 | } 374 | } 375 | 376 | /** 377 | * Check whether file with given name exists in parentDirectory or not. 378 | 379 | * @param fileName 380 | * * : Name of the file to check. 381 | * * 382 | * @param parentDirectory 383 | * * : Parent directory where directory with "fileName" should be present 384 | * * 385 | * * 386 | * @return true if a file with "fileName" exists, false otherwise 387 | */ 388 | fun isFileExists(fileName: String, parentDirectory: File): Boolean { 389 | val directoryToCheck = File(parentDirectory, fileName) 390 | return directoryToCheck.exists() && directoryToCheck.isFile 391 | } 392 | 393 | /** 394 | * Checks whether file with given name exists in AppDirectory 395 | 396 | * @param fileName 397 | * * : Name of the file to check. 398 | * * 399 | * * 400 | * @return true if a file with "directoryName" exists, false otherwise 401 | */ 402 | fun isFileExists(fileName: String, checkInCache: Boolean): Boolean { 403 | val parentDirectory = if (checkInCache) appCacheDirectory else appDirectory 404 | return isFileExists(fileName, parentDirectory) 405 | } 406 | 407 | /** 408 | * Check whether directory with given name exists in parentDirectory or not. 409 | 410 | * @param directoryName 411 | * * : Name of the directory to check. 412 | * * 413 | * @param parentDirectory 414 | * * : Parent directory where directory with "directoryName" should be present 415 | * * 416 | * * 417 | * @return true if a directory with "directoryName" exists, false otherwise 418 | */ 419 | fun isDirectoryExists(directoryName: String, parentDirectory: File): Boolean { 420 | val directoryToCheck = File(parentDirectory, directoryName) 421 | return directoryToCheck.exists() && directoryToCheck.isDirectory 422 | } 423 | 424 | @Throws(ExternalFileWriterException::class) private fun createDirectory(directory: File): File { 425 | if (!directory.exists() || !directory.isDirectory) { 426 | if (directory.mkdirs()) { 427 | val messege = "directory $directory created : Path " + directory.path 428 | 429 | } else { 430 | if (directory.exists()) { 431 | if (directory.isDirectory) { 432 | val messege = "directory $directory Already exists : Path $directory.path" 433 | 434 | } else { 435 | val messege = "$directory should be a directory but found a file : Path $directory.path" 436 | throwException(messege) 437 | } 438 | 439 | } 440 | } 441 | } 442 | return directory 443 | } 444 | 445 | private val canNotWriteFile = "Can not write file: " 446 | private val canNotCreateDirectory = "Can not create directory: " 447 | @Throws(ExternalFileWriterException::class) private fun isExternalStorageAvailable( 448 | isForFile: Boolean): Boolean { 449 | val errorStarter = if (isForFile) canNotWriteFile else canNotCreateDirectory 450 | 451 | val storageState = Environment.getExternalStorageState() 452 | 453 | when (storageState) { 454 | Environment.MEDIA_MOUNTED -> return true 455 | Environment.MEDIA_BAD_REMOVAL -> throwException("${errorStarter}Media was removed before it was unmounted.") 456 | Environment.MEDIA_CHECKING -> throwException( 457 | errorStarter + "Media is present and being disk-checked, " + "Please wait and try after some time") 458 | Environment.MEDIA_MOUNTED_READ_ONLY -> throwException(errorStarter + "Presented Media is read only") 459 | Environment.MEDIA_NOFS -> throwException(errorStarter + "Blank or unsupported file media") 460 | Environment.MEDIA_SHARED -> throwException(errorStarter + "Media is shared with USB mass storage") 461 | Environment.MEDIA_REMOVED -> throwException(errorStarter + "Media is not present") 462 | Environment.MEDIA_UNMOUNTABLE -> throwException(errorStarter + "Media is present but cannot be mounted") 463 | Environment.MEDIA_UNMOUNTED -> throwException(errorStarter + "Media is present but not mounted") 464 | } 465 | 466 | return false 467 | } 468 | 469 | @Throws(ExternalFileWriterException::class) private fun throwException(errorMessege: String) { 470 | throw ExternalFileWriterException(errorMessege) 471 | } 472 | 473 | /** 474 | * Exception to report back developer about media state or storage state if 475 | * writing is not 476 | * possible 477 | */ 478 | class ExternalFileWriterException(message: String) : Exception(message) 479 | } -------------------------------------------------------------------------------- /androidexternalfilewriter-kotlin/src/main/java/com/celites/androidexternalfilewriter_kotlin/KotlinStorageAccessFileWriter.kt: -------------------------------------------------------------------------------- 1 | package com.celites.androidexternalfilewriter_kotlin 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.content.SharedPreferences 7 | import android.net.Uri 8 | import android.os.Build 9 | import android.preference.PreferenceManager 10 | import android.support.annotation.RequiresApi 11 | import android.support.v4.app.Fragment 12 | import android.support.v4.content.ContextCompat 13 | import android.support.v4.provider.DocumentFile 14 | import android.text.TextUtils 15 | import com.celites.kutils.isEmptyString 16 | import com.celites.kutils.remove 17 | import java.io.FileNotFoundException 18 | import java.io.FileOutputStream 19 | import java.io.IOException 20 | 21 | /** 22 | * Created by Prasham on 4/11/2016. 23 | */ 24 | 25 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 26 | class KotlinStorageAccessFileWriter(private val 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 isExternalDirAvailable = isExternalDirAvailable() 53 | if (isExternalDirAvailable) { 54 | createAppDirectory() 55 | } 56 | } 57 | 58 | lateinit var context: Context 59 | lateinit var appCacheDirectory: DocumentFile 60 | lateinit var appDirectory: DocumentFile 61 | lateinit var externalCacheDirectory: DocumentFile 62 | lateinit var externalParentFile: DocumentFile 63 | lateinit var preferences: SharedPreferences 64 | private val canNotCreateDirectory = "Can not create directory: " 65 | private val canNotWriteFile = "Can not write file: " 66 | 67 | 68 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) private fun initProcessWithActivity(requestCode: Int, 69 | activity: Activity) { 70 | initCacheDirs() 71 | preferences = PreferenceManager.getDefaultSharedPreferences(context) 72 | val isExternalDirAvailable = isExternalDirAvailable() 73 | if (!isExternalDirAvailable) { 74 | 75 | val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) 76 | activity.startActivityForResult(intent, requestCode) 77 | } 78 | } 79 | 80 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) private fun initProcessWithFragment(requestCode: Int, 81 | fragment: Fragment) { 82 | initCacheDirs() 83 | preferences = PreferenceManager.getDefaultSharedPreferences(context) 84 | val isExternalDirAvailable = isExternalDirAvailable() 85 | if (!isExternalDirAvailable) { 86 | val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) 87 | fragment.startActivityForResult(intent, requestCode) 88 | } 89 | } 90 | 91 | 92 | fun isExternalDirAvailable(context: Context = this.context): Boolean { 93 | initCacheDirs(context) 94 | preferences = PreferenceManager.getDefaultSharedPreferences(context) 95 | val externalDirUrl = preferences.getString(PARENT_URI_KEY, "") 96 | val isExternalDirEmpty = externalDirUrl.isEmptyString() 97 | if (!isExternalDirEmpty) { 98 | externalParentFile = DocumentFile.fromTreeUri(context, Uri.parse(externalDirUrl)) 99 | try { 100 | createAppDirectory(context) 101 | } catch (e: Exception) { 102 | preferences.remove(PARENT_URI_KEY) 103 | return false 104 | } 105 | } 106 | return !isExternalDirEmpty 107 | } 108 | 109 | 110 | private fun initCacheDirs(context: Context = this.context) { 111 | val dirs = ContextCompat.getExternalCacheDirs(context) 112 | externalCacheDirectory = if (dirs.size > 1) { 113 | val dir = dirs[1] 114 | if (dir != null) { 115 | DocumentFile.fromFile(dir) 116 | } else { 117 | DocumentFile.fromFile(dirs[0]) 118 | } 119 | } else { 120 | DocumentFile.fromFile(dirs[0]) 121 | } 122 | } 123 | 124 | 125 | /** 126 | * Creates subdirectory in parent directory 127 | 128 | * @param parentDirectory 129 | * * : Parent directory where directory with "directoryName" should be created 130 | * * 131 | * @param displayName 132 | * * name of subdirectory 133 | * * 134 | * * 135 | * @return File object of created subdirectory 136 | * * 137 | * * 138 | * @throws ExternalFileWriterException 139 | * * if external storage is not available 140 | */ 141 | fun createSubDirectory(displayName: String, parentDirectory: DocumentFile): DocumentFile { 142 | getAppDirectory() 143 | return if (isDirectoryExists(displayName, parentDirectory)) { 144 | 145 | parentDirectory.createDirectory(displayName) 146 | } else { 147 | parentDirectory.findFile(displayName) 148 | } 149 | } 150 | 151 | 152 | /** 153 | * Check whether directory with given name exists in parentDirectory or not. 154 | 155 | * @param directoryName 156 | * * : Name of the directory to check. 157 | * * 158 | * @param parentDirectory 159 | * * : Parent directory where directory with "directoryName" should be present 160 | * * 161 | * * 162 | * @return true if a directory with "directoryName" exists, false otherwise 163 | */ 164 | fun isDirectoryExists(displayName: String, parentDirectory: DocumentFile): Boolean { 165 | val file = parentDirectory.findFile(displayName) 166 | return file != null && file.isDirectory 167 | } 168 | 169 | @RequiresApi(Build.VERSION_CODES.KITKAT) 170 | fun hasPermissions(file: DocumentFile): Boolean { 171 | val persistedUriPermissions = context.contentResolver.persistedUriPermissions 172 | val filterForPermission = persistedUriPermissions.filter { it.uri == file.uri && it.isReadPermission && it.isWritePermission } 173 | return filterForPermission.isNotEmpty() 174 | } 175 | 176 | /** Creates app directory */ 177 | private fun createAppDirectory(context: Context = this.context) { 178 | val directoryName = context.getString(context.applicationInfo.labelRes) 179 | appDirectory = if (isDirectoryExists(directoryName, externalParentFile)) { 180 | externalParentFile.findFile(directoryName) 181 | } else { 182 | externalParentFile.createDirectory(directoryName) 183 | } 184 | appCacheDirectory = if (isDirectoryExists(directoryName, externalCacheDirectory)) { 185 | externalCacheDirectory.findFile(directoryName) 186 | } else { 187 | externalCacheDirectory.createDirectory(directoryName) 188 | } 189 | 190 | } 191 | 192 | /** 193 | * Creates subdirectory in application directory 194 | 195 | * @param directoryName 196 | * * name of subdirectory 197 | * * 198 | * * 199 | * @return File object of created subdirectory 200 | * * 201 | * * 202 | * @throws ExternalFileWriterException 203 | * * if external storage is not available 204 | */ 205 | fun createSubdirectory(directoryName: String, inCache: Boolean = false): DocumentFile? { 206 | getAppDirectory() 207 | val appDirectory = getAppDirectory(inCache) 208 | return if (!isDirectoryExists(directoryName, inCache)) { 209 | 210 | appDirectory.createDirectory(directoryName) 211 | } else { 212 | appDirectory.findFile(directoryName) 213 | } 214 | } 215 | 216 | fun getAppDirectory(inCache: Boolean = false): DocumentFile { 217 | 218 | return if (inCache) appCacheDirectory else appDirectory 219 | } 220 | 221 | /** 222 | * Checks whether directory with given name exists in AppDirectory 223 | 224 | * @param directoryName 225 | * * : Name of the directory to check. 226 | * * 227 | * * 228 | * @return true if a directory with "directoryName" exists, false otherwise 229 | */ 230 | fun isDirectoryExists(displayName: String, inCache: Boolean): Boolean { 231 | val file = getDocumentFile(displayName, inCache) 232 | return file != null && file.isDirectory 233 | } 234 | 235 | private fun getDocumentFile(displayName: String, inCache: Boolean): DocumentFile? { 236 | val appDirectory = getAppDirectory(inCache) 237 | return appDirectory.findFile(displayName) 238 | } 239 | 240 | fun handleResult(requestCode: Int, resultCode: Int, data: Intent?, 241 | handlingFinished: () -> Unit = {}, 242 | askForPersistableUriPermission: Boolean = true) { 243 | if (resultCode == Activity.RESULT_OK) { 244 | if (requestCode == this.requestCode) { 245 | data?.let { 246 | val treeUri = it.data 247 | if (askForPersistableUriPermission) { 248 | val takeFlags = it.flags and (Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) 249 | context.contentResolver.takePersistableUriPermission(treeUri, takeFlags) 250 | } 251 | externalParentFile = DocumentFile.fromTreeUri(context, treeUri) 252 | preferences.edit().putString(PARENT_URI_KEY, externalParentFile.uri.toString()).apply() 253 | createAppDirectory() 254 | handlingFinished() 255 | } 256 | 257 | 258 | } 259 | } 260 | } 261 | 262 | /** 263 | * Check whether file with given name exists in parentDirectory or not. 264 | 265 | * @param fileName 266 | * * : Name of the file to check. 267 | * * 268 | * @param parentDirectory 269 | * * : Parent directory where directory with "fileName" should be present 270 | * * 271 | * * 272 | * @return true if a file with "fileName" exists, false otherwise 273 | */ 274 | fun isFileExists(displayName: String, inCache: Boolean = false): Boolean { 275 | val file = getDocumentFile(displayName, inCache) 276 | return file != null && file.isFile 277 | } 278 | 279 | @Throws(FileNotFoundException::class) 280 | fun writeDataToFile(fileName: String, mimeType: String, data: ByteArray, 281 | inCache: Boolean = false) { 282 | val appDir = getAppDirectory(inCache) 283 | writeDataToFile(appDir, fileName, data, mimeType) 284 | } 285 | 286 | /** 287 | * Writes data to the file. The file will be created in the directory name same as app. 288 | 289 | * @param fileName 290 | * * name of the file 291 | * * 292 | * @param data 293 | * * data to write 294 | * * 295 | * * 296 | * @throws ExternalFileWriterException 297 | * * if external storage is not available or free space is less than size of the data 298 | */ 299 | @Throws(FileNotFoundException::class) 300 | fun writeDataToFile(parent: DocumentFile, fileName: String, data: ByteArray, mimeType: String, 301 | onFileWritten: (DocumentFile) -> Unit = {}) { 302 | val file = createFile(fileName, parent, mimeType) 303 | writeDataToFile(file = file, data = data, onFileWritten = onFileWritten) 304 | } 305 | 306 | private fun createFile(fileName: String, parent: DocumentFile, 307 | mimeType: String) = if (!isFileExists(fileName, parent)) { 308 | 309 | parent.createFile(mimeType, fileName) 310 | } else { 311 | parent.findFile(fileName) 312 | } 313 | 314 | /** 315 | * Write byte array to file. Will show error if given file is a directory. 316 | 317 | * @param file 318 | * * : File where data is to be written. 319 | * * 320 | * @param data 321 | * * byte array which you want to write a file. If size of this is greater than size available, it will show error. 322 | */ 323 | @Throws(FileNotFoundException::class) private fun writeDataToFile(file: DocumentFile, 324 | data: ByteArray, 325 | onFileWritten: (DocumentFile) -> Unit = {}) { 326 | val fileDescriptor = context.contentResolver.openFileDescriptor(file.uri, "w") 327 | val out: FileOutputStream? 328 | if (fileDescriptor != null) { 329 | out = FileOutputStream(fileDescriptor.fileDescriptor) 330 | try { 331 | out.write(data) 332 | out.close() 333 | onFileWritten(file) 334 | } catch (e: IOException) { 335 | e.printStackTrace() 336 | } 337 | 338 | } 339 | } 340 | 341 | /** 342 | * Checks whether file with given name exists in AppDirectory 343 | 344 | * @param fileName 345 | * * : Name of the file to check. 346 | * * 347 | * * 348 | * @return true if a file with "directoryName" exists, false otherwise 349 | */ 350 | fun isFileExists(displayName: String, parentDirectory: DocumentFile): Boolean { 351 | val file = parentDirectory.findFile(displayName) 352 | return file != null && file.isFile 353 | } 354 | 355 | @Throws(FileNotFoundException::class) 356 | fun writeDataToFile(fileName: String, mimeType: String, data: String, inCache: Boolean, 357 | onFileWritten: (DocumentFile) -> Unit = {}) { 358 | val appDir = getAppDirectory(inCache) 359 | writeDataToFile(parent = appDir, fileName = fileName, data = data, mimeType = mimeType, 360 | onFileWritten = onFileWritten) 361 | } 362 | 363 | /** 364 | * Write data in file of a parent directory 365 | 366 | * @param parent 367 | * * parent directory 368 | * * 369 | * @param fileName 370 | * * desired filename 371 | * * 372 | * @param data 373 | * * data 374 | * * 375 | * * 376 | * @throws ExternalFileWriterException 377 | * * if external storage is not available or free space is less than size of the data 378 | */ 379 | @Throws(FileNotFoundException::class) 380 | fun writeDataToFile(parent: DocumentFile, fileName: String, data: String, mimeType: String, 381 | onFileWritten: (DocumentFile) -> Unit = {}) { 382 | val file = createFile(fileName, parent, mimeType) 383 | writeDataToFile(file = file, data = data, onFileWritten = onFileWritten) 384 | } 385 | 386 | /** 387 | * Write byte array to file. Will show error if given file is a directory. 388 | 389 | * @param file 390 | * * : File where data is to be written. 391 | * * 392 | * @param data 393 | * * String which you want to write a file. If size of this is greater than size available, it will show error. 394 | */ 395 | @Throws(FileNotFoundException::class) private fun writeDataToFile(file: DocumentFile, 396 | data: String, 397 | onFileWritten: (DocumentFile) -> Unit = {}) { 398 | val stringBuffer = data.toByteArray() 399 | writeDataToFile(file = file, data = stringBuffer, onFileWritten = onFileWritten) 400 | } 401 | 402 | @Throws(FileNotFoundException::class) 403 | fun writeDataToTimeStampedFile(mimeType: String, data: String, extension: String, 404 | filePrefix: String = "", inCache: Boolean, 405 | onFileWritten: (DocumentFile) -> Unit = {}) { 406 | val appDir = getAppDirectory(inCache) 407 | val fileExtension = if (TextUtils.isEmpty(extension)) "" else "." + extension 408 | val fileName = "$filePrefix${System.currentTimeMillis()}$fileExtension" 409 | writeDataToFile(parent = appDir, fileName = fileName, data = data, mimeType = mimeType, 410 | onFileWritten = onFileWritten) 411 | } 412 | 413 | @Throws(FileNotFoundException::class) 414 | fun writeDataToTimeStampedFile(mimeType: String, data: ByteArray, extension: String, 415 | filePrefix: String = "", inCache: Boolean, 416 | onFileWritten: (DocumentFile) -> Unit) { 417 | val appDir = getAppDirectory(inCache) 418 | val fileExtension = if (TextUtils.isEmpty(extension)) "" else "." + extension 419 | val fileName = "$filePrefix${System.currentTimeMillis()}$fileExtension" 420 | writeDataToFile(parent = appDir, fileName = fileName, data = data, mimeType = mimeType, 421 | onFileWritten = onFileWritten) 422 | } 423 | 424 | @Throws(FileNotFoundException::class) 425 | fun writeDataToTimeStampedFile(mimeType: String, data: String, extension: String, filePrefix: String = "", parent: DocumentFile, 426 | onFileWritten: (DocumentFile) -> Unit = {}) { 427 | val fileExtension = if (TextUtils.isEmpty(extension)) "" else "." + extension 428 | val fileName = "$filePrefix${System.currentTimeMillis()}$fileExtension" 429 | writeDataToFile(parent = parent, fileName = fileName, data = data, mimeType = mimeType, 430 | onFileWritten = onFileWritten) 431 | } 432 | 433 | @Throws(FileNotFoundException::class) 434 | fun writeDataToTimeStampedFile(mimeType: String, data: ByteArray, extension: String, filePrefix: String = "", parent: DocumentFile, 435 | onFileWritten: (DocumentFile) -> Unit = {}) { 436 | val fileExtension = if (TextUtils.isEmpty(extension)) "" else "." + extension 437 | val fileName = "$filePrefix${System.currentTimeMillis()}$fileExtension" 438 | writeDataToFile(parent = parent, fileName = fileName, data = data, mimeType = mimeType, 439 | onFileWritten = onFileWritten) 440 | } 441 | 442 | private fun createFile(fileName: String, inCache: Boolean, mimeType: String): DocumentFile { 443 | return createFile(fileName, getAppDirectory(inCache), mimeType) 444 | } 445 | 446 | public fun moveFile(file: DocumentFile, destinationDir: DocumentFile): Boolean { 447 | copyFile(destinationDir, file) 448 | return file.delete() 449 | } 450 | 451 | public fun KotlinStorageAccessFileWriter.copyFile(destinationDir: DocumentFile, 452 | file: DocumentFile, 453 | onFileWritten: (DocumentFile) -> Unit = {}) { 454 | val bytesFromFile = getBytesFromFile(file) 455 | writeDataToFile(parent = destinationDir, fileName = file.name, data = bytesFromFile, 456 | mimeType = file.type, onFileWritten = onFileWritten) 457 | } 458 | 459 | public fun getBytesFromFile(file: DocumentFile): ByteArray { 460 | val inputStream = context.contentResolver.openInputStream(file.uri) 461 | return inputStream.readBytes() 462 | } 463 | } -------------------------------------------------------------------------------- /androidexternalfilewriter-kotlin/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Androidexternalfilewriter-kotlin 3 | 4 | -------------------------------------------------------------------------------- /androidexternalfilewriter-kotlin/src/test/java/com/celites/androidexternalfilewriter_kotlin/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.celites.androidexternalfilewriter_kotlin; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /androidexternalfilewriter/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /androidexternalfilewriter/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply from: 'https://raw.githubusercontent.com/PrashamTrivedi/JCenter/master/installv1.gradle' 3 | apply from: 'https://raw.githubusercontent.com/PrashamTrivedi/JCenter/master/bintrayv1.gradle' 4 | android { 5 | compileSdkVersion rootProject.ext.compileSdkVersion 6 | buildToolsVersion rootProject.ext.buildToolsVersion 7 | 8 | defaultConfig { 9 | minSdkVersion 14 10 | targetSdkVersion 26 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile "com.android.support:appcompat-v7:$rootProject.ext.supportLibVersion" 24 | compile fileTree(dir: 'libs', include: ['*.jar']) 25 | compile 'com.celites:devutils:1.0@aar' 26 | } 27 | -------------------------------------------------------------------------------- /androidexternalfilewriter/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=androidexternalfilewriter 2 | POM_ARTIFACT_ID=androidexternalfilewriter 3 | POM_PACKAGING=aar 4 | VERSION_NAME=1.5.9 5 | VERSION_CODE=7 6 | GROUP=com.creativeelites 7 | POM_DESCRIPTION=Android External FileWriter 8 | POM_URL=https://github.com/PrashamTrivedi/AndroidExternalFileWriter 9 | POM_SCM_URL=https://github.com/PrashamTrivedi/AndroidExternalFileWriter.git 10 | POM_SCM_CONNECTION=scm:git@github.com:PrashamTrivedi/AndroidExternalFileWriter.git 11 | POM_SCM_DEV_CONNECTION=scm:git@github.com:PrashamTrivedi/AndroidExternalFileWriter.git 12 | POM_LICENCE_NAME=The Apache Software License, Version 2.0 13 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt 14 | POM_LICENCE_DIST=repo 15 | POM_DEVELOPER_ID=PrashamTrivedi 16 | POM_DEVELOPER_NAME=Prasham Trivedi 17 | -------------------------------------------------------------------------------- /androidexternalfilewriter/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\android-sdks/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /androidexternalfilewriter/src/androidTest/java/com/celites/androidexternalfilewriter/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.celites.androidexternalfilewriter; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest 10 | extends ApplicationTestCase { 11 | public ApplicationTest() { 12 | super(Application.class); 13 | } 14 | } -------------------------------------------------------------------------------- /androidexternalfilewriter/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /androidexternalfilewriter/src/main/java/com/celites/androidexternalfilewriter/AppExternalFileWriter.java: -------------------------------------------------------------------------------- 1 | package com.celites.androidexternalfilewriter; 2 | 3 | import android.content.Context; 4 | import android.os.Environment; 5 | import android.os.StatFs; 6 | import android.text.TextUtils; 7 | 8 | import java.io.File; 9 | import java.io.FileOutputStream; 10 | import java.io.IOException; 11 | 12 | /** 13 | * @author Prasham Trivedi 14 | * @version 2.5 15 | *

16 | * This class will create a directory having same name as your 17 | * application. With all the states handled and reported back to 18 | * developer. 19 | *

20 | */ 21 | public class AppExternalFileWriter { 22 | 23 | private static final String canNotWriteFile = "Can not write file: "; 24 | private static final String canNotCreateDirectory = "Can not create directory: "; 25 | private final File externalStorageDirectory; 26 | private final File externalCacheDirectory; 27 | private Context context; 28 | private File appDirectory; 29 | private File appCacheDirectory; 30 | 31 | /** 32 | * Creates external file writer 33 | * 34 | * @param context 35 | * : Context 36 | */ 37 | public AppExternalFileWriter(Context context) { 38 | this.context = context; 39 | externalStorageDirectory = Environment.getExternalStorageDirectory(); 40 | externalCacheDirectory = context.getExternalCacheDir(); 41 | } 42 | 43 | private File createFile(String fileName, boolean inCache) throws ExternalFileWriterException { 44 | return createFile(fileName, getAppDirectory(inCache)); 45 | } 46 | 47 | /** 48 | * Create a file in the app directory with given file name. 49 | * 50 | * @param fileName 51 | * : Desired name of the file 52 | * @param parent 53 | * parent of the file 54 | * 55 | * @return : File with desired name 56 | */ 57 | private File createFile(String fileName, File parent) throws ExternalFileWriterException { 58 | if (isExternalStorageAvailable(true)) { 59 | try { 60 | 61 | if (parent.isDirectory()) { 62 | 63 | File detailFile = new File(parent, fileName); 64 | if (!detailFile.exists()) 65 | detailFile.createNewFile(); 66 | else { 67 | String messege = "File already there "; 68 | throwException(messege); 69 | } 70 | return detailFile; 71 | } else { 72 | throwException(parent + " should be a directory"); 73 | } 74 | } catch (IOException e) { 75 | e.printStackTrace(); 76 | String errorMessege = "IOException " + e; 77 | throwException(errorMessege); 78 | } catch (Exception e) { 79 | e.printStackTrace(); 80 | String errorMessege = "Exception " + e; 81 | throwException(errorMessege); 82 | } 83 | } 84 | return null; 85 | } 86 | 87 | /** Creates app directory */ 88 | private void createAppDirectory() throws ExternalFileWriterException { 89 | String directoryName = context.getString(context.getApplicationInfo().labelRes); 90 | 91 | if (isExternalStorageAvailable(false)) { 92 | 93 | appDirectory = new File(Environment.getExternalStorageDirectory().toString(), 94 | directoryName); 95 | createDirectory(appDirectory); 96 | 97 | appCacheDirectory = new File(externalCacheDirectory, directoryName); 98 | createDirectory(appCacheDirectory); 99 | 100 | } 101 | 102 | } 103 | 104 | private double getAvailableSpace() { 105 | StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath()); 106 | double sdAvailSize = (double) stat.getAvailableBlocks() * (double) stat.getBlockSize(); 107 | return sdAvailSize; 108 | } 109 | 110 | private boolean isExternalStorageAvailable(boolean isForFile) 111 | throws ExternalFileWriterException { 112 | String errorStarter = (isForFile) ? canNotWriteFile : canNotCreateDirectory; 113 | 114 | String storageState = Environment.getExternalStorageState(); 115 | 116 | if (storageState.equals(Environment.MEDIA_MOUNTED)) { 117 | return true; 118 | } else if (storageState.equals(Environment.MEDIA_BAD_REMOVAL)) { 119 | throwException(errorStarter + "Media was removed before it was unmounted."); 120 | } else if (storageState.equals(Environment.MEDIA_CHECKING)) { 121 | throwException(errorStarter + "Media is present and being disk-checked, " 122 | + "Please wait and try after some time"); 123 | } else if (storageState.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) { 124 | throwException(errorStarter + "Presented Media is read only"); 125 | } else if (storageState.equals(Environment.MEDIA_NOFS)) { 126 | throwException(errorStarter + "Blank or unsupported file media"); 127 | } else if (storageState.equals(Environment.MEDIA_SHARED)) { 128 | throwException(errorStarter + "Media is shared with USB mass storage"); 129 | } else if (storageState.equals(Environment.MEDIA_REMOVED)) { 130 | throwException(errorStarter + "Media is not present"); 131 | } else if (storageState.equals(Environment.MEDIA_UNMOUNTABLE)) { 132 | throwException(errorStarter + "Media is present but cannot be mounted"); 133 | } else if (storageState.equals(Environment.MEDIA_UNMOUNTED)) { 134 | throwException(errorStarter + "Media is present but not mounted"); 135 | } 136 | 137 | return false; 138 | } 139 | 140 | private void throwException(String errorMessege) throws ExternalFileWriterException { 141 | throw new ExternalFileWriterException(errorMessege); 142 | } 143 | 144 | private File createDirectory(File directory) throws ExternalFileWriterException { 145 | if (!directory.exists() || !directory.isDirectory()) { 146 | if (directory.mkdirs()) { 147 | String messege = "directory " + directory + " created : Path " 148 | + directory.getPath(); 149 | 150 | } else { 151 | if (directory.exists()) { 152 | if (directory.isDirectory()) { 153 | String messege = "directory " + directory + " Already exists : Path " 154 | + directory.getPath(); 155 | 156 | } else { 157 | String messege = directory 158 | + "should be a directory but found a file : Path " 159 | + directory.getPath(); 160 | throwException(messege); 161 | } 162 | 163 | } 164 | } 165 | } 166 | return directory; 167 | } 168 | 169 | /** 170 | * Write byte array to file. Will show error if given file is a directory. 171 | * 172 | * @param file 173 | * : File where data is to be written. 174 | * @param data 175 | * String which you want to write a file. If size of this is 176 | * greater than size available, it will show error. 177 | */ 178 | private void writeDataToFile(File file, String data) throws ExternalFileWriterException { 179 | byte[] stringBuffer = data.getBytes(); 180 | writeDataToFile(file, stringBuffer); 181 | } 182 | 183 | /** 184 | * Write byte array to file. Will show error if given file is a directory. 185 | * 186 | * @param file 187 | * : File where data is to be written. 188 | * @param data 189 | * byte array which you want to write a file. If size of this is 190 | * greater than size available, it will show error. 191 | */ 192 | private void writeDataToFile(File file, byte[] data) throws ExternalFileWriterException { 193 | if (isExternalStorageAvailable(true)) { 194 | if (file.isDirectory()) { 195 | throwException(file + " is not a file, can not write data in it"); 196 | } else { 197 | if (file != null && data != null) { 198 | double dataSize = data.length; 199 | double remainingSize = getAvailableSpace(); 200 | if (dataSize >= remainingSize) { 201 | throwException("Not enough size available"); 202 | 203 | } else { 204 | try { 205 | FileOutputStream out = new FileOutputStream(file); 206 | out.write(data); 207 | out.close(); 208 | } catch (IOException e) { 209 | e.printStackTrace(); 210 | } catch (Exception e) { 211 | e.printStackTrace(); 212 | } 213 | } 214 | } 215 | 216 | } 217 | } 218 | } 219 | 220 | private File getAppDirectory(boolean inCache) { 221 | return (inCache) ? this.appCacheDirectory : this.appDirectory; 222 | } 223 | 224 | /** 225 | * Creates subdirectory in application directory 226 | * 227 | * @param directoryName 228 | * name of subdirectory 229 | * 230 | * @return File object of created subdirectory 231 | * 232 | * @throws ExternalFileWriterException 233 | * if external storage is not available 234 | */ 235 | public File createSubDirectory(String directoryName, boolean inCache) 236 | throws ExternalFileWriterException { 237 | if (isExternalStorageAvailable(false)) { 238 | 239 | getAppDirectory(); 240 | 241 | File subDirectory = new File(getAppDirectory(inCache), directoryName); 242 | 243 | return createDirectory(subDirectory); 244 | } else 245 | return null; 246 | } 247 | 248 | /** 249 | * Checks whether directory with given name exists in AppDirectory 250 | * 251 | * @param directoryName 252 | * : Name of the directory to check. 253 | * 254 | * @return true if a directory with "directoryName" exists, false otherwise 255 | */ 256 | public boolean isDirectoryExists(String directoryName, boolean checkInCache) { 257 | File parentDirectory = (checkInCache) ? appCacheDirectory : appDirectory; 258 | return isDirectoryExists(directoryName, parentDirectory); 259 | } 260 | 261 | /** 262 | * Check whether file with given name exists in parentDirectory or not. 263 | * 264 | * @param fileName 265 | * : Name of the file to check. 266 | * @param parentDirectory 267 | * : Parent directory where directory with "fileName" should be present 268 | * 269 | * @return true if a file with "fileName" exists, false otherwise 270 | */ 271 | public boolean isFileExists(String fileName, File parentDirectory) { 272 | File directoryToCheck = new File(parentDirectory, fileName); 273 | return directoryToCheck.exists() && directoryToCheck.isFile(); 274 | } 275 | 276 | /** 277 | * Checks whether file with given name exists in AppDirectory 278 | * 279 | * @param fileName 280 | * : Name of the file to check. 281 | * 282 | * @return true if a file with "directoryName" exists, false otherwise 283 | */ 284 | public boolean isFileExists(String fileName, boolean checkInCache) { 285 | File parentDirectory = (checkInCache) ? appCacheDirectory : appDirectory; 286 | return isFileExists(fileName, parentDirectory); 287 | } 288 | 289 | /** 290 | * Check whether directory with given name exists in parentDirectory or not. 291 | * 292 | * @param directoryName 293 | * : Name of the directory to check. 294 | * @param parentDirectory 295 | * : Parent directory where directory with "directoryName" should be present 296 | * 297 | * @return true if a directory with "directoryName" exists, false otherwise 298 | */ 299 | public boolean isDirectoryExists(String directoryName, File parentDirectory) { 300 | File directoryToCheck = new File(parentDirectory, directoryName); 301 | return directoryToCheck.exists() && directoryToCheck.isDirectory(); 302 | } 303 | 304 | /** 305 | * Creates subdirectory in parent directory 306 | * 307 | * @param parent 308 | * : Parent directory where directory with "directoryName" should be created 309 | * @param directoryName 310 | * name of subdirectory 311 | * 312 | * @return File object of created subdirectory 313 | * 314 | * @throws ExternalFileWriterException 315 | * if external storage is not available 316 | */ 317 | public File createSubDirectory(File parent, String directoryName) 318 | throws ExternalFileWriterException { 319 | if (isExternalStorageAvailable(false)) { 320 | 321 | getAppDirectory(); 322 | 323 | if (!parent.isDirectory()) 324 | throwException(parent.getName() + " Must be a directory "); 325 | 326 | File subDirectory = new File(parent, directoryName); 327 | 328 | return createDirectory(subDirectory); 329 | } else 330 | return null; 331 | } 332 | 333 | /** 334 | * Deletes given directory with all its subdirectories and its files. 335 | * 336 | * @param directory 337 | * : Directory to delete 338 | */ 339 | public void deleteDirectory(File directory) { 340 | if (directory != null) { 341 | if (directory.isDirectory()) 342 | for (File child : directory.listFiles()) { 343 | 344 | if (child != null) { 345 | if (child.isDirectory()) 346 | deleteDirectory(child); 347 | else 348 | child.delete(); 349 | } 350 | } 351 | 352 | directory.delete(); 353 | } 354 | // return false; 355 | } 356 | 357 | /** 358 | * Get created app directory 359 | * 360 | * @return File object of created AppDirectory 361 | */ 362 | public File getAppDirectory() throws ExternalFileWriterException { 363 | if (appDirectory == null) { 364 | createAppDirectory(); 365 | } 366 | return appDirectory; 367 | } 368 | 369 | /** 370 | * get External Cache directory 371 | * 372 | * @return File object of External Cache directory 373 | */ 374 | public File getExternalCacheDirectory() { 375 | return externalCacheDirectory; 376 | } 377 | 378 | /** 379 | * Get external storage directory 380 | * 381 | * @return File object of external storage directory 382 | */ 383 | public File getExternalStorageDirectory() { 384 | return externalStorageDirectory; 385 | } 386 | 387 | /** 388 | * Write data in file of a parent directory 389 | * 390 | * @param parent 391 | * parent directory 392 | * @param fileName 393 | * desired filename 394 | * @param data 395 | * data 396 | * 397 | * @throws ExternalFileWriterException 398 | * if external storage is not available or free space is 399 | * less than size of the data 400 | */ 401 | public void writeDataToFile(File parent, String fileName, byte[] data) 402 | throws ExternalFileWriterException { 403 | if (isExternalStorageAvailable(true)) { 404 | getAppDirectory(); 405 | 406 | File file = createFile(fileName, parent); 407 | 408 | writeDataToFile(file, data); 409 | } 410 | } 411 | 412 | /** 413 | * Writes data to the file. The file will be created in the directory name 414 | * same as app. 415 | * 416 | * @param fileName 417 | * name of the file 418 | * @param data 419 | * data to write 420 | * 421 | * @throws ExternalFileWriterException 422 | * if external storage is not available or free space is 423 | * less than size of the data 424 | */ 425 | public void writeDataToFile(String fileName, String data, boolean inCache) 426 | throws ExternalFileWriterException { 427 | if (isExternalStorageAvailable(true)) { 428 | getAppDirectory(); 429 | 430 | File file = createFile(fileName, inCache); 431 | 432 | writeDataToFile(file, data); 433 | } 434 | } 435 | 436 | /** 437 | * Writes data to the file. The file will be created in the directory name 438 | * same as app. 439 | * 440 | * @param fileName 441 | * name of the file 442 | * @param data 443 | * data to write 444 | * 445 | * @throws ExternalFileWriterException 446 | * if external storage is not available or free space is 447 | * less than size of the data 448 | */ 449 | public void writeDataToFile(String fileName, byte[] data, boolean inCache) 450 | throws ExternalFileWriterException { 451 | if (isExternalStorageAvailable(true)) { 452 | getAppDirectory(); 453 | 454 | File file = createFile(fileName, inCache); 455 | 456 | writeDataToFile(file, data); 457 | } 458 | } 459 | 460 | /** 461 | * Write data in file of a parent directory 462 | * 463 | * @param parent 464 | * parent directory 465 | * @param fileName 466 | * desired filename 467 | * @param data 468 | * data 469 | * 470 | * @throws ExternalFileWriterException 471 | * if external storage is not available or free space is 472 | * less than size of the data 473 | */ 474 | public void writeDataToFile(File parent, String fileName, String data) 475 | throws ExternalFileWriterException { 476 | if (isExternalStorageAvailable(true)) { 477 | getAppDirectory(); 478 | 479 | File file = createFile(fileName, parent); 480 | 481 | writeDataToFile(file, data); 482 | } 483 | } 484 | 485 | /** 486 | * Writes data to the file. The file will be created in the directory name 487 | * same as app. 488 | *

489 | * Name of the file will be the timestamp.extension 490 | *

491 | * 492 | * @param extension 493 | * extension of the file, pass null if you don't want to have 494 | * extension. 495 | * @param data 496 | * data to write 497 | * @param inCache 498 | * Pass true if you want to write data in External Cache. false if you want to write data in external directory. 499 | * 500 | * @throws ExternalFileWriterException 501 | * if external storage is not available or free space is 502 | * less than size of the data 503 | */ 504 | public void writeDataToTimeStampedFile(String extension, String data, boolean inCache) 505 | throws ExternalFileWriterException { 506 | if (isExternalStorageAvailable(true)) { 507 | getAppDirectory(); 508 | 509 | String fileExtension = (TextUtils.isEmpty(extension)) ? "" : "." + extension; 510 | String fileName = System.currentTimeMillis() + fileExtension; 511 | 512 | File file = createFile(fileName, getAppDirectory(inCache)); 513 | 514 | writeDataToFile(file, data); 515 | } 516 | } 517 | 518 | /** 519 | * Writes data to the file. The file will be created in the directory name 520 | * same as app. 521 | *

522 | * Name of the file will be the timestamp.extension 523 | *

524 | * 525 | * @param parent 526 | * parent directory path 527 | * @param extension 528 | * extension of the file, pass null if you don't want to have 529 | * extension. 530 | * @param data 531 | * data to write 532 | * 533 | * @throws ExternalFileWriterException 534 | * if external storage is not available or free space is 535 | * less than size of the data 536 | */ 537 | public void writeDataToTimeStampedFile(File parent, String extension, String data) 538 | throws ExternalFileWriterException { 539 | if (isExternalStorageAvailable(true)) { 540 | getAppDirectory(); 541 | 542 | String fileExtension = (TextUtils.isEmpty(extension)) ? "" : "." + extension; 543 | String fileName = System.currentTimeMillis() + fileExtension; 544 | 545 | File file = createFile(fileName, parent); 546 | 547 | writeDataToFile(file, data); 548 | } 549 | } 550 | 551 | /** 552 | * Writes data to the file. The file will be created in the directory name 553 | * same as app. 554 | *

555 | * Name of the file will be the timestamp.extension 556 | *

557 | * 558 | * @param extension 559 | * extension of the file, pass null if you don't want to have 560 | * extension. 561 | * @param data 562 | * data to write 563 | * 564 | * @throws ExternalFileWriterException 565 | * if external storage is not available or free space is 566 | * less than size of the data 567 | */ 568 | public void writeDataToTimeStampedFile(String extension, byte[] data, boolean inCache) 569 | throws ExternalFileWriterException { 570 | if (isExternalStorageAvailable(true)) { 571 | getAppDirectory(); 572 | 573 | String fileExtension = (TextUtils.isEmpty(extension)) ? "" : "." + extension; 574 | String fileName = System.currentTimeMillis() + fileExtension; 575 | 576 | File file = createFile(fileName, getAppDirectory(inCache)); 577 | 578 | writeDataToFile(file, data); 579 | } 580 | } 581 | 582 | /** 583 | * Writes data to the file. The file will be created in the directory name 584 | * same as app. 585 | *

586 | * Name of the file will be the timestamp.extension 587 | *

588 | * 589 | * @param parent 590 | * parent directory path 591 | * @param extension 592 | * extension of the file, pass null if you don't want to have 593 | * extension. 594 | * @param data 595 | * data to write 596 | * 597 | * @throws ExternalFileWriterException 598 | * if external storage is not available or free space is 599 | * less than size of the data 600 | */ 601 | public void writeDataToTimeStampedFile(File parent, String extension, byte[] data) 602 | throws ExternalFileWriterException { 603 | if (isExternalStorageAvailable(true)) { 604 | getAppDirectory(); 605 | 606 | String fileExtension = (TextUtils.isEmpty(extension)) ? "" : "." + extension; 607 | String fileName = System.currentTimeMillis() + fileExtension; 608 | 609 | File file = createFile(fileName, parent); 610 | 611 | writeDataToFile(file, data); 612 | } 613 | } 614 | 615 | /** 616 | * Exception to report back developer about media state or storage state if 617 | * writing is not 618 | * possible 619 | */ 620 | public class ExternalFileWriterException 621 | extends Exception { 622 | 623 | public ExternalFileWriterException(String messege) { 624 | super(messege); 625 | } 626 | 627 | } 628 | } -------------------------------------------------------------------------------- /androidexternalfilewriter/src/main/java/com/celites/androidexternalfilewriter/ExternalFileWriterException.java: -------------------------------------------------------------------------------- 1 | package com.celites.androidexternalfilewriter; 2 | 3 | /** 4 | * Exception to report back developer about media state or storage state if writing is not possible 5 | */ 6 | public class ExternalFileWriterException 7 | extends Exception { 8 | 9 | public ExternalFileWriterException(String messege) { 10 | super(messege); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /androidexternalfilewriter/src/main/java/com/celites/androidexternalfilewriter/SAFFileWriter.java: -------------------------------------------------------------------------------- 1 | package com.celites.androidexternalfilewriter; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Activity; 5 | import android.content.Intent; 6 | import android.content.SharedPreferences; 7 | import android.net.Uri; 8 | import android.os.Build.VERSION_CODES; 9 | import android.os.ParcelFileDescriptor; 10 | import android.preference.PreferenceManager; 11 | import android.support.v4.content.ContextCompat; 12 | import android.support.v4.provider.DocumentFile; 13 | import android.text.TextUtils; 14 | import com.ceelites.devutils.ConstantMethods; 15 | import java.io.File; 16 | import java.io.FileNotFoundException; 17 | import java.io.FileOutputStream; 18 | import java.io.IOException; 19 | 20 | /** 21 | * Created by Prasham on 12/25/2015. 22 | */ 23 | @TargetApi(VERSION_CODES.LOLLIPOP) 24 | public class SAFFileWriter { 25 | 26 | private String PARENT_URI_KEY = "APP_EXTERNAL_PARENT_FILE_URI"; 27 | private Activity activity; 28 | private DocumentFile appCacheDirectory; 29 | private DocumentFile appDirectory; 30 | private DocumentFile externalCacheDirectory; 31 | private DocumentFile externalParentFile; 32 | private final SharedPreferences preferences; 33 | private int requestCode; 34 | private static final String canNotCreateDirectory = "Can not create directory: "; 35 | private static final String canNotWriteFile = "Can not write file: "; 36 | 37 | 38 | /** 39 | * Inits new SAFFileWriter object, it will first check whether we already have a parent directory with proper uri access or not. 40 | * 41 | * @param activity: 42 | * Activity for context and starting request for OPEN_DOCUMENT_TREE 43 | * @param requestCode: 44 | * Request code to listen to OPEN_DOCUMENT_TREE 45 | */ 46 | public SAFFileWriter(Activity activity, int requestCode) { 47 | this.activity = activity; 48 | this.requestCode = requestCode; 49 | File[] dirs = ContextCompat.getExternalCacheDirs(activity); 50 | if (dirs.length > 1) { 51 | File dir = dirs[1]; 52 | if (dir != null) { 53 | externalCacheDirectory = DocumentFile.fromFile(dir); 54 | } else { 55 | externalCacheDirectory = DocumentFile.fromFile(dirs[0]); 56 | } 57 | } else { 58 | externalCacheDirectory = DocumentFile.fromFile(dirs[0]); 59 | } 60 | preferences = PreferenceManager.getDefaultSharedPreferences(activity); 61 | String externalDirUrl = preferences.getString(PARENT_URI_KEY, ""); 62 | if (ConstantMethods.isEmptyString(externalDirUrl)) { 63 | Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); 64 | activity.startActivityForResult(intent, requestCode); 65 | } 66 | } 67 | 68 | /** 69 | * Creates subdirectory in parent directory 70 | * 71 | * @param parentDirectory 72 | * : Parent directory where directory with "directoryName" should be created 73 | * @param displayName 74 | * name of subdirectory 75 | * 76 | * @return File object of created subdirectory 77 | * 78 | * @throws ExternalFileWriterException 79 | * if external storage is not available 80 | */ 81 | public DocumentFile createSubDirectory(String displayName, DocumentFile parentDirectory) { 82 | getAppDirectory(); 83 | if (isDirectoryExists(displayName, parentDirectory)) { 84 | 85 | return parentDirectory.createDirectory(displayName); 86 | } else { 87 | return parentDirectory.findFile(displayName); 88 | } 89 | } 90 | 91 | /** 92 | * Get created app directory 93 | * 94 | * @return File object of created AppDirectory 95 | */ 96 | public DocumentFile getAppDirectory() { 97 | if (appDirectory == null) { 98 | createAppDirectory(); 99 | } 100 | return appDirectory; 101 | } 102 | 103 | /** 104 | * Check whether directory with given name exists in parentDirectory or not. 105 | * 106 | * @param directoryName 107 | * : Name of the directory to check. 108 | * @param parentDirectory 109 | * : Parent directory where directory with "directoryName" should be present 110 | * 111 | * @return true if a directory with "directoryName" exists, false otherwise 112 | */ 113 | public boolean isDirectoryExists(String displayName, DocumentFile parentDirectory) { 114 | DocumentFile file = parentDirectory.findFile(displayName); 115 | return file != null && file.isDirectory(); 116 | } 117 | 118 | /** Creates app directory */ 119 | private void createAppDirectory() { 120 | String directoryName = activity.getString(activity.getApplicationInfo().labelRes); 121 | appDirectory = externalParentFile.createDirectory(directoryName); 122 | appCacheDirectory = externalCacheDirectory.createDirectory(directoryName); 123 | } 124 | 125 | /** 126 | * Creates subdirectory in application directory 127 | * 128 | * @param directoryName 129 | * name of subdirectory 130 | * 131 | * @return File object of created subdirectory 132 | * 133 | * @throws ExternalFileWriterException 134 | * if external storage is not available 135 | */ 136 | public DocumentFile createSubdirectory(String directoryName, boolean inCache) { 137 | getAppDirectory(); 138 | DocumentFile appDirectory = getAppDirectory(inCache); 139 | if (!isDirectoryExists(directoryName, inCache)) { 140 | 141 | return appDirectory.createDirectory(directoryName); 142 | } else { 143 | return appDirectory.findFile(directoryName); 144 | } 145 | } 146 | 147 | public DocumentFile getAppDirectory(boolean inCache) { 148 | return (inCache) ? this.appCacheDirectory : this.appDirectory; 149 | } 150 | 151 | /** 152 | * Checks whether directory with given name exists in AppDirectory 153 | * 154 | * @param directoryName 155 | * : Name of the directory to check. 156 | * 157 | * @return true if a directory with "directoryName" exists, false otherwise 158 | */ 159 | public boolean isDirectoryExists(String displayName, boolean inCache) { 160 | DocumentFile file = getDocumentFile(displayName, inCache); 161 | return file != null && file.isDirectory(); 162 | } 163 | 164 | private DocumentFile getDocumentFile(String displayName, boolean inCache) { 165 | DocumentFile appDirectory = getAppDirectory(inCache); 166 | return appDirectory.findFile(displayName); 167 | } 168 | 169 | public void handleResult(int requestCode, int resultCode, Intent data) { 170 | if (resultCode == Activity.RESULT_OK) { 171 | if (requestCode == this.requestCode) { 172 | Uri treeUri = data.getData(); 173 | externalParentFile = DocumentFile.fromTreeUri(activity, treeUri); 174 | preferences.edit().putString(PARENT_URI_KEY, String.valueOf(externalParentFile.getUri())); 175 | getAppDirectory(); 176 | 177 | } 178 | } 179 | } 180 | 181 | /** 182 | * Check whether file with given name exists in parentDirectory or not. 183 | * 184 | * @param fileName 185 | * : Name of the file to check. 186 | * @param parentDirectory 187 | * : Parent directory where directory with "fileName" should be present 188 | * 189 | * @return true if a file with "fileName" exists, false otherwise 190 | */ 191 | public boolean isFileExists(String displayName, boolean inCache) { 192 | DocumentFile file = getDocumentFile(displayName, inCache); 193 | return file != null && file.isFile(); 194 | } 195 | 196 | public void writeDataToFile(String fileName, String mimeType, byte[] data, boolean inCache) throws FileNotFoundException { 197 | getAppDirectory(); 198 | DocumentFile appDir = getAppDirectory(inCache); 199 | writeDataToFile(appDir, fileName, data, mimeType); 200 | } 201 | 202 | /** 203 | * Writes data to the file. The file will be created in the directory name same as app. 204 | * 205 | * @param fileName 206 | * name of the file 207 | * @param data 208 | * data to write 209 | * 210 | * @throws ExternalFileWriterException 211 | * if external storage is not available or free space is less than size of the data 212 | */ 213 | public void writeDataToFile(DocumentFile parent, String fileName, byte[] data, String mimeType) throws FileNotFoundException { 214 | DocumentFile file = createFile(fileName, parent, mimeType); 215 | writeDataToFile(file, data); 216 | } 217 | 218 | private DocumentFile createFile(String fileName, DocumentFile parent, String mimeType) { 219 | if (!isFileExists(fileName, parent)) { 220 | 221 | return parent.createFile(mimeType, fileName); 222 | } else { 223 | return parent.findFile(fileName); 224 | } 225 | } 226 | 227 | /** 228 | * Write byte array to file. Will show error if given file is a directory. 229 | * 230 | * @param file 231 | * : File where data is to be written. 232 | * @param data 233 | * byte array which you want to write a file. If size of this is greater than size available, it will show error. 234 | */ 235 | private void writeDataToFile(DocumentFile file, byte[] data) throws FileNotFoundException { 236 | ParcelFileDescriptor fileDescriptor = activity.getContentResolver().openFileDescriptor(file.getUri(), "w"); 237 | FileOutputStream out = null; 238 | if (fileDescriptor != null) { 239 | out = new FileOutputStream(fileDescriptor.getFileDescriptor()); 240 | try { 241 | out.write(data); 242 | out.close(); 243 | } catch (IOException e) { 244 | e.printStackTrace(); 245 | } 246 | } 247 | } 248 | 249 | /** 250 | * Checks whether file with given name exists in AppDirectory 251 | * 252 | * @param fileName 253 | * : Name of the file to check. 254 | * 255 | * @return true if a file with "directoryName" exists, false otherwise 256 | */ 257 | public boolean isFileExists(String displayName, DocumentFile parentDirectory) { 258 | DocumentFile file = parentDirectory.findFile(displayName); 259 | return file != null && file.isFile(); 260 | } 261 | 262 | public void writeDataToFile(String fileName, String mimeType, String data, boolean inCache) throws FileNotFoundException { 263 | DocumentFile appDir = getAppDirectory(inCache); 264 | writeDataToFile(appDir, fileName, data, mimeType); 265 | } 266 | 267 | /** 268 | * Write data in file of a parent directory 269 | * 270 | * @param parent 271 | * parent directory 272 | * @param fileName 273 | * desired filename 274 | * @param data 275 | * data 276 | * 277 | * @throws ExternalFileWriterException 278 | * if external storage is not available or free space is less than size of the data 279 | */ 280 | public void writeDataToFile(DocumentFile parent, String fileName, String data, String mimeType) throws FileNotFoundException { 281 | DocumentFile file = createFile(fileName, parent, mimeType); 282 | writeDataToFile(file, data); 283 | } 284 | 285 | /** 286 | * Write byte array to file. Will show error if given file is a directory. 287 | * 288 | * @param file 289 | * : File where data is to be written. 290 | * @param data 291 | * String which you want to write a file. If size of this is greater than size available, it will show error. 292 | */ 293 | private void writeDataToFile(DocumentFile file, String data) throws FileNotFoundException { 294 | byte[] stringBuffer = data.getBytes(); 295 | writeDataToFile(file, stringBuffer); 296 | } 297 | 298 | public void writeDataToTimeStampedFile(String mimeType, String data, String extension, boolean inCache) throws FileNotFoundException { 299 | DocumentFile appDir = getAppDirectory(inCache); 300 | String fileExtension = (TextUtils.isEmpty(extension)) ? "" : "." + extension; 301 | String fileName = System.currentTimeMillis() + fileExtension; 302 | writeDataToFile(appDir, fileName, data, mimeType); 303 | } 304 | 305 | public void writeDataToTimeStampedFile(String mimeType, byte[] data, String extension, boolean inCache) throws FileNotFoundException { 306 | DocumentFile appDir = getAppDirectory(inCache); 307 | String fileExtension = (TextUtils.isEmpty(extension)) ? "" : "." + extension; 308 | String fileName = System.currentTimeMillis() + fileExtension; 309 | writeDataToFile(appDir, fileName, data, mimeType); 310 | } 311 | 312 | public void writeDataToTimeStampedFile(String mimeType, String data, String extension, boolean inCache, DocumentFile parent) throws 313 | FileNotFoundException { 314 | String fileExtension = (TextUtils.isEmpty(extension)) ? "" : "." + extension; 315 | String fileName = System.currentTimeMillis() + fileExtension; 316 | writeDataToFile(parent, fileName, data, mimeType); 317 | } 318 | 319 | public void writeDataToTimeStampedFile(String mimeType, byte[] data, String extension, boolean inCache, DocumentFile parent) throws 320 | FileNotFoundException { 321 | String fileExtension = (TextUtils.isEmpty(extension)) ? "" : "." + extension; 322 | String fileName = System.currentTimeMillis() + fileExtension; 323 | writeDataToFile(parent, fileName, data, mimeType); 324 | } 325 | 326 | private DocumentFile createFile(String fileName, boolean inCache, String mimeType) { 327 | return createFile(fileName, getAppDirectory(inCache), mimeType); 328 | } 329 | 330 | } 331 | -------------------------------------------------------------------------------- /androidexternalfilewriter/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AndroidExternalFileWriter 3 | 4 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.compileSdkVersion 5 | buildToolsVersion rootProject.ext.buildToolsVersion 6 | 7 | defaultConfig { 8 | applicationId "com.celites.appexternalfilewriter" 9 | minSdkVersion 17 10 | targetSdkVersion 25 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile 'com.creativeelites:androidexternalfilewriter-kotlin:0.2' 25 | compile "com.android.support:appcompat-v7:$rootProject.ext.supportLibVersion" 26 | // compile (':androidexternalfilewriter') 27 | compile project(':androidexternalfilewriter') 28 | } 29 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\android-sdks/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/celites/appexternalfilewriter/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.celites.appexternalfilewriter; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest 10 | extends ApplicationTestCase { 11 | public ApplicationTest() { 12 | super(Application.class); 13 | } 14 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/celites/appexternalfilewriter/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.celites.appexternalfilewriter; 2 | 3 | import android.content.Intent; 4 | import android.os.Build.VERSION; 5 | import android.os.Build.VERSION_CODES; 6 | import android.os.Bundle; 7 | import android.support.v4.provider.DocumentFile; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.view.Menu; 10 | import android.view.View; 11 | import android.widget.ToggleButton; 12 | import com.celites.androidexternalfilewriter.AppExternalFileWriter; 13 | import com.celites.androidexternalfilewriter.SAFFileWriter; 14 | import java.io.File; 15 | import java.io.FileNotFoundException; 16 | 17 | public class MainActivity 18 | extends AppCompatActivity { 19 | 20 | private ToggleButton inCache; 21 | private SAFFileWriter safFileWriter; 22 | private DocumentFile testDirectory; 23 | private File testFolder; 24 | private AppExternalFileWriter writer; 25 | private static final int REQUEST_FILE_ACCESS = 2488; 26 | 27 | public void createSubFolderInAppFolder(View v) { 28 | if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { 29 | testDirectory = safFileWriter.createSubdirectory("test", inCache.isChecked()); 30 | } else { 31 | try { 32 | testFolder = writer.createSubDirectory("test", inCache.isChecked()); 33 | } catch (Exception e) { 34 | e.printStackTrace(); 35 | } 36 | } 37 | } 38 | 39 | @Override 40 | public boolean onCreateOptionsMenu(Menu menu) { 41 | // Inflate the menu; this adds items to the action bar if it is present. 42 | getMenuInflater().inflate(R.menu.main, menu); 43 | return true; 44 | } 45 | 46 | public void writeDataIntoSubFolder(View v) { 47 | if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { 48 | testDirectory = safFileWriter.createSubdirectory("test", inCache.isChecked()); 49 | try { 50 | safFileWriter.writeDataToFile(testDirectory, "testFile", getString(R.string.loremipsum), "file/txt"); 51 | } catch (FileNotFoundException e) { 52 | e.printStackTrace(); 53 | } 54 | } else { 55 | try { 56 | if (testFolder == null) { 57 | testFolder = writer.createSubDirectory("test", inCache.isChecked()); 58 | } 59 | writer.writeDataToFile(testFolder, "testFile", getString(R.string.loremipsum)); 60 | } catch (Exception e) { 61 | e.printStackTrace(); 62 | } 63 | } 64 | } 65 | 66 | public void writeDataIntoTestFile(View v) { 67 | if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { 68 | try { 69 | safFileWriter.writeDataToFile("testFile", "file/txt", getString(R.string.loremipsum), inCache.isChecked()); 70 | } catch (FileNotFoundException e) { 71 | e.printStackTrace(); 72 | } 73 | } else { 74 | try { 75 | writer.writeDataToFile("testFile", getString(R.string.loremipsum), inCache.isChecked()); 76 | } catch (Exception e) { 77 | e.printStackTrace(); 78 | } 79 | } 80 | } 81 | 82 | public void writeTimeStampedFile(View v) { 83 | if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { 84 | try { 85 | safFileWriter.writeDataToTimeStampedFile("file/txt", getString(R.string.loremipsum), "txt", inCache.isChecked()); 86 | } catch (FileNotFoundException e) { 87 | e.printStackTrace(); 88 | } 89 | } else { 90 | try { 91 | writer.writeDataToTimeStampedFile("txt", getString(R.string.loremipsum), inCache.isChecked()); 92 | } catch (Exception e) { 93 | e.printStackTrace(); 94 | } 95 | } 96 | } 97 | 98 | @Override 99 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 100 | super.onActivityResult(requestCode, resultCode, data); 101 | // writer.handleResult(requestCode, resultCode, data); 102 | if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { 103 | safFileWriter.handleResult(requestCode, resultCode, data); 104 | } 105 | } 106 | 107 | @Override 108 | protected void onCreate(Bundle savedInstanceState) { 109 | super.onCreate(savedInstanceState); 110 | setContentView(R.layout.activity_main); 111 | writer = new AppExternalFileWriter(this); 112 | if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { 113 | safFileWriter = new SAFFileWriter(this, REQUEST_FILE_ACCESS); 114 | } 115 | inCache = (ToggleButton) findViewById(R.id.toggleButton); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | 14 |