Skip to content

Commit 8f57919

Browse files
authored
Compose code for App Distribution (#1630)
1 parent 8c3e5e5 commit 8f57919

File tree

11 files changed

+271
-12
lines changed

11 files changed

+271
-12
lines changed

appdistribution/app/build.gradle

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ plugins {
66

77
android {
88
namespace 'com.google.firebase.appdistributionquickstart'
9-
compileSdk 33
9+
compileSdk 34
1010
defaultConfig {
1111
applicationId "com.google.firebase.appdistributionquickstart"
1212
minSdk 21 // minSdk would be 19 without compose
13-
targetSdk 33
13+
targetSdk 34
1414
versionCode 1
1515
versionName "1.0"
1616

@@ -66,7 +66,7 @@ dependencies {
6666
implementation platform('com.google.firebase:firebase-bom:31.0.2')
6767

6868
// ADD the SDK to the "prerelease" variant only (example)
69-
implementation 'com.google.firebase:firebase-appdistribution-ktx:16.0.0-beta01'
69+
implementation 'com.google.firebase:firebase-appdistribution:16.0.0-beta12'
7070

7171
// For an optimal experience using App Distribution, add the Firebase SDK
7272
// for Google Analytics. This is recommended, but not required.
@@ -77,6 +77,7 @@ dependencies {
7777
implementation "androidx.compose.material:material:$compose_version"
7878
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
7979
implementation 'androidx.activity:activity-compose:1.5.1'
80+
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.4.1'
8081

8182
androidTestImplementation 'androidx.test:runner:1.4.0'
8283
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

appdistribution/app/src/main/AndroidManifest.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@
2121
android:theme="@style/AppTheme">
2222
</activity>
2323

24+
<activity
25+
android:name=".kotlin.ComposeMainActivity"
26+
android:label="@string/app_name"
27+
android:theme="@style/AppTheme">
28+
</activity>
29+
2430
<activity android:name=".EntryChoiceActivity"
2531
android:exported="true">
2632
<intent-filter>

appdistribution/app/src/main/java/com/google/firebase/appdistributionquickstart/EntryChoiceActivity.kt

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,29 @@ import android.content.Intent
44
import com.firebase.example.internal.BaseEntryChoiceActivity
55
import com.firebase.example.internal.Choice
66
import com.google.firebase.appdistributionquickstart.java.MainActivity
7+
import com.google.firebase.appdistributionquickstart.kotlin.ComposeMainActivity
78
import com.google.firebase.appdistributionquickstart.kotlin.KotlinMainActivity
89

910
class EntryChoiceActivity : BaseEntryChoiceActivity() {
1011

1112
override fun getChoices(): List<Choice> {
1213
return listOf(
13-
Choice(
14-
"Java",
15-
"Run the Firebase App Distribution quickstart written in Java.",
16-
Intent(this, MainActivity::class.java)),
17-
Choice(
18-
"Kotlin",
19-
"Run the Firebase App Distribution quickstart written in Kotlin.",
20-
Intent(this, KotlinMainActivity::class.java))
21-
)
14+
Choice(
15+
"Java",
16+
"Run the Firebase App Distribution quickstart written in Java.",
17+
Intent(this, MainActivity::class.java)
18+
),
19+
Choice(
20+
"Kotlin",
21+
"Run the Firebase App Distribution quickstart written in Kotlin.",
22+
Intent(this, KotlinMainActivity::class.java)
23+
),
24+
Choice(
25+
"Compose",
26+
"Run the Firebase App Distribution quickstart written in Compose.",
27+
Intent(this, ComposeMainActivity::class.java),
28+
),
29+
30+
)
2231
}
2332
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.google.firebase.appdistributionquickstart.kotlin
2+
3+
import androidx.lifecycle.ViewModel
4+
import androidx.lifecycle.ViewModelProvider
5+
import androidx.lifecycle.viewmodel.CreationExtras
6+
import com.google.firebase.Firebase
7+
import com.google.firebase.appdistribution.FirebaseAppDistribution
8+
import com.google.firebase.appdistribution.FirebaseAppDistributionException
9+
import com.google.firebase.appdistribution.appDistribution
10+
11+
class AppDistributionViewModel(
12+
val appDistribution: FirebaseAppDistribution
13+
) : ViewModel() {
14+
15+
companion object {
16+
const val TAG = "AppDistribution-Quickstart"
17+
18+
// Used to inject this ViewModel's dependencies
19+
// See also: https://developer.android.com/topic/libraries/architecture/viewmodel/viewmodel-factories
20+
val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
21+
@Suppress("UNCHECKED_CAST")
22+
override fun <T : ViewModel> create(
23+
modelClass: Class<T>,
24+
extras: CreationExtras
25+
): T {
26+
// Get instance.
27+
val appDistribution = Firebase.appDistribution
28+
return AppDistributionViewModel(appDistribution) as T
29+
}
30+
}
31+
}
32+
33+
fun updateIfNewRelease() {
34+
appDistribution.updateIfNewReleaseAvailable()
35+
.addOnProgressListener { updateProgress ->
36+
// (Optional) Implement custom progress updates in addition to
37+
// automatic NotificationManager updates.
38+
}
39+
.addOnFailureListener { e ->
40+
if (e is FirebaseAppDistributionException) {
41+
// Handle exception.
42+
}
43+
}
44+
}
45+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package com.google.firebase.appdistributionquickstart.kotlin
2+
3+
import android.os.Bundle
4+
import androidx.activity.ComponentActivity
5+
import androidx.activity.compose.setContent
6+
import androidx.compose.foundation.Image
7+
import androidx.compose.foundation.layout.Column
8+
import androidx.compose.foundation.layout.Spacer
9+
import androidx.compose.foundation.layout.fillMaxSize
10+
import androidx.compose.foundation.layout.height
11+
import androidx.compose.foundation.layout.padding
12+
import androidx.compose.material.MaterialTheme
13+
import androidx.compose.material.Scaffold
14+
import androidx.compose.material.Surface
15+
import androidx.compose.material.Text
16+
import androidx.compose.material.TopAppBar
17+
import androidx.compose.runtime.Composable
18+
import androidx.compose.runtime.DisposableEffect
19+
import androidx.compose.runtime.LaunchedEffect
20+
import androidx.compose.runtime.collectAsState
21+
import androidx.compose.runtime.getValue
22+
import androidx.compose.ui.Alignment
23+
import androidx.compose.ui.Modifier
24+
import androidx.compose.ui.graphics.Color
25+
import androidx.compose.ui.layout.ContentScale
26+
import androidx.compose.ui.platform.LocalLifecycleOwner
27+
import androidx.compose.ui.res.colorResource
28+
import androidx.compose.ui.res.painterResource
29+
import androidx.compose.ui.res.stringResource
30+
import androidx.compose.ui.text.style.TextAlign
31+
import androidx.compose.ui.unit.dp
32+
import androidx.lifecycle.Lifecycle
33+
import androidx.lifecycle.LifecycleEventObserver
34+
import androidx.lifecycle.LifecycleOwner
35+
import androidx.lifecycle.viewmodel.compose.viewModel
36+
import com.google.firebase.appdistributionquickstart.R
37+
import com.google.firebase.appdistributionquickstart.ui.theme.AppDistributionTheme
38+
39+
40+
class ComposeMainActivity : ComponentActivity() {
41+
42+
override fun onCreate(savedInstanceState: Bundle?) {
43+
super.onCreate(savedInstanceState)
44+
45+
setContent {
46+
AppDistributionTheme {
47+
Surface(
48+
modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background
49+
) {
50+
MainAppView()
51+
}
52+
}
53+
}
54+
}
55+
}
56+
57+
@Composable
58+
fun MainAppView(
59+
appDistributionViewModel: AppDistributionViewModel = viewModel(factory = AppDistributionViewModel.Factory)
60+
) {
61+
62+
ComposableLifecycle { source, event ->
63+
if (event == Lifecycle.Event.ON_RESUME) {
64+
appDistributionViewModel.updateIfNewRelease()
65+
}
66+
}
67+
68+
69+
Scaffold(topBar = {
70+
TopAppBar(
71+
backgroundColor = colorResource(R.color.colorPrimary)
72+
) {
73+
Text(
74+
text = stringResource(R.string.app_name),
75+
style = MaterialTheme.typography.h6,
76+
textAlign = TextAlign.Center,
77+
modifier = Modifier.padding(8.dp),
78+
color = Color.White
79+
)
80+
}
81+
}, content = { it ->
82+
Column(
83+
horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier
84+
.fillMaxSize()
85+
.padding(it)
86+
) {
87+
Spacer(modifier = Modifier.height(30.dp))
88+
Image(
89+
painterResource(R.drawable.firebase_lockup_400),
90+
contentDescription = "Firebase logo",
91+
contentScale = ContentScale.Crop,
92+
)
93+
Text(
94+
text = stringResource(R.string.textview_text), modifier = Modifier.padding(8.dp)
95+
)
96+
}
97+
})
98+
}
99+
100+
@Composable
101+
fun ComposableLifecycle(
102+
lifeCycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
103+
onEvent: (LifecycleOwner, Lifecycle.Event) -> Unit
104+
) {
105+
DisposableEffect(lifeCycleOwner) {
106+
val observer = LifecycleEventObserver { source, event ->
107+
onEvent(source, event)
108+
}
109+
lifeCycleOwner.lifecycle.addObserver(observer)
110+
onDispose {
111+
lifeCycleOwner.lifecycle.removeObserver(observer)
112+
}
113+
}
114+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.google.firebase.appdistributionquickstart.ui.theme
2+
3+
import androidx.compose.ui.graphics.Color
4+
5+
val Purple80 = Color(0xFFD0BCFF)
6+
val PurpleGrey80 = Color(0xFFCCC2DC)
7+
val Pink80 = Color(0xFFEFB8C8)
8+
9+
val FirebaseBlue = Color(0xFF0288D1) // copied from colors.xml
10+
val FirebaseBannerBlue = Color(0xFF039BE5) // copied from colors.xml
11+
val FirebaseOrange = Color(0xFFFFA000) // copied from colors.xml
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.google.firebase.appdistributionquickstart.ui.theme
2+
3+
import androidx.compose.foundation.shape.RoundedCornerShape
4+
import androidx.compose.material.Shapes
5+
import androidx.compose.ui.unit.dp
6+
7+
val Shapes = Shapes(
8+
small = RoundedCornerShape(16.dp),
9+
medium = RoundedCornerShape(2.dp),
10+
large = RoundedCornerShape(0.dp)
11+
)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.google.firebase.appdistributionquickstart.ui.theme
2+
3+
import androidx.compose.foundation.isSystemInDarkTheme
4+
import androidx.compose.material.MaterialTheme
5+
import androidx.compose.material.darkColors
6+
import androidx.compose.material.lightColors
7+
8+
import androidx.compose.runtime.Composable
9+
10+
private val DarkColorPalette = darkColors(
11+
primary = Purple80,
12+
primaryVariant = PurpleGrey80,
13+
secondary = Pink80
14+
)
15+
16+
private val LightColorPalette = lightColors(
17+
primary = FirebaseBlue,
18+
primaryVariant = FirebaseBannerBlue,
19+
secondary = FirebaseOrange
20+
)
21+
22+
@Composable
23+
fun AppDistributionTheme(
24+
darkTheme: Boolean = isSystemInDarkTheme(),
25+
content: @Composable () -> Unit
26+
) {
27+
val colors = if (darkTheme) {
28+
DarkColorPalette
29+
} else {
30+
LightColorPalette
31+
}
32+
33+
MaterialTheme(
34+
colors = colors,
35+
typography = Typography,
36+
shapes = Shapes,
37+
content = content
38+
)
39+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.google.firebase.appdistributionquickstart.ui.theme
2+
3+
import androidx.compose.material.Typography
4+
import androidx.compose.ui.text.TextStyle
5+
import androidx.compose.ui.text.font.FontFamily
6+
import androidx.compose.ui.text.font.FontWeight
7+
import androidx.compose.ui.unit.sp
8+
9+
// Set of Material typography styles to start with
10+
val Typography = Typography(
11+
body1 = TextStyle(
12+
fontFamily = FontFamily.Default,
13+
fontWeight = FontWeight.Normal,
14+
fontSize = 16.sp,
15+
lineHeight = 24.sp,
16+
letterSpacing = 0.5.sp
17+
)
18+
)

appdistribution/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
buildscript {
44

5+
ext {
6+
compose_version = '1.2.1'
7+
}
8+
59
repositories {
610
mavenLocal()
711
google()

0 commit comments

Comments
 (0)