Android's 10 and 11 Scoped Storage | DevsDay.ru

IT-блоги Android's 10 and 11 Scoped Storage

DZone Web Dev 8 апреля 2021 г. Nishi Thakkar


What Is Scoped Storage?

Working with the file system is an important part of developing any Android app. As discussed below, with Android 10, when you gave an app storage permission, it could access any file on the device.

In Android 10 and 11(API level 29/30), Google introduced the concept of Scoped Storage, which enhances the platform, giving better protection to the app and user data on external storage. Apps that run on Android 11 but target Android 10 (API level 29) can still request the requestLegacyExternalStorage attribute. After updating in Android 11 (API level 30), the system ignores the requestLegacyExternalStorage flag.

Getting Started

  • MediaStore will need either one of two permissions which are the Read_External_Storage and Write_External_Storage permissions, you should request these permissions in the app Manifest.xml file.
  • If you target Android 10 or higher, set the value of requestLegacyExternalStorage to true in your app's manifest file.
  • If your app target Android 10 or higher, set the value of requestLegacyExternalStorage to true in your app's manifest file for capturing an image from the camera:

AndroidManifest

Django
 




x
12


 
1
<manifest
2
   ......
3
   <uses-permission   
4
      android:name=
5
        "android.permission.READ_EXTERNAL_STORAGE" />   <uses-permission 
6
       android:name=
7
       "android.permission.WRITE_EXTERNAL_STORAGE" />    <application
8
      <!-- This attribute  is "false" by default on apps targeting    Android 10 or Higher -->   
9
   
10
      android:requestLegacyExternalStorage="true"
11
    </application>
12
</manifest>



Now, I’m going to explain scoped storage using the example of a captured image from the Gallery.

Pick image: Download Folder:

Django
 




xxxxxxxxxx
1
14


1
if (isDownloadsDocument(uri)) 
2
{// DownloadsProvider    val parcelFileDescriptor =  context.contentResolver.openFileDescriptor(uri, "r", null)
3
    parcelFileDescriptor?.let 
4
    {
5
        val inputStream = FileInputStream(
6
               parcelFileDescriptor.fileDescriptor)
7
        val file = File(
8
                    context.cacheDir, 
9
                    context.contentResolver.getFileName(uri))
10
        val outputStream = FileOutputStream(file)
11
        IOUtils.copyStream(inputStream, outputStream)
12
        return file.path
13
    }
14
}



|  getFileName(uri)

Django
 




xxxxxxxxxx
1
14


1
fun ContentResolver.getFileName(fileUri: Uri): String 
2
{
3
    var name = ""
4
    val returnCursor = this.query(fileUri, null, null, null, null)
5
    if (returnCursor != null)       
6
    {
7
       val nameIndex = returnCursor.getColumnIndex(
8
                        OpenableColumns.DISPLAY_NAME)
9
       returnCursor.moveToFirst()
10
       name = returnCursor.getString(nameIndex)
11
       returnCursor.close()
12
     }
13
     return URLEncoder.encode(name, "utf-8")
14
}



Pick image: Google Drive:

Django
 




xxxxxxxxxx
1


1
if (isGoogleDriveUri(uri)) 
2
{
3
     return getDriveFilePath(context, uri)
4
}



|  getDriveFilePath(context, uri) 

Django
 




xxxxxxxxxx
1
46


 
1
fun getDriveFilePath(context: Context, uri: Uri): String 
2
{
3
    val returnUri = uri
4
    val returnCursor: Cursor? = context.contentResolver.query(returnUri, null, null, null, null)     /*
5
       * Get the column indexes of the data in the Cursor,
6
       *     * move to the first row in the Cursor, get the data,
7
       *     * and display it.
8
       * */
9
    val nameIndex: Int = returnCursor!!.getColumnIndex(OpenableColumns.DISPLAY_NAME)    val sizeIndex: Int = returnCursor.getColumnIndex(OpenableColumns.SIZE)
10
        returnCursor.moveToFirst()    val name: String = (returnCursor.getString(nameIndex))
11
    val size = java.lang.Long.toString(returnCursor.getLong(sizeIndex))   val file = File(context.cacheDir,URLEncoder.encode(name,"utf-8"))
12
   try 
13
   {
14
       val inputStream:InputStream? = context.contentResolver.openInputStream(uri)
15
       val outputStream = FileOutputStream(file)
16
       val read: Int = 0
17
       val maxBufferSize: Int = 1 * 1024 * 1024
18
       val bytesAvailable: Int = inputStream!!.available()       //int bufferSize = 1024;
19
       val bufferSize: Int = Math.min(bytesAvailable, maxBufferSize)
20
       val buffers = ByteArray(bufferSize)       inputStream.use 
21
       { 
22
           inputStream: InputStream ->
23
           outputStream.use{ fileOut ->
24
                while (true) 
25
                {
26
                   val length = inputStream.read(buffers)
27
                   if (length <= 0)
28
                        break
29
                   fileOut.write(buffers, 0, length)
30
                }
31
                fileOut.flush()
32
                fileOut.close()
33
            }
34
       }
35
          
36
       Log.e("File Size", "Size " + file.length())
37
       inputStream.close()
38

          
39
       Log.e("File Path", "Path " + file.path)
40
       Log.e("File Size", "Size " + file.length())
41
   } catch (e: Exception) 
42
   {
43
      Log.e("Exception", e.message.toString())
44
   }
45
   return file.path
46
}



Pick image: Recent Folder/MediaDocument:

Django
 




xxxxxxxxxx
1
11


1
if (isMediaDocument(uri)) 
2
{
3
    val docId: String = DocumentsContract.getDocumentId(uri)
4
    val split: List<String> = docId.split(":")
5
    val type: String = split[0]
6
    var contentUri: Uri? = null
7
    val selection: String = "_id=?"
8
    val selectionArgs = arrayOf(split[1])    return copyFileToInternalStorage(
9
        context, uri, context.getString(R.string.app_name))
10
            
11
}



|  copyFileInternalStorage(context,uri,”test”)

Django
 




xxxxxxxxxx
1
43


1
private fun copyFileToInternalStorage(mContext: Context, uri: Uri, newDirName: String): String 
2
{
3
   val returnCursor: Cursor? = mContext.contentResolver.query(uri, arrayOf(OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE), null, null, null)/*
4
     * Get the column indexes of the data in the Cursor,
5
     *     * move to the first row in the Cursor, get the data,
6
     *     * and display it.
7
     * */
8
     val nameIndex = returnCursor!!.getColumnIndex(OpenableColumns.DISPLAY_NAME)
9
     val sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE)
10
        returnCursor.moveToFirst()     val name = returnCursor.getString(nameIndex)
11
     val size = java.lang.Long.toString(returnCursor.getLong(sizeIndex))val output: File
12
      if (newDirName != "") 
13
      {
14
         val dir = File(mContext.filesDir.toString()+ "/" +    newDirName)
15
         if (!dir.exists())
16
         {
17
            dir.mkdir()
18
         }
19
         output = File(mContext.filesDir.toString() 
20
          + "/" + newDirName 
21
          + "/" + URLEncoder.encode(name, "utf-8"))
22
     } else 
23
     {
24
        output = File(mContext.filesDir.toString()
25
                + "/" + URLEncoder.encode(name, "utf-8"))
26
     }
27
     try {
28
            val inputStream: InputStream? =
29
                   mContext.contentResolver.openInputStream(uri)
30
            val outputStream = FileOutputStream(output)
31
            var read = 0
32
            val bufferSize = 1024
33
            val buffers = ByteArray(bufferSize)            while (inputStream!!.read(buffers).also { read = it } 
34
                   != -1) 
35
            {
36
                outputStream.write(buffers, 0, read)
37
            }            inputStream.close()
38
            outputStream.close()
39
        } catch (e: java.lang.Exception) {
40
            Log.e("Exception", e.message!!)
41
        }
42
        return output.path
43
    }



Pick image: MediaStore (and general): 

Django
 




xxxxxxxxxx
1


1
if ("content".equals(uri.scheme, ignoreCase = true)) 
2
{
3
    return 
4
       if (isGooglePhotosUri(uri)) 
5
            uri.lastPathSegment!! 
6
       else copyFileToInternalStorage(
7
                 context,uri,                 
8
                 context.getString(R.string.app_name))
9
}



Pick image: External Storage:

Django
 




xxxxxxxxxx
1
21


1
if (isExternalStorageDocument(uri)) 
2
{// ExternalStorageProvider   val docId: String = DocumentsContract.getDocumentId(uri)
3
   val split: List<String> = docId.split(":")
4
   val type: String = split[0]
5
                
6
   // This is for checking Main Memory
7
   if ("primary".equals(type, ignoreCase = true))
8
   {
9
      if (split.size > 1) 
10
      {
11
          return context.getExternalFilesDir(null).toString() + "/"   + split[1]
12
      } 
13
      else 
14
      {
15
         return context.getExternalFilesDir(null).toString() + "/"
16
      }
17
      // This is for checking SD Card
18
   } else {
19
       return "storage" + "/" + docId.replace(":", "/")
20
   }
21
}



Here is all the providers for storage:

Django
 




xxxxxxxxxx
1
47


1
/**
2
 * @param uri The Uri to check.
3
 * @return Whether the Uri authority is ExternalStorageProvider.
4
 */
5
fun isExternalStorageDocument(uri: Uri): Boolean 
6
{
7
   return "com.android.externalstorage.documents"
8
                            .equals(uri.authority)
9
}
10

          
11
fun isGoogleDriveUri(uri: Uri): Boolean 
12
{
13
    return "com.google.android.apps.docs.storage".
14
                 equals(uri.authority) ||
15
           "com.google.android.apps.docs.storage.legacy".
16
                 equals(uri.authority)
17
}
18

          
19
/**
20
 * @param uri The Uri to check.
21
 * @return Whether the Uri authority is DownloadsProvider.
22
 */
23
fun isDownloadsDocument(uri: Uri): Boolean
24
{
25
   return "com.android.providers.downloads.documents"
26
                                .equals(uri.authority)
27
}
28

          
29
/**
30
 * @param uri The Uri to check.
31
 * @return Whether the Uri authority is MediaProvider.
32
 */
33
fun isMediaDocument(uri: Uri): Boolean 
34
{
35
    return "com.android.providers.media.documents"
36
                            .equals(uri.authority)
37
}
38

          
39
/**
40
 * @param uri The Uri to check.
41
 * @return Whether the Uri authority is Google Photos.
42
 */
43
fun isGooglePhotosUri(uri: Uri): Boolean 
44
{
45
    return "com.google.android.apps.photos.content"
46
                            .equals(uri.authority)
47
}



Here is the final code for pickup image from any location:

| getRealPathFromURI(context,uri)

Django
 




xxxxxxxxxx
1
228


1
@SuppressLint("NewApi")
2
fun getRealPathFromURI(context: Context, uri: Uri): String 
3
{
4
      val isKitKat: Boolean = 
5
           Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
6
      
7
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) 
8
{ //main if start   
9
      
10
      // DocumentProvider
11
       if (isGoogleDriveUri(uri))
12
       {
13
           return getDriveFilePath(context, uri)
14
       }
15
           
16
       else if (isExternalStorageDocument(uri)) 
17
       {// ExternalStorageProvider
18
                
19
            val docId: String = DocumentsContract.getDocumentId(uri)
20
            val split: List<String> = docId.split(":")
21
            val type: String = split[0]             // This is for checking Main Memory
22
           if ("primary".equals(type, ignoreCase = true)) 
23
           {
24
               if (split.size > 1) 
25
               {
26
                 return context.getExternalFilesDir(null).toString()
27
                           + "/" + split[1]
28
               } 
29
               else 
30
               {
31
                  return context.getExternalFilesDir(null).
32
                                           toString() + "/"
33
               }
34
            // This is for checking SD Card
35
            } else 
36
            {
37
                return "storage" + "/" + docId.replace(":", "/")
38
            }        } else if (isDownloadsDocument(uri)) 
39
        {
40
        // DownloadsProvider           val parcelFileDescriptor =
41
          context.contentResolver.openFileDescriptor(uri, "r", null)          parcelFileDescriptor?.let 
42
          {
43
             val inputStream = FileInputStream(
44
                          parcelFileDescriptor.fileDescriptor)
45
             val file = File(
46
                      context.cacheDir, 
47
                      context.contentResolver.getFileName(uri))
48
             val outputStream = FileOutputStream(file)
49
             IOUtils.copyStream(inputStream, outputStream)
50
             return file.path
51
          }
52
         }         
53
         else if (isMediaDocument(uri)) 
54
         {
55
            val docId: String =DocumentsContract.getDocumentId(uri)
56
            val split: List<String> = docId.split(":")
57
            val type: String = split[0]
58
            return copyFileToInternalStorage(context,uri,"app name")
59
           
60
          } 
61
        }//main if end
62
        else if ("content".equals(uri.scheme, ignoreCase = true)) 
63
        {
64
            // MediaStore (and general)
65
            return if (isGooglePhotosUri(uri)) uri.lastPathSegment!!   
66
                    else copyFileToInternalStorage(
67
                           context,
68
                            uri,
69
                            "your app name")
70
        } 
71
        else if ("file".equals(uri.scheme, ignoreCase = true)) {
72
            // File
73
            return uri.path!!
74
        }
75
        return null!!
76
    }
77

          
78
   fun ContentResolver.getFileName(fileUri: Uri): String 
79
   {
80
      var name = ""
81
      val returnCursor = this.query(fileUri, null, null, null, null)
82
      if (returnCursor != null) {
83
         val nameIndex =returnCursor.getColumnIndex(
84
                          OpenableColumns.DISPLAY_NAME)
85
         returnCursor.moveToFirst()
86
         name = returnCursor.getString(nameIndex)
87
          returnCursor.close()
88
       }
89
       return URLEncoder.encode(name, "utf-8")
90
    }
91

          
92
    /**
93
     * @param uri The Uri to check.
94
     * @return Whether the Uri authority is ExternalStorageProvider.
95
     */
96
    fun isExternalStorageDocument(uri: Uri): Boolean {
97
        return "com.android.externalstorage.documents".
98
                                equals(uri.authority)
99
    }
100

          
101
    fun isGoogleDriveUri(uri: Uri): Boolean {
102
        return "com.google.android.apps.docs.storage".
103
                 equals(uri.authority) 
104
             || "com.google.android.apps.docs.storage.legacy".
105
                 equals(uri.authority)
106
    }
107

          
108
    /**
109
     * @param uri The Uri to check.
110
     * @return Whether the Uri authority is DownloadsProvider.
111
     */
112
    fun isDownloadsDocument(uri: Uri): Boolean {
113
        return "com.android.providers.downloads.documents"
114
                .equals(uri.authority)
115
    }
116

          
117
    /**
118
     * @param uri The Uri to check.
119
     * @return Whether the Uri authority is MediaProvider.
120
     */
121
    fun isMediaDocument(uri: Uri): Boolean {
122
        return "com.android.providers.media.documents"
123
               .equals(uri.authority)
124
    }
125

          
126
    /**
127
     * @param uri The Uri to check.
128
     * @return Whether the Uri authority is Google Photos.
129
     */
130
    fun isGooglePhotosUri(uri: Uri): Boolean {
131
        return "com.google.android.apps.photos.content"
132
                .equals(uri.authority)
133
    }
134

          
135

          
136
    fun getDriveFilePath(context: Context, uri: Uri): String 
137
    {
138
        val returnUri = uri
139
        val returnCursor: Cursor? = context.contentResolver.query
140
                            (returnUri, null, null, null, null)
141
       
142
        val nameIndex: Int = returnCursor!!.getColumnIndex
143
                             (OpenableColumns.DISPLAY_NAME)
144
        val sizeIndex: Int = returnCursor.getColumnIndex
145
                                       (OpenableColumns.SIZE)        
146
        returnCursor.moveToFirst()        val name: String = (returnCursor.getString(nameIndex))
147
        val size = Long.toString(returnCursor.getLong(sizeIndex))
148
        val file = File(context.cacheDir, 
149
                 URLEncoder.encode(name, "utf-8"))        try {
150
            val inputStream: InputStream? = context.contentResolver.openInputStream(uri)
151
            val outputStream = FileOutputStream(file)            val read: Int = 0
152
            val maxBufferSize: Int = 1 * 1024 * 1024
153
            val bytesAvailable: Int = inputStream!!.available()
154
            //int bufferSize = 1024;
155
            val bufferSize: Int =
156
                       Math.min(bytesAvailable, maxBufferSize)
157
            val buffers = ByteArray(bufferSize)            inputStream.use { inputStream: InputStream ->
158
                outputStream.use { fileOut ->
159
                    while (true) {
160
                        val length = inputStream.read(buffers)
161
                        if (length <= 0)
162
                            break
163
                        fileOut.write(buffers, 0, length)
164
                    }
165
                    fileOut.flush()
166
                    fileOut.close()
167
                }
168
            }
169
          
170
            Log.e("File Size", "Size " + file.length())
171
            inputStream.close()            Log.e("File Path", "Path " + file.path)
172
            Log.e("File Size", "Size " + file.length())
173
        } catch (e: Exception) {
174
            Log.e("Exception", e.message.toString())
175
        }
176
        return file.path
177
    }
178

          
179
private fun copyFileToInternalStorage(mContext: Context, uri: Uri, newDirName: String): String 
180
{
181
    val returnCursor: Cursor? = mContext.contentResolver.query(
182
                               uri,
183
                              arrayOf(OpenableColumns.DISPLAY_NAME,         
184
                              OpenableColumns.SIZE),
185
                              null,
186
                              null,
187
                              null)
188

          
189
       val nameIndex = returnCursor!!.getColumnIndex
190
                       (OpenableColumns.DISPLAY_NAME)
191
       val sizeIndex = returnCursor.getColumnIndex
192
                       (OpenableColumns.SIZE)        returnCursor.moveToFirst()        val name = returnCursor.getString(nameIndex)
193
        val size = java.lang.Long.toString
194
                (returnCursor.getLong(sizeIndex))        val output: File
195
        if (newDirName != "") 
196
        {
197
           val dir =File(mContext.filesDir.toString() 
198
                    + "/" + newDirName)
199
           if (!dir.exists()) 
200
           {
201
                dir.mkdir()
202
           }
203
           output = File(
204
                mContext.filesDir.toString() 
205
                + "/" + newDirName + "/" 
206
                + URLEncoder.encode(name,"utf-8"))
207
        } 
208
       else 
209
       {
210
            output = File(mContext.filesDir.toString() 
211
                     + "/" + URLEncoder.encode(name, "utf-8"))
212
       }       try
213
       {
214
         val inputStream: InputStream?= 
215
               mContext.contentResolver.openInputStream(uri)
216
         val outputStream = FileOutputStream(output)
217
         var read = 0
218
         val bufferSize = 1024
219
         val buffers = ByteArray(bufferSize)        while (inputStream!!.read(buffers).also { read = it} != -1){
220
                outputStream.write(buffers, 0, read)
221
        }        inputStream.close()
222
        outputStream.close()
223
     } catch (e: java.lang.Exception) {
224
            Log.e("Exception", e.message!!)
225
     }
226
     return output.path
227
  }
228
}//function finish



If you have any questions or feedback regarding this post, please comment or reach out. Thanks. Happy coding!

Источник: DZone Web Dev

android app developement android app developer andriod tips/tutorials scoped storage

Читайте также


Ionic, Flutter, and React Native: When To Use Them

Разработка DZone Web Dev 11 апреля 2021 г. 9:20
In this article, we will see what Cross-Platform App Frameworks: Ionic, Flutter, and React Native are and when to use them. Each of these frameworks allows you to build an app for both the major platforms: iOS and Android. All of these frameworks are...... читать далее
comparison cross platform app development flutter app development mobile aap development ionic app development

Flutter 2.0: All About the New Updates for App Development

Разработка DZone Web Dev 11 апреля 2021 г. 8:57
Over 150,000 Flutter applications have so far been launched into the Google Play Store. The open-source app development framework has been widely accepted by the developer community. No surprises thus that Flutter 2.0 was a much-awaited launch. Is it...... читать далее
flutter flutter app development flutter framework flutter app developers flutter vs react native flutter sdk

Разработка DZone Web Dev 10 апреля 2021 г. 18:44

When connecting an iOS or Android app built using Expo to your data endpoints hosted on your localhost, you might hit a couple of roadblocks in terms of connectivity. This article covers ways to fix that issue in the subsections below. SAME Wi-Fi Tal...... читать далее

tutorial android web development mobile app development ios expo

DevOps linuxhint.com 10 апреля 2021 г. 6:11

Oculus Quest undoubtedly has a huge library of games and applications. The SideQuest is a platform where developers publish their creative content, including virtual reality games, applications, and experiences. Sideloading is a process of getting co...... читать далее

Virtual Reality

Разработка DZone Web Dev 9 апреля 2021 г. 19:38

Introduction Angular is one of the most popular JavaScript web frameworks. Angular's approach to organizing the programmer's work involves hiding the execution of various service operations in the depths of the framework, giving the developer conveni...... читать далее

open source javascript angular ui libraries angular 2 angular ui library design angular libraries open source angular

Разработка DZone Web Dev 8 апреля 2021 г. 0:46

React Native has gained immense popularity as one of the top open-source mobile app frameworks. It is created by Facebook and supports various smart devices such as Android, iOS, tvOS, macOS, Windows, and UWP. We all know React Native allows develope...... читать далее

react native app development react native application

Дизайн Юрий Ветров об интерфейсах 12 апреля 2021 г. 5:30

Дайджест собирает свежие статьи по дизайну интерфейсов, а также инструменты, паттерны, кейсы, тренды и исторические рассказы с 2009 года. Я тщательно фильтрую большой поток подписок, чтобы вы могли прокачать свои… The post Дайджест продуктового...... читать далее

Дайджест Продуктовый дизайн

Популярные темы

новости (389) ux (357) design (326) headline (263) python (226) ubuntu (218) ux-design (213) devops (205) новость (204) javascript (200) web dev (193) security (186) seo (149) tutorial (140) дайджесты вакансий от new.hr (136) working in tech (132) статьи (130) ui (126) programming (117) testing roundup (113) software testing (110) user-experience (109) дизайн (97) google (93) product-design (93) java (89) игровые проекты (85) ui-design (84) design-thinking (83) api5 (76) технологии (76) primary (76) прочее (70) windows 10 (68) движки и конструкторы игр (67) бизнес (67) php (66) bash programming (66) laravel (65) technology (64) job hunting (64) hardware (60) debian (58) css (57) linux mint (57) uncategorized (56) обучение (55) мероприятия (53) работа (52) español (51) docker (50) covid-19 (50) case-study (49) web design and applications (49) android (49) chrome (48) cloud (48) турбо-страницы (47) инструкции (46) обзоры (45) data (45) angular (44) publication (44) machine learning (44) ux-research (44) tutorials (43) навыки алисы (43) inspiration (43) home page stories (43) apple (42) web (41) art (41) networking (41) разработчики (41) mysql mariadb (40) c++ (40) powershell (40) job skills (40) kubernetes (40) kali linux (40) ios (40) virtual reality (39) google ads (39) автоматизация (38) wp (38) vue.js (37) маркетинг (37) cybersecurity (37) тестирование (36) полезное (36) productivity (36) wordpress (36) события (36) aspnet (36) arch linux (36) marketing (36) кейсы (35) centos (35) events (35) обновления в instagram (35)