Skip to content

Commit

Permalink
Merge pull request #6 from Andi-IM/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
Andi-IM authored Aug 11, 2023
2 parents 8e30b67 + 65864a2 commit 9fa7ba9
Show file tree
Hide file tree
Showing 106 changed files with 4,163 additions and 305 deletions.
56 changes: 51 additions & 5 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
plugins {
id("com.android.application")
kotlin("android")
kotlin("kapt")
id("com.google.dagger.hilt.android")
id("com.google.gms.google-services")
id("com.google.firebase.crashlytics")
id("com.google.firebase.firebase-perf")
}

android {
compileSdk = libs.versions.compile.sdk.version.get().toInt()

defaultConfig {
minSdk = libs.versions.min.sdk.version.get().toInt()
namespace = "com.github.andiim.orchidscan.app"
targetSdk = libs.versions.target.sdk.version.get().toInt()
namespace = "com.github.andiim.plantscan.app"

applicationId = AppCoordinates.APP_ID
versionCode = AppCoordinates.APP_VERSION_CODE
versionName = AppCoordinates.APP_VERSION_NAME
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildFeatures {
viewBinding = true
compose = true
buildConfig = true
}
composeOptions { kotlinCompilerExtensionVersion = libs.versions.compose.compilerextension.get() }
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
kotlinOptions { jvmTarget = JavaVersion.VERSION_17.toString() }
buildTypes {
getByName("release") {
isMinifyEnabled = false
Expand All @@ -38,6 +44,7 @@ android {
lint {
warningsAsErrors = true
abortOnError = true
baseline = File("lint-baseline.xml")
}

// Use this block to configure different flavors
Expand All @@ -55,16 +62,55 @@ android {
}

dependencies {
// Logging
implementation(libs.timber)

// UI
implementation(libs.material)

// Hilt
implementation(libs.dagger.hilt)
implementation(libs.dagger.hilt.navigation.compose)
kapt(libs.dagger.hilt.compiler)

// Ext. Module
implementation(projects.libraryAndroid)
implementation(projects.libraryKotlin)

// Compose
implementation(platform(libs.compose.bom))
implementation(libs.bundles.compose)
debugImplementation(libs.bundles.compose.debug)
implementation(libs.bundles.lifecycle)

// Accompanist
implementation(libs.accompanist.permission)
implementation(libs.accompanist.webview)

// Firebase
implementation(platform(libs.firebase.bom))
implementation(libs.bundles.firebase)
implementation(libs.play.services.auth)

// compat
implementation(libs.androidx.appcompat)
implementation(libs.androidx.core.ktx)

// Unit tests
testImplementation(libs.junit)
testImplementation(libs.dagger.hilt.testing)
kaptTest(libs.dagger.hilt.compiler)

// Instrument test
androidTestImplementation(libs.androidx.test.ext.junit)
androidTestImplementation(libs.androidx.test.ext.junit.ktx)
androidTestImplementation(libs.androidx.test.rules)
androidTestImplementation(libs.espresso.core)
androidTestImplementation(libs.dagger.hilt.testing)
androidTestImplementation(libs.kotlin.coroutines.test)
androidTestImplementation(libs.truth)
kaptAndroidTest(libs.dagger.hilt.compiler)
}

kapt { correctErrorTypes = true }
hilt { enableAggregatingTask = true }
36 changes: 30 additions & 6 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,46 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
android:name=".PlantScanHiltApplication"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="AllowBackup">
<activity android:name=".MainActivity"
android:exported="true">
android:theme="@style/Theme.PlantScan.Material3"
tools:targetApi="31">
<activity
android:name=".PlantScanActivity"
android:exported="true"
android:theme="@style/Theme.PlantScan.NoActionBar"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".PlantScanMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>

<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@mipmap/ic_launcher" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/md_theme_light_primary" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="@string/default_notification_channel_id" />
<meta-data
android:name="firebase_performance_logcat_enabled"
android:value="true" />
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.github.andiim.plantscan.app

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class PlantScanActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { PlantScanApp() }
}
}
112 changes: 112 additions & 0 deletions app/src/main/java/com/github/andiim/plantscan/app/PlantScanApp.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package com.github.andiim.plantscan.app

import android.Manifest
import android.content.Context
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Snackbar
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import com.github.andiim.plantscan.app.ui.common.composables.BottomBar
import com.github.andiim.plantscan.app.ui.common.composables.PermissionDialog
import com.github.andiim.plantscan.app.ui.common.composables.RationaleDialog
import com.github.andiim.plantscan.app.ui.common.snackbar.SnackbarManager
import com.github.andiim.plantscan.app.ui.navigation.SetupRootNavGraph
import com.github.andiim.plantscan.app.ui.theme.PlantScanTheme
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import com.google.accompanist.permissions.shouldShowRationale
import kotlinx.coroutines.CoroutineScope

@Composable
fun PlantScanApp() {
PlantScanTheme {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
RequestNotificationPermissionDialog()
}

Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {

val appState = rememberAppState()
Scaffold(
snackbarHost = {
SnackbarHost(
hostState = appState.snackbarHostState,
modifier = Modifier.padding(8.dp),
snackbar = { snackbarData ->
Snackbar(
snackbarData,
contentColor = MaterialTheme.colorScheme.onPrimary
)
})
},
bottomBar = { BottomBar(state = appState) }
) { innerPadding ->
SetupRootNavGraph(appState, modifier = Modifier.padding(innerPadding))
}
}
}
}

@Composable
@ReadOnlyComposable
fun getContext(): Context {
LocalConfiguration.current
return LocalContext.current
}

@Composable
fun rememberAppState(
snackbarHostState: SnackbarHostState = SnackbarHostState(),
navController: NavHostController = rememberNavController(),
snackbarManager: SnackbarManager = SnackbarManager,
getContext: Context = getContext(),
coroutineScope: CoroutineScope = rememberCoroutineScope()
) =
remember(
snackbarHostState,
navController,
snackbarManager,
getContext,
coroutineScope
) {
PlantScanAppState(
snackbarHostState,
navController,
snackbarManager,
getContext,
coroutineScope
)
}

@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun RequestNotificationPermissionDialog() {
val permissionState =
rememberPermissionState(permission = Manifest.permission.POST_NOTIFICATIONS)

if (!permissionState.status.isGranted) {
if (permissionState.status.shouldShowRationale) RationaleDialog()
else PermissionDialog { permissionState.launchPermissionRequest() }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.github.andiim.plantscan.app

import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.compose.material3.SnackbarHostState
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
import com.github.andiim.plantscan.app.ui.common.snackbar.SnackbarManager
import com.github.andiim.plantscan.app.ui.common.snackbar.SnackbarMessage.Companion.toMessage
import com.github.andiim.plantscan.app.ui.navigation.Direction
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch

class PlantScanAppState(
val snackbarHostState: SnackbarHostState,
val navController: NavHostController,
private val snackbarManager: SnackbarManager,
private val context: Context,
coroutineScope: CoroutineScope
) {
init {
coroutineScope.launch {
snackbarManager.snackbarMessages.filterNotNull().collect { snackbarMessage ->
val text = snackbarMessage.toMessage(context.resources)
snackbarHostState.showSnackbar(text)
}
}
}

fun popUp() {
navController.popBackStack()
}

fun navigate(route: String, singleTopLaunch: Boolean = true) {
if (route == Direction.Detect.route) {
toDetectActivity()
} else {
navController.navigate(route) { launchSingleTop = singleTopLaunch }
}
}

private fun toDetectActivity() {
val intent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("plantscan://detection")
`package` = context.packageName
}
context.startActivity(intent)
}

fun navigateAndPopUp(route: String, popUp: String) {
navController.navigate(route) {
launchSingleTop = true
popUpTo(popUp) { inclusive = true }
}
}

fun clearAndNavigate(route: String) {
navController.navigate(route) {
popUpTo(navController.graph.findStartDestination().id) {
// saveState = true
inclusive = true
}
launchSingleTop = true
// restoreState = true
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.github.andiim.plantscan.app

import android.app.Application
import com.github.andiim.plantscan.app.di.DebugModule.provideTimberTree
import dagger.hilt.android.HiltAndroidApp
import timber.log.Timber

@HiltAndroidApp
class PlantScanHiltApplication : Application() {
override fun onCreate() {
super.onCreate()

if (BuildConfig.DEBUG) {
Timber.plant(provideTimberTree())
}
}
}
Loading

0 comments on commit 9fa7ba9

Please sign in to comment.