@@ -3,6 +3,8 @@ package com.programmersbox.otakumanager
3
3
import android.os.Bundle
4
4
import androidx.activity.ComponentActivity
5
5
import androidx.activity.compose.setContent
6
+ import androidx.annotation.StringRes
7
+ import androidx.compose.animation.ExperimentalAnimationApi
6
8
import androidx.compose.animation.animateColorAsState
7
9
import androidx.compose.foundation.ExperimentalFoundationApi
8
10
import androidx.compose.foundation.combinedClickable
@@ -14,31 +16,48 @@ import androidx.compose.foundation.lazy.GridCells
14
16
import androidx.compose.foundation.lazy.LazyRow
15
17
import androidx.compose.foundation.lazy.LazyVerticalGrid
16
18
import androidx.compose.foundation.lazy.items
17
- import androidx.compose.foundation.shape.RoundedCornerShape
18
19
import androidx.compose.foundation.text.KeyboardActions
19
20
import androidx.compose.foundation.text.KeyboardOptions
20
21
import androidx.compose.material.*
21
22
import androidx.compose.material.icons.Icons
23
+ import androidx.compose.material.icons.filled.Book
22
24
import androidx.compose.material.icons.filled.Cancel
25
+ import androidx.compose.material.icons.filled.Favorite
26
+ import androidx.compose.material.icons.filled.Menu
23
27
import androidx.compose.runtime.*
24
28
import androidx.compose.runtime.rxjava2.subscribeAsState
25
29
import androidx.compose.ui.Modifier
30
+ import androidx.compose.ui.graphics.vector.ImageVector
26
31
import androidx.compose.ui.platform.LocalFocusManager
32
+ import androidx.compose.ui.res.stringResource
27
33
import androidx.compose.ui.text.input.ImeAction
28
34
import androidx.compose.ui.tooling.preview.Preview
29
35
import androidx.compose.ui.unit.dp
36
+ import androidx.navigation.NavController
37
+ import androidx.navigation.NavDestination.Companion.hierarchy
38
+ import androidx.navigation.NavGraph.Companion.findStartDestination
39
+ import androidx.navigation.NavType
40
+ import androidx.navigation.compose.*
41
+ import com.google.accompanist.systemuicontroller.rememberSystemUiController
30
42
import com.google.android.material.composethemeadapter.MdcTheme
31
43
import com.google.android.material.dialog.MaterialAlertDialogBuilder
32
44
import com.programmersbox.favoritesdatabase.DbModel
45
+ import com.programmersbox.favoritesdatabase.toItemModel
46
+ import com.programmersbox.gsonutils.fromJson
47
+ import com.programmersbox.gsonutils.toJson
33
48
import com.programmersbox.models.ApiService
34
49
import com.programmersbox.otakumanager.ui.theme.OtakuWorldTheme
35
- import com.programmersbox.sharedutils.FirebaseAuthentication
50
+ import com.programmersbox.sharedutils.AppUpdate
51
+ import com.programmersbox.sharedutils.appUpdateCheck
36
52
import com.programmersbox.uiviews.GenericInfo
37
53
import com.programmersbox.uiviews.utils.ComposableUtils
38
54
import com.programmersbox.uiviews.utils.CoverCard
39
55
import com.programmersbox.uiviews.utils.CustomChip
40
56
import io.reactivex.Flowable
57
+ import io.reactivex.Single
41
58
import io.reactivex.android.schedulers.AndroidSchedulers
59
+ import io.reactivex.disposables.CompositeDisposable
60
+ import io.reactivex.rxkotlin.addTo
42
61
import io.reactivex.schedulers.Schedulers
43
62
import org.koin.android.ext.android.inject
44
63
import com.programmersbox.anime_sources.Sources as ASources
@@ -47,9 +66,13 @@ import com.programmersbox.novel_sources.Sources as NSources
47
66
48
67
class MainActivity : ComponentActivity () {
49
68
50
- private val animeListener = FirebaseDb2 (" favoriteShows" , " episodesWatched" , " animeworld" , " showUrl" , " numEpisodes" ).FirebaseListener ()
51
- private val mangaListener = FirebaseDb2 (" favoriteManga" , " chaptersRead" , " mangaworld" , " mangaUrl" , " chapterCount" ).FirebaseListener ()
52
- private val novelListener = FirebaseDb2 (" favoriteNovels" , " novelsChaptersRead" , " novelworld" , " novelUrl" , " novelNumChapters" ).FirebaseListener ()
69
+ private val animeFire = FirebaseDb2 (" favoriteShows" , " episodesWatched" , " animeworld" , " showUrl" , " numEpisodes" )
70
+ private val mangaFire = FirebaseDb2 (" favoriteManga" , " chaptersRead" , " mangaworld" , " mangaUrl" , " chapterCount" )
71
+ private val novelFire = FirebaseDb2 (" favoriteNovels" , " novelsChaptersRead" , " novelworld" , " novelUrl" , " novelNumChapters" )
72
+
73
+ private val animeListener = animeFire.FirebaseListener ()
74
+ private val mangaListener = mangaFire.FirebaseListener ()
75
+ private val novelListener = novelFire.FirebaseListener ()
53
76
54
77
private val genericInfo by inject<GenericInfo >()
55
78
@@ -63,16 +86,116 @@ class MainActivity : ComponentActivity() {
63
86
private val mangaSources = MSources .values().map { it.serviceName }
64
87
private val novelSources = NSources .values().map { it.serviceName }
65
88
89
+ sealed class Screen (val route : String , @StringRes val resourceId : Int , val icon : ImageVector ) {
90
+ object Favorites : Screen(" mainUi" , R .string.viewFavoritesMenu, Icons .Filled .Favorite )
91
+ object Settings : Screen(" settings" , R .string.settings, Icons .Filled .Menu )
92
+ object Details : Screen(" details?info={item}" , R .string.app_name, Icons .Filled .Book )
93
+ }
94
+
95
+ private val disposable = CompositeDisposable ()
96
+
97
+ @ExperimentalAnimationApi
66
98
@ExperimentalMaterialApi
67
99
@ExperimentalFoundationApi
68
100
override fun onCreate (savedInstanceState : Bundle ? ) {
69
101
super .onCreate(savedInstanceState)
70
102
103
+ Single .create<AppUpdate .AppUpdates > {
104
+ AppUpdate .getUpdate()?.let { d -> it.onSuccess(d) } ? : it.onError(Exception (" Something went wrong" ))
105
+ }
106
+ .subscribeOn(Schedulers .io())
107
+ .observeOn(AndroidSchedulers .mainThread())
108
+ .doOnError { }
109
+ .subscribe(appUpdateCheck::onNext)
110
+ .addTo(disposable)
111
+
112
+ val screenList = listOf (
113
+ Screen .Favorites ,
114
+ Screen .Settings
115
+ )
116
+
71
117
setContent {
72
118
MdcTheme {
73
119
// A surface container using the 'background' color from the theme
74
120
Surface (color = MaterialTheme .colors.background) {
75
- MainUi ()
121
+
122
+ val navController = rememberNavController()
123
+
124
+ Scaffold (
125
+ bottomBar = {
126
+ BottomNavigation {
127
+ val navBackStackEntry by navController.currentBackStackEntryAsState()
128
+ val currentDestination = navBackStackEntry?.destination
129
+ screenList.forEach { screen ->
130
+ BottomNavigationItem (
131
+ icon = { Icon (screen.icon, contentDescription = null ) },
132
+ label = { Text (stringResource(id = screen.resourceId)) },
133
+ selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true ,
134
+ onClick = {
135
+ navController.navigate(screen.route) {
136
+ // Pop up to the start destination of the graph to
137
+ // avoid building up a large stack of destinations
138
+ // on the back stack as users select items
139
+ popUpTo(navController.graph.findStartDestination().id) {
140
+ saveState = true
141
+ }
142
+ // Avoid multiple copies of the same destination when
143
+ // reselecting the same item
144
+ launchSingleTop = true
145
+ // Restore state when reselecting a previously selected item
146
+ restoreState = true
147
+ }
148
+ }
149
+ )
150
+ }
151
+ }
152
+ }
153
+ ) { p ->
154
+
155
+ NavHost (navController = navController, startDestination = Screen .Favorites .route, modifier = Modifier .padding(p)) {
156
+ composable(Screen .Favorites .route) { MainUi (navController) }
157
+ composable(Screen .Settings .route) { OtakuSettings (this @MainActivity, genericInfo) }
158
+ composable(
159
+ Screen .Details .route,
160
+ arguments = listOf (
161
+ navArgument(" item" ) {
162
+ type = NavType .StringType
163
+ }
164
+ )
165
+ ) {
166
+ val i = it.arguments?.getString(" item" )?.fromJson<DbModel >()
167
+ ?.let { genericInfo.toSource(it.source)?.let { it1 -> it.toItemModel(it1) } }
168
+ DetailsScreen (
169
+ info = i!! ,
170
+ logoId = when (i.source.serviceName) {
171
+ in animeSources -> R .drawable.animeworld_logo
172
+ in mangaSources -> R .drawable.mangaworld_logo
173
+ in novelSources -> R .drawable.novelworld_logo
174
+ else -> R .drawable.ic_launcher_foreground
175
+ },
176
+ firebase = when (i.source.serviceName) {
177
+ in animeSources -> animeFire
178
+ in mangaSources -> mangaFire
179
+ in novelSources -> novelFire
180
+ else -> null
181
+ },
182
+ itemListener = when (i.source.serviceName) {
183
+ in animeSources -> animeFire
184
+ in mangaSources -> mangaFire
185
+ in novelSources -> novelFire
186
+ else -> null
187
+ }?.copy()?.FirebaseListener (),
188
+ chapterListener = when (i.source.serviceName) {
189
+ in animeSources -> animeFire
190
+ in mangaSources -> mangaFire
191
+ in novelSources -> novelFire
192
+ else -> null
193
+ }?.copy()?.FirebaseListener (),
194
+ navController = navController
195
+ )
196
+ }
197
+ }
198
+ }
76
199
}
77
200
}
78
201
}
@@ -81,7 +204,10 @@ class MainActivity : ComponentActivity() {
81
204
@ExperimentalMaterialApi
82
205
@ExperimentalFoundationApi
83
206
@Composable
84
- fun MainUi () {
207
+ fun MainUi (navController : NavController ) {
208
+
209
+ val systemUi = rememberSystemUiController()
210
+ systemUi.setStatusBarColor(animateColorAsState(MaterialTheme .colors.primaryVariant).value)
85
211
86
212
val focusManager = LocalFocusManager .current
87
213
@@ -174,20 +300,7 @@ class MainActivity : ComponentActivity() {
174
300
}
175
301
},
176
302
bottomBar = {
177
- if (FirebaseAuthentication .currentUser == null ) {
178
- Button (
179
- onClick = {
180
- // if (FirebaseAuthentication.currentUser == null) {
181
- // TODO: if not logged in, show empty state for needing to login
182
- FirebaseAuthentication .signIn(this @MainActivity)
183
- // }
184
- },
185
- modifier = Modifier .fillMaxWidth(),
186
- shape = RoundedCornerShape (0f )
187
- ) {
188
- Text (" Login" )
189
- }
190
- }
303
+ // TODO: if not logged in, show empty state for needing to login
191
304
}
192
305
) {
193
306
@@ -213,16 +326,15 @@ class MainActivity : ComponentActivity() {
213
326
) {
214
327
215
328
if (info.value.size == 1 ) {
216
- // TODO: Open a modified details view here
217
- // val item = info.value.firstOrNull()?.let { genericInfo.toSource(it.source)?.let { it1 -> it.toItemModel(it1) } }
218
- // navController.navigate(FavoriteFragmentDirections.actionFavoriteFragmentToDetailsFragment(item))
329
+ val item = info.value.firstOrNull()
330
+ navController.navigate(MainActivity .Screen .Details .route.replace(" {item}" , item.toJson()))
219
331
} else {
220
332
MaterialAlertDialogBuilder (this @MainActivity)
221
333
.setTitle(R .string.chooseASource)
222
334
.setItems(info.value.map { " ${it.source} - ${it.title} " }.toTypedArray()) { d, i ->
223
- // val item = info.value[i].let { genericInfo.toSource(it.source)?.let { it1 -> it.toItemModel(it1) } }
224
- // navController.navigate(FavoriteFragmentDirections.actionFavoriteFragmentToDetailsFragment(item))
335
+ val item = info.value[i]
225
336
d.dismiss()
337
+ navController.navigate(MainActivity .Screen .Details .route.replace(" {item}" , item.toJson()))
226
338
}
227
339
.show()
228
340
}
@@ -240,6 +352,7 @@ class MainActivity : ComponentActivity() {
240
352
animeListener.unregister()
241
353
mangaListener.unregister()
242
354
novelListener.unregister()
355
+ disposable.dispose()
243
356
}
244
357
}
245
358
0 commit comments