Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion GhostRun-Android/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import org.jetbrains.kotlin.kapt3.base.Kapt.kapt

plugins {
alias(libs.plugins.com.android.application)
alias(libs.plugins.org.jetbrains.kotlin.android)
alias(libs.plugins.com.google.dagger.hilt.android)
kotlin("kapt")
}

android {
Expand Down Expand Up @@ -66,4 +70,7 @@ dependencies {
androidTestImplementation(libs.ui.test.junit4)
debugImplementation(libs.ui.tooling)
debugImplementation(libs.ui.test.manifest)
}

implementation(libs.hilt.android)
kapt(libs.hilt.compiler)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.erooja.ghostrun_android

import android.app.Application
class App: Application() {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.erooja.ghostrun_android.permission

import android.Manifest
import android.content.Context
import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.IntentSenderRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.lifecycle.lifecycleScope
import com.erooja.ghostrun_android.permission.LocationPermissionUtil.permissions
import com.erooja.ghostrun_android.state.LocationPermissionState
import dagger.hilt.android.qualifiers.ActivityContext
import dagger.hilt.android.scopes.ActivityScoped
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableSharedFlow
import javax.inject.Inject

@ActivityScoped
class LocationPermissionRequester @Inject constructor(
@ActivityContext private val context: Context,
) {
private val lifecycleScope: CoroutineScope
get() = (context as ComponentActivity).lifecycleScope

private var resolutionResultLauncher: ActivityResultLauncher<IntentSenderRequest>? = null
private var requestPermissionLauncher: ActivityResultLauncher<Array<String>>? = null

val permissionFlow: MutableSharedFlow<LocationPermissionState> = MutableSharedFlow()

init {
initializeLauncher()
}

private fun initializeLauncher() {
resolutionResultLauncher = (context as ComponentActivity).activityResultRegistry.register(
RESOLUTION_RESULT,
ActivityResultContracts.StartIntentSenderForResult()
) {
if (it.resultCode == ComponentActivity.RESULT_OK) {
emitFlow(LocationPermissionState.ObtainLocationPermission)
} else {
emitFlow(LocationPermissionState.Error.PermissionFail)
}
}

requestPermissionLauncher = context.activityResultRegistry.register(
REQUEST_LOCATION_PERMISSION, ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
when {
permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) -> {
emitFlow(LocationPermissionState.ObtainLocationPermission)
}
permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) -> {
emitFlow(LocationPermissionState.Error.PermissionFail)
}
else -> {
emitFlow(LocationPermissionState.Error.PermissionFail)
}
}
}
}

private fun emitFlow(state: LocationPermissionState) = lifecycleScope.launch {
permissionFlow.emit(state)
}

fun requestPermission() {
requestPermissionLauncher?.launch(permissions)
}

fun requestResolution(request: IntentSenderRequest) {
resolutionResultLauncher?.launch(request)
}

companion object {
private const val RESOLUTION_RESULT = "RESOLUTION_RESULT"
private const val REQUEST_LOCATION_PERMISSION = "REQUEST_LOCATION_PERMISSION"
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.erooja.ghostrun_android.permission

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import androidx.core.content.ContextCompat
import com.erooja.ghostrun_android.state.CurrentLocationState
import kotlinx.coroutines.flow.MutableStateFlow

object LocationPermissionUtil {
val trackingLocationFlow: MutableStateFlow<CurrentLocationState> = MutableStateFlow(
CurrentLocationState.UnInitialized)

val permissions = arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)

fun needPermissionRequest(context: Context): Boolean {
return !isPermissionGranted(context)
}

fun isAlreadyPermissionDenied(context: Context): Boolean {
return permissions.any {
ContextCompat.checkSelfPermission(context, it) != PackageManager.PERMISSION_GRANTED
}
}

fun isPermissionGranted(context: Context): Boolean {
return permissions.all {
ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.erooja.ghostrun_android.permission

import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import dagger.hilt.android.qualifiers.ActivityContext
import javax.inject.Inject

class NetworkConnectivityManager @Inject constructor(
@ActivityContext context: Context
) {
private val connectivityManager: ConnectivityManager by lazy {
(context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager)
}

private val networkRequest: NetworkRequest by lazy {
NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build()
}

private var onNetworkConnectionSucceed: (() -> Unit)? = null
private var onNetworkConnectionFailed: (() -> Unit)? = null

private val networkConnectivityCallback by lazy {
object: ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
super.onAvailable(network)
onNetworkConnectionSucceed?.invoke()
}

override fun onLost(network: Network) {
super.onLost(network)
onNetworkConnectionFailed?.invoke()
}
}
}

init {
connectivityManager.registerNetworkCallback(networkRequest, networkConnectivityCallback)
}

fun isNotAvailableNetwork(): Boolean {
val networkCapabilities = connectivityManager.activeNetwork ?: return true
val activateTransport =
connectivityManager.getNetworkCapabilities(networkCapabilities) ?: return true

return !(activateTransport.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
|| activateTransport.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
|| activateTransport.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET))
}

fun setNetworkConnectionSucceed(onNetworkConnectionSucceed: () -> Unit): NetworkConnectivityManager {
this.onNetworkConnectionSucceed = onNetworkConnectionSucceed
return this
}

fun setNetworkConnectionFailed(onNetworkConnectionFailed: () -> Unit): NetworkConnectivityManager {
this.onNetworkConnectionFailed = onNetworkConnectionFailed
return this
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.erooja.ghostrun_android.state

data class Coordinate(
val x: Double,
val y: Double
) {
companion object {
val Default = Coordinate(
x = Double.NaN,
y = Double.NaN
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.erooja.ghostrun_android.state

sealed class CurrentLocationState {
object UnInitialized: CurrentLocationState()

data class Success(
val boundBottomLeftCoordinate: Coordinate = Coordinate.Default,
val boundTopRightCoordinate: Coordinate = Coordinate.Default,
val centerCoordinate: Coordinate = Coordinate.Default,
) : CurrentLocationState()

object Error : CurrentLocationState()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.erooja.ghostrun_android.state

sealed class LocationPermissionState {
object ObtainLocationPermission: LocationPermissionState()

sealed class Error : LocationPermissionState() {
object PermissionFail : Error()
}
}
3 changes: 2 additions & 1 deletion GhostRun-Android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
plugins {
alias(libs.plugins.com.android.application) apply false
alias(libs.plugins.org.jetbrains.kotlin.android) apply false
}
alias(libs.plugins.com.google.dagger.hilt.android) apply false
}
6 changes: 5 additions & 1 deletion GhostRun-Android/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[versions]
com-android-application = "8.1.0-alpha11"
com-android-application = "8.1.0-rc01"
org-jetbrains-kotlin-android = "1.7.20"
core-ktx = "1.9.0"
junit = "4.13.2"
Expand All @@ -8,6 +8,7 @@ espresso-core = "3.5.1"
lifecycle-runtime-ktx = "2.3.1"
activity-compose = "1.5.1"
compose-bom = "2022.10.00"
hilt = "2.44"

[libraries]
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
Expand All @@ -24,10 +25,13 @@ ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview
ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
material3 = { group = "androidx.compose.material3", name = "material3" }
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" }

[plugins]
com-android-application = { id = "com.android.application", version.ref = "com-android-application" }
org-jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "org-jetbrains-kotlin-android" }
com-google-dagger-hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt"}

[bundles]

8 changes: 3 additions & 5 deletions GhostRun-Android/local.properties
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
## This file is automatically generated by Android Studio.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file should *NOT* be checked into Version Control Systems,
## This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
sdk.dir=/Users/mintm1pro/Library/Android/sdk
#Sat Jul 08 14:45:25 KST 2023
sdk.dir=/Users/sh.jeong/Library/Android/sdk