Skip to content

Commit 03030f3

Browse files
committed
1. Add firebase crashlytics and performance monitoring
2. Upgrade AGP to 7.0.2 3. Rename package and files to remove `TvMaze` reference 4. Add Firebase analytics 5. Integrate Firebase Remote Config
1 parent d2df602 commit 03030f3

30 files changed

+325
-66
lines changed

app/build.gradle.kts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ android {
5656
}
5757
}
5858
create("dev") {
59-
dimension = "default"
6059
applicationId = "com.android.tvflix"
6160
firebaseAppDistribution {
6261
releaseNotesFile = "release-notes.txt"
@@ -123,7 +122,6 @@ dependencies {
123122
implementation(Deps.AndroidX.ktx_fragment)
124123
implementation(Deps.AndroidX.ktx_activity)
125124
implementation(Deps.AndroidX.constraint_layout)
126-
implementation(Deps.AndroidX.Lifecycle.extensions)
127125
kapt(Deps.AndroidX.Lifecycle.compiler)
128126
implementation(Deps.AndroidX.Lifecycle.viewmodel)
129127
implementation(Deps.AndroidX.Paging.runtime)
@@ -135,6 +133,7 @@ dependencies {
135133
implementation(Deps.AndroidX.annotation)
136134
// end-region AndroidX
137135

136+
implementation(platform(Deps.OkHttp.okhttp_bom))
138137
implementation(Deps.OkHttp.main)
139138
implementation(Deps.OkHttp.logging_interceptor)
140139
implementation(Deps.Glide.runtime)

app/src/main/AndroidManifest.xml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,24 @@
1515
android:theme="@style/AppTheme"
1616
tools:ignore="LockedOrientationActivity">
1717
<activity
18-
android:name=".home.HomeActivity"
19-
android:exported="true"
20-
android:screenOrientation="sensorPortrait">
18+
android:name=".splash.SplashActivity"
19+
android:theme="@style/SplashTheme"
20+
android:exported="true">
2121
<intent-filter>
2222
<action android:name="android.intent.action.MAIN" />
2323

2424
<category android:name="android.intent.category.LAUNCHER" />
25+
2526
<action android:name="android.intent.action.VIEW" />
2627
</intent-filter>
2728
</activity>
28-
29+
<activity
30+
android:name=".home.HomeActivity"
31+
android:exported="true"
32+
android:screenOrientation="sensorPortrait" />
2933
<activity
3034
android:name=".shows.AllShowsActivity"
3135
android:screenOrientation="sensorPortrait" />
32-
3336
<activity
3437
android:name=".favorite.FavoriteShowsActivity"
3538
android:screenOrientation="sensorPortrait" />
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
package com.android.tvflix
22

33
import android.app.Application
4+
import com.android.tvflix.config.AppConfig
45
import dagger.hilt.android.HiltAndroidApp
56
import timber.log.Timber
7+
import javax.inject.Inject
68

79
@HiltAndroidApp
810
class TvFlixApplication : Application() {
11+
@Inject
12+
lateinit var appConfig: AppConfig
13+
914
override fun onCreate() {
1015
super.onCreate()
1116
if (BuildConfig.DEBUG) {
1217
Timber.plant(Timber.DebugTree())
1318
}
19+
appConfig.initialise()
1420
}
1521
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package com.android.tvflix.config
2+
3+
import com.android.tvflix.BuildConfig
4+
import com.google.firebase.ktx.Firebase
5+
import com.google.firebase.remoteconfig.ktx.remoteConfig
6+
import com.google.firebase.remoteconfig.ktx.remoteConfigSettings
7+
import kotlinx.coroutines.suspendCancellableCoroutine
8+
import javax.inject.Inject
9+
import javax.inject.Singleton
10+
import kotlin.coroutines.resume
11+
import kotlin.coroutines.resumeWithException
12+
13+
@Singleton
14+
class AppConfig
15+
@Inject
16+
constructor() {
17+
companion object {
18+
const val FETCH_TIMEOUT_S = 20L
19+
const val MIN_FETCH_INTERVAL_S = 3600L
20+
}
21+
22+
private val remoteConfig by lazy { Firebase.remoteConfig }
23+
24+
fun initialise() {
25+
val minFetchInterval: Long = if (BuildConfig.DEBUG) {
26+
0
27+
} else {
28+
MIN_FETCH_INTERVAL_S
29+
}
30+
val remoteConfigSettings = remoteConfigSettings {
31+
fetchTimeoutInSeconds = FETCH_TIMEOUT_S
32+
minimumFetchIntervalInSeconds = minFetchInterval
33+
}
34+
remoteConfig.apply {
35+
setConfigSettingsAsync(remoteConfigSettings)
36+
setDefaultsAsync(ConfigDefaults.getDefaultValues())
37+
}
38+
}
39+
40+
suspend fun fetch(): Boolean =
41+
suspendCancellableCoroutine { continuation ->
42+
remoteConfig.fetchAndActivate().addOnSuccessListener {
43+
continuation.resume(true)
44+
}.addOnFailureListener { exc -> continuation.resumeWithException(exc) }
45+
}
46+
47+
fun getString(key: String): String {
48+
return remoteConfig.getString(key)
49+
}
50+
51+
fun getBoolean(key: String): Boolean {
52+
return remoteConfig.getBoolean(key)
53+
}
54+
55+
fun getDouble(key: String): Double {
56+
return remoteConfig.getDouble(key)
57+
}
58+
59+
fun getLong(key: String): Long {
60+
return remoteConfig.getLong(key)
61+
}
62+
63+
fun getInt(key: String): Int {
64+
return remoteConfig.getLong(key).toInt()
65+
}
66+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.android.tvflix.config
2+
3+
object ConfigDefaults {
4+
fun getDefaultValues(): Map<String, Any> {
5+
return hashMapOf(ConfigKeys.FEATURE_FAVORITES_ENABLE to false)
6+
}
7+
8+
object ConfigKeys {
9+
const val FEATURE_FAVORITES_ENABLE = "feature_favorites_enable"
10+
}
11+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.android.tvflix.config
2+
3+
import javax.inject.Qualifier
4+
5+
@Qualifier
6+
@Target(
7+
AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER,
8+
AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.FIELD
9+
)
10+
annotation class FavoritesFeatureFlag
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.android.tvflix.config
2+
3+
import dagger.Module
4+
import dagger.Provides
5+
import dagger.hilt.InstallIn
6+
import dagger.hilt.components.SingletonComponent
7+
8+
@InstallIn(SingletonComponent::class)
9+
@Module
10+
object FeatureFlagModule {
11+
@FavoritesFeatureFlag
12+
@Provides
13+
fun provideFavoritesFeatureFlag(appConfig: AppConfig): Boolean {
14+
return appConfig.getBoolean(ConfigDefaults.ConfigKeys.FEATURE_FAVORITES_ENABLE)
15+
}
16+
}

app/src/main/java/com/android/tvflix/favorite/FavoriteShowsAdapter.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ class FavoriteShowsAdapter(
6161
return favoriteShows.size
6262
}
6363

64-
class FavoriteShowHolder(val binding: ShowListItemBinding) : RecyclerView.ViewHolder(binding.root)
64+
class FavoriteShowHolder(val binding: ShowListItemBinding) :
65+
RecyclerView.ViewHolder(binding.root)
6566

6667
interface Callback {
6768
fun onFavoriteClicked(show: FavoriteShow)

app/src/main/java/com/android/tvflix/home/HomeActivity.kt

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.android.tvflix.home
22

3+
import android.app.Activity
4+
import android.content.Intent
35
import android.os.Bundle
46
import android.view.Menu
57
import android.view.MenuItem
@@ -11,19 +13,42 @@ import androidx.core.view.isVisible
1113
import androidx.lifecycle.lifecycleScope
1214
import androidx.recyclerview.widget.GridLayoutManager
1315
import com.android.tvflix.R
16+
import com.android.tvflix.config.FavoritesFeatureFlag
1417
import com.android.tvflix.databinding.ActivityHomeBinding
1518
import com.android.tvflix.favorite.FavoriteShowsActivity
1619
import com.android.tvflix.shows.AllShowsActivity
1720
import com.android.tvflix.utils.GridItemDecoration
1821
import dagger.hilt.android.AndroidEntryPoint
1922
import kotlinx.coroutines.flow.collect
23+
import javax.inject.Inject
2024

2125
@AndroidEntryPoint
2226
class HomeActivity : AppCompatActivity(), ShowsAdapter.Callback {
2327
private val homeViewModel: HomeViewModel by viewModels()
2428
private lateinit var showsAdapter: ShowsAdapter
2529
private val binding by lazy { ActivityHomeBinding.inflate(layoutInflater) }
2630

31+
@JvmField
32+
@FavoritesFeatureFlag
33+
@Inject
34+
var favoritesFeatureEnable: Boolean = false
35+
36+
companion object {
37+
private const val NO_OF_COLUMNS = 2
38+
39+
@JvmStatic
40+
fun start(
41+
context: Activity
42+
) {
43+
val intent = Intent(context, HomeActivity::class.java)
44+
.apply {
45+
flags = (Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
46+
or Intent.FLAG_ACTIVITY_CLEAR_TOP)
47+
}
48+
context.startActivity(intent)
49+
}
50+
}
51+
2752
override fun onCreate(savedInstanceState: Bundle?) {
2853
super.onCreate(savedInstanceState)
2954
setContentView(binding.root)
@@ -74,7 +99,7 @@ class HomeActivity : AppCompatActivity(), ShowsAdapter.Callback {
7499

75100
private fun showPopularShows(homeViewData: HomeViewData) {
76101
val gridLayoutManager = GridLayoutManager(this, NO_OF_COLUMNS)
77-
showsAdapter = ShowsAdapter(this)
102+
showsAdapter = ShowsAdapter(this, favoritesFeatureEnable)
78103
showsAdapter.updateList(homeViewData.episodes.toMutableList())
79104
binding.popularShows.apply {
80105
layoutManager = gridLayoutManager
@@ -91,6 +116,7 @@ class HomeActivity : AppCompatActivity(), ShowsAdapter.Callback {
91116

92117
override fun onCreateOptionsMenu(menu: Menu): Boolean {
93118
menuInflater.inflate(R.menu.home_menu, menu)
119+
menu.findItem(R.id.action_favorites).isVisible = favoritesFeatureEnable
94120
return super.onCreateOptionsMenu(menu)
95121
}
96122

@@ -111,8 +137,4 @@ class HomeActivity : AppCompatActivity(), ShowsAdapter.Callback {
111137
override fun onFavoriteClicked(showViewData: HomeViewData.ShowViewData) {
112138
homeViewModel.onFavoriteClick(showViewData)
113139
}
114-
115-
companion object {
116-
private const val NO_OF_COLUMNS = 2
117-
}
118140
}

app/src/main/java/com/android/tvflix/home/ShowsAdapter.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ import com.bumptech.glide.request.RequestOptions
1616

1717
const val IS_FAVORITE = "IS_FAVORITE"
1818

19-
class ShowsAdapter constructor(
20-
private val callback: Callback
19+
class ShowsAdapter(
20+
private val callback: Callback,
21+
private val favoritesFeatureEnable: Boolean
2122
) : RecyclerView.Adapter<ShowsAdapter.ShowHolder>() {
2223
private var episodeViewDataList: MutableList<HomeViewData.EpisodeViewData> = mutableListOf()
2324

@@ -26,7 +27,7 @@ class ShowsAdapter constructor(
2627
val showListItemBinding = ShowListItemBinding
2728
.inflate(layoutInflater, parent, false)
2829
val showHolder = ShowHolder(showListItemBinding)
29-
showHolder.binding.showFavoriteIcon = true
30+
showHolder.binding.showFavoriteIcon = favoritesFeatureEnable
3031
showHolder.binding.favorite.setOnClickListener { onFavouriteIconClicked(showHolder.absoluteAdapterPosition) }
3132
return showHolder
3233
}
@@ -42,7 +43,8 @@ class ShowsAdapter constructor(
4243
if (position != RecyclerView.NO_POSITION) {
4344
val episodeViewData = episodeViewDataList[position]
4445
val isFavorite = episodeViewData.showViewData.isFavoriteShow
45-
val updatedShowViewData = episodeViewData.showViewData.copy(isFavoriteShow = !isFavorite)
46+
val updatedShowViewData =
47+
episodeViewData.showViewData.copy(isFavoriteShow = !isFavorite)
4648
val updatedEpisodeViewData = episodeViewData.copy(showViewData = updatedShowViewData)
4749
episodeViewDataList[position] = updatedEpisodeViewData
4850
notifyItemChanged(position)

0 commit comments

Comments
 (0)