/
BaseActivityResultContracts.kt
194 lines (173 loc) · 7.97 KB
/
BaseActivityResultContracts.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/*
* Copyright 2019 Kaushik N. Sanji
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mindorks.kaushiknsanji.instagram.demo.ui.base
import android.annotation.TargetApi
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import androidx.activity.result.contract.ActivityResultContract
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.VisibleForTesting
import com.mindorks.kaushiknsanji.instagram.demo.utils.common.putExtrasFromMap
import com.mindorks.paracamera.Camera
import java.io.Serializable
/**
* An abstract base activity call contracts that facilitates the setup of required
* activity call contracts for the app.
*
* These activity call contracts can also be used by the Fragments in the app.
*
* @author Kaushik N Sanji
*/
object BaseActivityResultContracts {
// Constant used for logs and Bundle Keys
private const val TAG = "BaseActivityResultContracts"
// Bundle Key constant for Activity Result Code
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
const val BUNDLE_KEY_RESULT_CODE = "$TAG.result.RESULT_CODE"
/**
* Returns the Result Code of an Activity Result if saved in a [Bundle].
*/
fun getResultCode(bundle: Bundle) = bundle.getInt(BUNDLE_KEY_RESULT_CODE)
/**
* An abstract [ActivityResultContract] which specifies that an Activity can be called
* with an input [Map] of Intent Extras and produce an output [Bundle].
*/
abstract class InputExtrasMapOutputBundle :
ActivityResultContract<Map<String, Serializable>, Bundle>() {
/**
* Create an intent that can be used for
* [androidx.activity.result.ActivityResultCaller.registerForActivityResult].
*
* @param context [Context] to create an [Intent].
* @param input The source [Map] containing the Intent Extras to load from.
*
* @return Returns the [Intent] with the Extras loaded from [input].
*/
override fun createIntent(context: Context, input: Map<String, Serializable>): Intent =
provideIntentWithoutInputExtras(context).putExtrasFromMap(input)
/**
* Parse and convert result obtained from
* [androidx.activity.result.ActivityResultCallback.onActivityResult] to [Bundle].
*
* @param resultCode The [Int] result code returned by the child activity
* through its setResult().
* @param intent An [Intent], which can return result data to the caller
* (various data can be attached to Intent "extras").
*
* @return [Bundle] containing the [resultCode] in the key [BUNDLE_KEY_RESULT_CODE] and
* also the [intent] extras if any.
*/
override fun parseResult(resultCode: Int, intent: Intent?): Bundle =
(intent?.extras ?: Bundle()).apply {
if (resultCode >= Activity.RESULT_OK) {
putInt(BUNDLE_KEY_RESULT_CODE, resultCode)
}
}
/**
* To be overridden by subclasses to provide an [Intent] that needs to be used to start the
* required Activity, without the Intent Extras which will be passed as part of
* [androidx.activity.result.ActivityResultLauncher.launch]
*
* @param context [Context] to create an [Intent].
*
* @return Returns the [Intent] to start the required Activity.
*/
abstract fun provideIntentWithoutInputExtras(context: Context): Intent
}
/**
* An [ActivityResultContract] to prompt the user to open a document and receive its
* contents as a `content://` [android.net.Uri] which allows to use
* [android.content.ContentResolver.openInputStream] to access its raw data.
*
* This extends [ActivityResultContracts.OpenDocument] to add the
* category [Intent.CATEGORY_OPENABLE] so that only the content that can be streamed is returned.
*
* The `input` is an [Array] of MIME types to filter by, e.g., `image/ *`.
*
* Can be extended to override [createIntent] if you wish to pass additional extras to
* the [Intent] created by `super.createIntent()`.
*/
@TargetApi(Build.VERSION_CODES.KITKAT)
open class OpenableDocument : ActivityResultContracts.OpenDocument() {
/**
* Create an intent that can be used for
* [androidx.activity.result.ActivityResultCaller.registerForActivityResult].
*
* @param context [Context] to create an [Intent].
* @param input An [Array] of MIME types to filter by.
*
* @return Returns an [Intent] that prompts the user to open an openable document.
*/
override fun createIntent(context: Context, input: Array<out String>): Intent {
return super.createIntent(context, input).apply {
// With the Intent that can open any document, filter results that can be
// streamed like files (this excludes stuff like timezones and contacts)
addCategory(Intent.CATEGORY_OPENABLE)
}
}
}
/**
* An [ActivityResultContract] to take a picture and save it into the `content://` [android.net.Uri]
* provided by the input [ParaCamera][Camera].
*
* The `input` is a [ParaCamera][Camera] instance required to setup the [Intent] for Image capture.
*
* Can be extended to override [createIntent] if you wish to pass additional extras to
* the [Intent] created by `super.createIntent()`.
*/
open class ParaCameraTakePicture : ActivityResultContract<Camera, Boolean>() {
/**
* Create an intent that can be used for
* [androidx.activity.result.ActivityResultCaller.registerForActivityResult].
*
* @param context [Context] to create an [Intent].
* @param input [Camera] instance to setup the [Intent] for Image capture.
*
* @return Returns an [Intent] with action [MediaStore.ACTION_IMAGE_CAPTURE] to launch a
* Camera app for capturing an Image.
*/
override fun createIntent(context: Context, input: Camera): Intent {
// Access the private method of ParaCamera API which initializes the Intent for Image capture
val setUpIntentMethod =
input.javaClass.getDeclaredMethod("setUpIntent", Intent::class.java).apply {
isAccessible = true
}
// Prepare and return the Intent for Image Capture
return Intent(MediaStore.ACTION_IMAGE_CAPTURE).apply {
// Setup the Intent further with the ParaCamera API
setUpIntentMethod.invoke(input, this)
}
}
/**
* Parse and convert result obtained from
* [androidx.activity.result.ActivityResultCallback.onActivityResult] to [Boolean].
*
* @param resultCode The [Int] result code returned by the child activity
* through its setResult().
* @param intent An [Intent], which can return result data to the caller
* (various data can be attached to Intent "extras").
*
* @return Returns `true` if the Image capture was successful, i.e.,
* if the [resultCode] is [Activity.RESULT_OK]; `false` otherwise.
*/
override fun parseResult(resultCode: Int, intent: Intent?): Boolean =
resultCode == Activity.RESULT_OK
}
}