-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
64 changed files
with
1,961 additions
and
72 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# Setup | ||
|
||
``` | ||
dependencies { | ||
implementation "com.github.skgmn:startactivityx:1.0.0" | ||
} | ||
``` | ||
If you don't know how to access to GitHub Packges, please refer to [this](https://gist.github.com/skgmn/79da4a935e904078491e932bd5b327c7). | ||
|
||
# Features | ||
|
||
## startActivityForResult | ||
|
||
```kotlin | ||
lifecycleScope.launch { | ||
val activityResult = startActivityForResult(intent) | ||
} | ||
``` | ||
That's it. | ||
It provides some extension methods of `ActivityResult` for the convenience. | ||
|
||
```kotlin | ||
val ActivityResult.isOk: Boolean | ||
val ActivityResult.isCanceled: Boolean | ||
fun ActivityResult.getDataIfOk(): Intent? | ||
``` | ||
So returned `ActivityResult` can be used like this in more kotlin way. | ||
|
||
```kotlin | ||
lifecycleScope.launch { | ||
startActivityForResult(intent).getDataIfOk()?.let { open(it) } | ||
} | ||
``` | ||
|
||
## requestPermissions | ||
|
||
```kotlin | ||
lifecycleScope.launch { | ||
if (requestPermissions(Manifest.permission.CAMERA).granted) { | ||
// Permissions are granted here | ||
startCamera() | ||
} | ||
} | ||
``` | ||
Simple again. | ||
This single method also handles rationale dialog and _do not ask again_ cases so there are no other things to acquire permissions. | ||
|
||
There are some more features which are not documented yet. Please refer to source code and sample code to know about them. | ||
|
||
## PermissionStatus | ||
|
||
Sometimes there needs to show or hide views according to whether permissions are granted or not. `listenPermissionStatus()` has been introduced to manage this case. It returns `Flow<Boolean>` which infinitely emits boolean values that indicate whether required permissions are granted. | ||
```kotlin | ||
lifecycleScope.launch { | ||
listenPermissonStatus(Manifest.permission.CAMERA).collect { | ||
binding.permissionsGranted = it.granted | ||
} | ||
} | ||
``` | ||
```xml | ||
<Button | ||
android:text="Grant permissions" | ||
android:visibility="@{permissionsGranted ? View.GONE : View.VISIBLE}" /> | ||
``` | ||
|
||
Or `getPermissionStatus()` can be used to get `PermissionStatus` just once. | ||
|
||
## startActivityForInstance | ||
|
||
This is some kind of bonus feature. It starts an Activity and returns its instance. | ||
```kotlin | ||
lifecycleScope.launch { | ||
val intent = ExplicitIntent(context, MyActivity::class.java) | ||
val activity = startActivityForInstance(intent) | ||
} | ||
``` |
24 changes: 0 additions & 24 deletions
24
app/src/androidTest/java/com/github/skgmn/rapidstartactivity/ExampleInstrumentedTest.kt
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
17 changes: 0 additions & 17 deletions
17
app/src/test/java/com/github/skgmn/rapidstartactivity/ExampleUnitTest.kt
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
package="com.github.skgmn.startactivityx.camerasample"> | ||
|
||
<uses-feature android:name="android.hardware.camera.any" /> | ||
|
||
<uses-permission android:name="android.permission.CAMERA" /> | ||
<uses-permission | ||
android:name="android.permission.WRITE_EXTERNAL_STORAGE" | ||
android:maxSdkVersion="28" /> | ||
|
||
<application | ||
android:name=".SampleApplication" | ||
android:allowBackup="true" | ||
android:icon="@mipmap/ic_launcher" | ||
android:label="@string/app_name" | ||
android:roundIcon="@mipmap/ic_launcher_round" | ||
android:supportsRtl="true" | ||
android:theme="@style/Theme.CameraSample"> | ||
<activity android:name=".MainActivity"> | ||
<intent-filter> | ||
<action android:name="android.intent.action.MAIN" /> | ||
<category android:name="android.intent.category.LAUNCHER" /> | ||
</intent-filter> | ||
</activity> | ||
</application> | ||
|
||
</manifest> |
94 changes: 94 additions & 0 deletions
94
camerasample/src/main/java/com/github/skgmn/startactivityx/camerasample/MainActivity.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package com.github.skgmn.startactivityx.camerasample | ||
|
||
import android.Manifest | ||
import android.content.ActivityNotFoundException | ||
import android.content.Intent | ||
import android.net.Uri | ||
import android.os.Build | ||
import android.os.Bundle | ||
import android.widget.Toast | ||
import androidx.activity.viewModels | ||
import androidx.appcompat.app.AppCompatActivity | ||
import androidx.databinding.DataBindingUtil | ||
import androidx.lifecycle.lifecycleScope | ||
import androidx.lifecycle.whenStarted | ||
import com.github.skgmn.startactivityx.* | ||
import com.github.skgmn.startactivityx.camerasample.databinding.ActivityMainBinding | ||
import com.github.skgmn.viewmodelevent.handle | ||
import kotlinx.coroutines.flow.collect | ||
import kotlinx.coroutines.launch | ||
|
||
class MainActivity : AppCompatActivity() { | ||
private val viewModel: MainViewModel by viewModels() | ||
|
||
private lateinit var binding: ActivityMainBinding | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
binding = DataBindingUtil.setContentView(this, R.layout.activity_main) | ||
|
||
binding.owner = this | ||
binding.lifecycleOwner = this | ||
binding.viewModel = viewModel | ||
|
||
lifecycleScope.launch { | ||
requestPermissions(Manifest.permission.CAMERA) | ||
viewModel.permissionsInitiallyRequested.value = true | ||
init() | ||
} | ||
} | ||
|
||
private fun init() { | ||
handleEvents() | ||
|
||
lifecycleScope.launch { | ||
listenPermissionStatus(Manifest.permission.CAMERA).collect { | ||
viewModel.cameraPermissionsGranted.value = it.granted | ||
} | ||
} | ||
} | ||
|
||
private fun handleEvents() = with(viewModel) { | ||
handle(requestCameraPermissionsByUserEvent) { | ||
lifecycleScope.launch { | ||
val permissionRequest = PermissionRequest(listOf(Manifest.permission.CAMERA), true) | ||
requestPermissions(permissionRequest) | ||
} | ||
} | ||
handle(requestTakePhotoPermissionsEvent) { | ||
lifecycleScope.launch { | ||
val permissionRequest = | ||
PermissionRequest(listOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), true) | ||
val permissionResult = requestPermissions(permissionRequest) | ||
if (permissionResult.granted) { | ||
// ImageCapture seems not work well right after a permission is granted. | ||
// In this case recreate and rebind ImageCapture to workaround this issue. | ||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P && | ||
permissionResult == GrantResult.JUST_GRANTED | ||
) { | ||
viewModel.replaceImageCapture() | ||
binding.executePendingBindings() | ||
} | ||
whenStarted { | ||
viewModel.takePhoto() | ||
} | ||
} else { | ||
Toast.makeText(this@MainActivity, R.string.no_permissions, Toast.LENGTH_SHORT) | ||
.show() | ||
} | ||
} | ||
} | ||
handle(showTakenPhotoEvent) { uri -> | ||
if (uri != Uri.EMPTY) { | ||
try { | ||
val intent = Intent(Intent.ACTION_VIEW, uri) | ||
startActivity(intent) | ||
return@handle | ||
} catch (e: ActivityNotFoundException) { | ||
// fall through | ||
} | ||
} | ||
Toast.makeText(this@MainActivity, R.string.photo_saved, Toast.LENGTH_SHORT).show() | ||
} | ||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
camerasample/src/main/java/com/github/skgmn/startactivityx/camerasample/MainViewModel.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package com.github.skgmn.startactivityx.camerasample | ||
|
||
import android.app.Application | ||
import android.content.ContentValues | ||
import android.net.Uri | ||
import android.provider.MediaStore | ||
import androidx.camera.core.CameraSelector | ||
import androidx.camera.core.ImageCapture | ||
import androidx.camera.core.Preview | ||
import androidx.lifecycle.AndroidViewModel | ||
import androidx.lifecycle.viewModelScope | ||
import com.github.skgmn.cameraxx.takePicture | ||
import com.github.skgmn.viewmodelevent.publicEvent | ||
import kotlinx.coroutines.flow.MutableStateFlow | ||
import kotlinx.coroutines.flow.SharingStarted | ||
import kotlinx.coroutines.flow.stateIn | ||
import kotlinx.coroutines.launch | ||
|
||
class MainViewModel(application: Application) : AndroidViewModel(application) { | ||
private val imageCaptureUseCaseFlow = MutableStateFlow(newImageCapture()) | ||
|
||
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA | ||
val previewUseCase = Preview.Builder().build() | ||
val imageCaptureUseCase = | ||
imageCaptureUseCaseFlow.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) | ||
|
||
val permissionsInitiallyRequested = MutableStateFlow(false) | ||
val cameraPermissionsGranted = MutableStateFlow(false) | ||
|
||
val requestCameraPermissionsByUserEvent = publicEvent<Any>() | ||
val requestTakePhotoPermissionsEvent = publicEvent<Any>() | ||
val showTakenPhotoEvent = publicEvent<Uri>() | ||
|
||
fun requestCameraPermissions() { | ||
requestCameraPermissionsByUserEvent.post(Unit) | ||
} | ||
|
||
fun requestTakePhotoPermissions() { | ||
requestTakePhotoPermissionsEvent.post(Unit) | ||
} | ||
|
||
fun takePhoto() { | ||
viewModelScope.launch { | ||
val outputOptions = ImageCapture.OutputFileOptions.Builder( | ||
getApplication<Application>().contentResolver, | ||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, | ||
ContentValues() | ||
).build() | ||
|
||
val result = imageCaptureUseCaseFlow.value.takePicture(outputOptions) | ||
showTakenPhotoEvent.post(result.savedUri ?: Uri.EMPTY) | ||
} | ||
} | ||
|
||
fun replaceImageCapture() { | ||
imageCaptureUseCaseFlow.value = newImageCapture() | ||
} | ||
|
||
private fun newImageCapture() = ImageCapture.Builder().build() | ||
} |
Oops, something went wrong.