Skip to content

Commit 5235444

Browse files
authored
Merge pull request #55 from PatilShreyas/fix-54
[#54] Add registration method variant for Fragment without `ActivityResultRegistry`
2 parents 50d0727 + ae38183 commit 5235444

File tree

10 files changed

+242
-3
lines changed

10 files changed

+242
-3
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
<activity android:name=".ui.composePermission.ComposePermissionActivity"/>
3131
<activity android:name=".ui.multipermission.MultiPermissionActivity"/>
3232
<activity android:name=".ui.contacts.ContactsActivity"/>
33+
<activity android:name=".ui.fragment.ExampleFragmentActivity"/>
3334
</application>
3435

3536
</manifest>

app/src/main/java/dev/shreyaspatil/permissionFlow/example/ui/MainActivity.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import androidx.appcompat.app.AppCompatActivity
2222
import dev.shreyaspatil.permissionFlow.example.databinding.ActivityMainBinding
2323
import dev.shreyaspatil.permissionFlow.example.ui.composePermission.ComposePermissionActivity
2424
import dev.shreyaspatil.permissionFlow.example.ui.contacts.ContactsActivity
25+
import dev.shreyaspatil.permissionFlow.example.ui.fragment.ExampleFragmentActivity
2526
import dev.shreyaspatil.permissionFlow.example.ui.multipermission.MultiPermissionActivity
2627

2728
class MainActivity : AppCompatActivity() {
@@ -36,6 +37,7 @@ class MainActivity : AppCompatActivity() {
3637
buttonContacts.setOnClickListener { launchScreen<ContactsActivity>() }
3738
buttonMultiPermission.setOnClickListener { launchScreen<MultiPermissionActivity>() }
3839
buttonComposeSample.setOnClickListener { launchScreen<ComposePermissionActivity>() }
40+
buttonFragmentSample.setOnClickListener { launchScreen<ExampleFragmentActivity>() }
3941
}
4042
}
4143

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/**
2+
* Copyright 2022 Shreyas Patil
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+
package dev.shreyaspatil.permissionFlow.example.ui.fragment
17+
18+
import android.os.Bundle
19+
import android.view.LayoutInflater
20+
import android.view.View
21+
import android.view.ViewGroup
22+
import androidx.core.view.isVisible
23+
import androidx.fragment.app.Fragment
24+
import androidx.lifecycle.lifecycleScope
25+
import dev.shreyaspatil.permissionFlow.PermissionFlow
26+
import dev.shreyaspatil.permissionFlow.example.databinding.ViewFragmentExampleBinding
27+
import dev.shreyaspatil.permissionFlow.utils.registerForPermissionFlowRequestsResult
28+
import kotlinx.coroutines.launch
29+
30+
class ExampleFragment : Fragment() {
31+
32+
private var _binding: ViewFragmentExampleBinding? = null
33+
private val binding: ViewFragmentExampleBinding get() = _binding!!
34+
35+
private val permissions = arrayOf(
36+
android.Manifest.permission.CAMERA,
37+
android.Manifest.permission.READ_CALL_LOG,
38+
android.Manifest.permission.READ_CONTACTS,
39+
android.Manifest.permission.READ_PHONE_STATE,
40+
)
41+
42+
private val permissionFlow = PermissionFlow.getInstance()
43+
44+
private val permissionLauncher = registerForPermissionFlowRequestsResult()
45+
46+
override fun onCreateView(
47+
inflater: LayoutInflater,
48+
container: ViewGroup?,
49+
savedInstanceState: Bundle?,
50+
): View {
51+
_binding = ViewFragmentExampleBinding.inflate(inflater, null, false)
52+
return binding.root
53+
}
54+
55+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
56+
super.onViewCreated(view, savedInstanceState)
57+
initView()
58+
observePermissions()
59+
}
60+
61+
private fun initView() {
62+
binding.button.setOnClickListener {
63+
permissionLauncher.launch(permissions)
64+
}
65+
}
66+
67+
private fun observePermissions() {
68+
viewLifecycleOwner.lifecycleScope.launch {
69+
permissionFlow.getMultiplePermissionState(*permissions).collect {
70+
val grantedPermissionsText = it.grantedPermissions.joinToString(
71+
separator = "\n",
72+
prefix = "Granted Permissions:\n",
73+
)
74+
val deniedPermissionsText = it.deniedPermissions.joinToString(
75+
separator = "\n",
76+
prefix = "Denied Permissions:\n",
77+
)
78+
79+
binding.grantedPermissionsText.text = grantedPermissionsText
80+
binding.deniedPermissionsText.text = deniedPermissionsText
81+
82+
binding.button.isVisible = !it.allGranted
83+
}
84+
}
85+
}
86+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* Copyright 2022 Shreyas Patil
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+
package dev.shreyaspatil.permissionFlow.example.ui.fragment
17+
18+
import android.os.Bundle
19+
import android.widget.FrameLayout
20+
import androidx.appcompat.app.AppCompatActivity
21+
import dev.shreyaspatil.permissionFlow.example.R
22+
23+
class ExampleFragmentActivity : AppCompatActivity() {
24+
override fun onCreate(savedInstanceState: Bundle?) {
25+
super.onCreate(savedInstanceState)
26+
setContentView(R.layout.activity_fragment_example)
27+
findViewById<FrameLayout>(R.id.frameLayout).let { frameLayout ->
28+
supportFragmentManager
29+
.beginTransaction()
30+
.replace(frameLayout.id, ExampleFragment())
31+
.commit()
32+
}
33+
}
34+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:layout_width="match_parent"
4+
android:layout_height="match_parent">
5+
6+
<FrameLayout
7+
android:id="@+id/frameLayout"
8+
android:layout_width="match_parent"
9+
android:layout_height="match_parent"/>
10+
</androidx.constraintlayout.widget.ConstraintLayout>

app/src/main/res/layout/activity_main.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,11 @@
2626
android:layout_height="wrap_content"
2727
android:text="Observing permissions in Compose"
2828
tools:ignore="HardcodedText" />
29+
30+
<Button
31+
android:id="@+id/buttonFragmentSample"
32+
android:layout_width="wrap_content"
33+
android:layout_height="wrap_content"
34+
android:text="Fragment Example"
35+
tools:ignore="HardcodedText" />
2936
</LinearLayout>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
android:layout_width="match_parent"
5+
android:layout_height="match_parent"
6+
android:padding="16dp">
7+
8+
<Button
9+
android:id="@+id/button"
10+
android:layout_width="match_parent"
11+
android:layout_height="wrap_content"
12+
android:layout_marginStart="16dp"
13+
android:layout_marginTop="16dp"
14+
android:layout_marginEnd="16dp"
15+
android:text="Request Permissions"
16+
app:layout_constraintEnd_toEndOf="parent"
17+
app:layout_constraintStart_toStartOf="parent"
18+
app:layout_constraintTop_toTopOf="parent" />
19+
20+
<TextView
21+
android:id="@+id/grantedPermissionsText"
22+
android:layout_width="match_parent"
23+
android:layout_height="wrap_content"
24+
android:layout_marginStart="16dp"
25+
android:layout_marginTop="16dp"
26+
android:layout_marginEnd="16dp"
27+
android:text="Granted Permissions:"
28+
android:textColor="#009688"
29+
app:layout_constraintEnd_toEndOf="parent"
30+
app:layout_constraintStart_toStartOf="parent"
31+
app:layout_constraintTop_toBottomOf="@+id/button" />
32+
33+
<TextView
34+
android:id="@+id/deniedPermissionsText"
35+
android:layout_width="match_parent"
36+
android:layout_height="wrap_content"
37+
android:layout_marginStart="16dp"
38+
android:layout_marginTop="16dp"
39+
android:layout_marginEnd="16dp"
40+
android:text="Denied Permissions:"
41+
android:textColor="#E91E63"
42+
app:layout_constraintEnd_toEndOf="parent"
43+
app:layout_constraintStart_toStartOf="parent"
44+
app:layout_constraintTop_toBottomOf="@+id/grantedPermissionsText" />
45+
46+
</androidx.constraintlayout.widget.ConstraintLayout>

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ RELEASE_SIGNING_ENABLED=true
1212
SONATYPE_AUTOMATIC_RELEASE=true
1313

1414
GROUP=dev.shreyaspatil.permission-flow
15-
VERSION_NAME=1.0.0-rc01
15+
VERSION_NAME=1.1.1
1616

1717
POM_INCEPTION_YEAR=2022
1818
POM_URL=https://github.com/PatilShreyas/permission-flow-android/

permission-flow/src/main/java/dev/shreyaspatil/permissionFlow/utils/PermissionResultLauncher.kt

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,35 @@ fun ComponentActivity.registerForPermissionFlowRequestsResult(
5757
callback,
5858
)
5959

60+
/**
61+
* Returns a [ActivityResultLauncher] for this Fragment which internally notifies [PermissionFlow]
62+
* about the state change whenever permission state is changed with this launcher.
63+
*
64+
* Usage:
65+
*
66+
* ```
67+
* class MyFragment: Fragment() {
68+
* private val permissionLauncher = registerForPermissionFlowRequestsResult()
69+
*
70+
* fun askContactPermission() {
71+
* permissionLauncher.launch(android.Manifest.permission.READ_CONTACTS)
72+
* }
73+
* }
74+
* ```
75+
*
76+
* @param requestPermissionsContract A contract specifying permission request and result.
77+
* registry.
78+
* @param callback Callback of a permission state change.
79+
*/
80+
@JvmOverloads
81+
fun Fragment.registerForPermissionFlowRequestsResult(
82+
requestPermissionsContract: RequestPermissionsContract = RequestPermissionsContract(),
83+
callback: ActivityResultCallback<Map<String, Boolean>> = emptyCallback(),
84+
): ActivityResultLauncher<Array<String>> = registerForActivityResult(
85+
requestPermissionsContract,
86+
callback,
87+
)
88+
6089
/**
6190
* Returns a [ActivityResultLauncher] for this Fragment which internally notifies [PermissionFlow]
6291
* about the state change whenever permission state is changed with this launcher.
@@ -81,7 +110,7 @@ fun ComponentActivity.registerForPermissionFlowRequestsResult(
81110
@JvmOverloads
82111
fun Fragment.registerForPermissionFlowRequestsResult(
83112
requestPermissionsContract: RequestPermissionsContract = RequestPermissionsContract(),
84-
activityResultRegistry: ActivityResultRegistry = requireActivity().activityResultRegistry,
113+
activityResultRegistry: ActivityResultRegistry,
85114
callback: ActivityResultCallback<Map<String, Boolean>> = emptyCallback(),
86115
): ActivityResultLauncher<Array<String>> = registerForActivityResult(
87116
requestPermissionsContract,

permission-flow/src/test/java/dev/shreyaspatil/permissionFlow/utils/PermissionResultLauncherTest.kt

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,34 @@ class PermissionResultLauncherTest {
5858
} returns mockk<ActivityResultLauncher<Array<String>>>()
5959
}
6060

61-
fragment.registerForPermissionFlowRequestsResult(mockk(), mockk())
61+
fragment.registerForPermissionFlowRequestsResult(mockk(), mockk(), mockk())
6262

6363
verify(exactly = 1) {
6464
fragment.registerForActivityResult(any<RequestPermissionsContract>(), any(), any())
6565
}
6666
}
67+
68+
@Test
69+
fun test_Fragment_registerForPermissionFlowRequestsResult_withoutActivityRegistry() {
70+
val fragment = mockk<Fragment> {
71+
every {
72+
registerForActivityResult(
73+
any<ActivityResultContract<Array<String>, Map<String, Boolean>>>(),
74+
any<ActivityResultCallback<Map<String, Boolean>>>(),
75+
)
76+
} returns mockk<ActivityResultLauncher<Array<String>>>()
77+
}
78+
79+
val requestPermissionsContract = mockk<RequestPermissionsContract>()
80+
val callback = mockk<ActivityResultCallback<Map<String, Boolean>>>()
81+
82+
fragment.registerForPermissionFlowRequestsResult(
83+
requestPermissionsContract = requestPermissionsContract,
84+
callback = callback,
85+
)
86+
87+
verify(exactly = 1) {
88+
fragment.registerForActivityResult(requestPermissionsContract, callback)
89+
}
90+
}
6791
}

0 commit comments

Comments
 (0)