1 /*
<lambda>null2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.photopicker.extensions
18 
19 import android.content.Intent
20 import android.provider.MediaStore
21 import com.android.photopicker.core.configuration.IllegalIntentExtraException
22 
23 /**
24  * Check the various possible actions the intent could be running under and extract a valid value
25  * from EXTRA_PICK_IMAGES_MAX
26  *
27  * @param default The default value to use if a suitable one cannot be found.
28  * @return a valid selection limit set on the [Intent], or the default value if the provided value
29  *   is invalid, or not set.
30  */
31 fun Intent.getPhotopickerSelectionLimitOrDefault(default: Int): Int {
32 
33     val limit =
34         if (
35             getAction() == MediaStore.ACTION_PICK_IMAGES &&
36                 getExtras()?.containsKey(MediaStore.EXTRA_PICK_IMAGES_MAX) ?: false
37         ) {
38             // ACTION_PICK_IMAGES supports EXTRA_PICK_IMAGES_MAX,
39             // so return the value from the intent
40             getIntExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, -1)
41         } else if (
42             getAction() == Intent.ACTION_GET_CONTENT &&
43                 extras?.containsKey(MediaStore.EXTRA_PICK_IMAGES_MAX) ?: false
44         ) {
45             // GET_CONTENT does not support EXTRA_PICK_IMAGES_MAX
46             throw IllegalIntentExtraException(
47                 "EXTRA_PICK_IMAGES_MAX is not allowed for ACTION_GET_CONTENT, " +
48                     "use ACTION_PICK_IMAGES instead."
49             )
50         } else {
51             // No EXTRA_PICK_IMAGES_MAX was set, return the provided default
52             default
53         }
54 
55     // Ensure the limit extracted from above is in the allowed range
56     if (limit !in 1..MediaStore.getPickImagesMaxLimit()) {
57         throw IllegalIntentExtraException(
58             "EXTRA_PICK_IMAGES_MAX not in the allowed range. Must be between 1 " +
59                 "and ${MediaStore.getPickImagesMaxLimit()}"
60         )
61     }
62 
63     return limit
64 }
65 
66 /**
67  * @return An [ArrayList] of MIME type filters derived from the intent. If no MIME type filters
68  *   should be applied, return null.
69  * @throws [IllegalIntentExtraException] if the input MIME types filters cannot be applied.
70  */
Intentnull71 fun Intent.getPhotopickerMimeTypes(): ArrayList<String>? {
72     val mimeTypes: Array<String>? = getStringArrayExtra(Intent.EXTRA_MIME_TYPES)
73     if (mimeTypes != null) {
74         if (mimeTypes.all { mimeType -> isMediaMimeType(mimeType) }) {
75             return mimeTypes.toCollection(ArrayList())
76         } else {
77             // Picker can be opened from Documents UI by the user. In this case, the intent action
78             // will be Intent.ACTION_GET_CONTENT and the mime types may contain non-media types.
79             // Don't apply any MIME type filters in this case. Otherwise, throw an exception.
80             if (!action.equals(Intent.ACTION_GET_CONTENT)) {
81                 throw IllegalIntentExtraException(
82                     "Only media MIME types can be accepted. Input MIME types: $mimeTypes"
83                 )
84             }
85         }
86     } else {
87         // Ignore the set type if it is not media type and don't apply any MIME type filters.
88         if (type != null && isMediaMimeType(type!!)) return arrayListOf(type!!)
89     }
90     return null
91 }
92 
93 /**
94  * Determines if Photopicker is capable of handling the [Intent.EXTRA_MIME_TYPES] provided to the
95  * activity in this Photopicker session launched with [android.intent.ACTION_GET_CONTENT].
96  *
97  * @return true if the list of mimetypes can be handled by Photopicker.
98  */
canHandleGetContentIntentMimeTypesnull99 fun Intent.canHandleGetContentIntentMimeTypes(): Boolean {
100     if (!hasExtra(Intent.EXTRA_MIME_TYPES)) {
101         // If the incoming type is */* then Photopicker can't handle this mimetype
102         return isMediaMimeType(getType())
103     }
104 
105     val mimeTypes = getStringArrayExtra(Intent.EXTRA_MIME_TYPES)
106     mimeTypes?.let {
107 
108         // If the list of MimeTypes is empty, nothing was explicitly set, so assume that
109         // non-media files should be displayed.
110         if (mimeTypes.size == 0) return false
111 
112         // Ensure all mimetypes in the incoming filter list are supported
113         for (mimeType in mimeTypes) {
114             if (!isMediaMimeType(mimeType)) {
115                 return false
116             }
117         }
118     }
119         // Should not be null at this point (the intent contains the extra key),
120         // but better safe than sorry.
121         ?: return false
122 
123     return true
124 }
125 
126 /**
127  * Determines if the mimeType is a media mimetype that Photopicker can support.
128  *
129  * @return Whether the mimetype is supported by Photopicker.
130  */
isMediaMimeTypenull131 private fun isMediaMimeType(mimeType: String?): Boolean {
132     return mimeType?.let { it.startsWith("image/") || it.startsWith("video/") } ?: false
133 }
134