Replace Timber with Logger
This commit is contained in:
@@ -10,7 +10,7 @@ import com.bumptech.glide.module.AppGlideModule
|
||||
import dagger.hilt.android.EntryPointAccessors.fromApplication
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import gq.kirmanak.mealient.di.GlideModuleEntryPoint
|
||||
import timber.log.Timber
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import java.io.InputStream
|
||||
|
||||
@GlideModule
|
||||
@@ -18,13 +18,13 @@ class MealieGlideModule : AppGlideModule() {
|
||||
|
||||
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
|
||||
super.registerComponents(context, glide, registry)
|
||||
Timber.v("registerComponents() called with: context = $context, glide = $glide, registry = $registry")
|
||||
getLogger(context).v { "registerComponents() called with: context = $context, glide = $glide, registry = $registry" }
|
||||
replaceOkHttp(context, registry)
|
||||
appendRecipeLoader(registry, context)
|
||||
}
|
||||
|
||||
private fun appendRecipeLoader(registry: Registry, context: Context) {
|
||||
Timber.v("appendRecipeLoader() called with: registry = $registry, context = $context")
|
||||
getLogger(context).v { "appendRecipeLoader() called with: registry = $registry, context = $context" }
|
||||
registry.append(
|
||||
RecipeSummaryEntity::class.java,
|
||||
InputStream::class.java,
|
||||
@@ -33,17 +33,15 @@ class MealieGlideModule : AppGlideModule() {
|
||||
}
|
||||
|
||||
private fun replaceOkHttp(context: Context, registry: Registry) {
|
||||
Timber.v("replaceOkHttp() called with: context = $context, registry = $registry")
|
||||
getLogger(context).v { "replaceOkHttp() called with: context = $context, registry = $registry" }
|
||||
val okHttp = getEntryPoint(context).provideOkHttp()
|
||||
registry.replace(
|
||||
GlideUrl::class.java,
|
||||
InputStream::class.java,
|
||||
OkHttpUrlLoader.Factory(okHttp)
|
||||
GlideUrl::class.java, InputStream::class.java, OkHttpUrlLoader.Factory(okHttp)
|
||||
)
|
||||
}
|
||||
|
||||
private fun getEntryPoint(context: Context): GlideModuleEntryPoint {
|
||||
Timber.v("getEntryPoint() called with: context = $context")
|
||||
return fromApplication(context, GlideModuleEntryPoint::class.java)
|
||||
}
|
||||
private fun getEntryPoint(context: Context): GlideModuleEntryPoint =
|
||||
fromApplication(context, GlideModuleEntryPoint::class.java)
|
||||
|
||||
private fun getLogger(context: Context): Logger = getEntryPoint(context).provideLogger()
|
||||
}
|
||||
|
||||
@@ -13,18 +13,23 @@ import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.databinding.MainActivityBinding
|
||||
import timber.log.Timber
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: MainActivityBinding
|
||||
private val viewModel by viewModels<MainActivityViewModel>()
|
||||
private val title: String by lazy { getString(R.string.app_name) }
|
||||
private val uiState: MainActivityUiState get() = viewModel.uiState
|
||||
|
||||
@Inject
|
||||
lateinit var logger: Logger
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Timber.v("onCreate() called with: savedInstanceState = $savedInstanceState")
|
||||
logger.v { "onCreate() called with: savedInstanceState = $savedInstanceState" }
|
||||
binding = MainActivityBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setSupportActionBar(binding.toolbar)
|
||||
@@ -36,7 +41,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun onNavigationItemSelected(menuItem: MenuItem): Boolean {
|
||||
Timber.v("onNavigationItemSelected() called with: menuItem = $menuItem")
|
||||
logger.v { "onNavigationItemSelected() called with: menuItem = $menuItem" }
|
||||
menuItem.isChecked = true
|
||||
val deepLink = when (menuItem.itemId) {
|
||||
R.id.add_recipe -> ADD_RECIPE_DEEP_LINK
|
||||
@@ -49,19 +54,19 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun onUiStateChange(uiState: MainActivityUiState) {
|
||||
Timber.v("onUiStateChange() called with: uiState = $uiState")
|
||||
logger.v { "onUiStateChange() called with: uiState = $uiState" }
|
||||
supportActionBar?.title = if (uiState.titleVisible) title else null
|
||||
binding.navigationView.isVisible = uiState.navigationVisible
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
|
||||
private fun setToolbarRoundCorner() {
|
||||
Timber.v("setToolbarRoundCorner() called")
|
||||
logger.v { "setToolbarRoundCorner() called" }
|
||||
val drawables = listOf(
|
||||
binding.toolbarHolder.background as? MaterialShapeDrawable,
|
||||
binding.toolbar.background as? MaterialShapeDrawable,
|
||||
)
|
||||
Timber.d("setToolbarRoundCorner: drawables = $drawables")
|
||||
logger.d { "setToolbarRoundCorner: drawables = $drawables" }
|
||||
val radius = resources.getDimension(R.dimen.main_activity_toolbar_corner_radius)
|
||||
for (drawable in drawables) {
|
||||
drawable?.apply {
|
||||
@@ -72,7 +77,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
Timber.v("onCreateOptionsMenu() called with: menu = $menu")
|
||||
logger.v { "onCreateOptionsMenu() called with: menu = $menu" }
|
||||
menuInflater.inflate(R.menu.main_toolbar, menu)
|
||||
menu.findItem(R.id.logout).isVisible = uiState.canShowLogout
|
||||
menu.findItem(R.id.login).isVisible = uiState.canShowLogin
|
||||
@@ -80,7 +85,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
Timber.v("onOptionsItemSelected() called with: item = $item")
|
||||
logger.v { "onOptionsItemSelected() called with: item = $item" }
|
||||
val result = when (item.itemId) {
|
||||
R.id.login -> {
|
||||
navigateDeepLink(AUTH_DEEP_LINK)
|
||||
@@ -96,7 +101,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun navigateDeepLink(deepLink: String) {
|
||||
Timber.v("navigateDeepLink() called with: deepLink = $deepLink")
|
||||
logger.v { "navigateDeepLink() called with: deepLink = $deepLink" }
|
||||
findNavController(binding.navHost.id).navigate(deepLink.toUri())
|
||||
}
|
||||
|
||||
|
||||
@@ -3,15 +3,16 @@ package gq.kirmanak.mealient.ui.activity
|
||||
import androidx.lifecycle.*
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class MainActivityViewModel @Inject constructor(
|
||||
private val authRepo: AuthRepo,
|
||||
private val logger: Logger,
|
||||
) : ViewModel() {
|
||||
|
||||
private val _uiState = MutableLiveData(MainActivityUiState())
|
||||
@@ -32,7 +33,7 @@ class MainActivityViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
fun logout() {
|
||||
Timber.v("logout() called")
|
||||
logger.v { "logout() called" }
|
||||
viewModelScope.launch { authRepo.logout() }
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,9 @@ import gq.kirmanak.mealient.databinding.FragmentAddRecipeBinding
|
||||
import gq.kirmanak.mealient.databinding.ViewSingleInputBinding
|
||||
import gq.kirmanak.mealient.extensions.checkIfInputIsEmpty
|
||||
import gq.kirmanak.mealient.extensions.collectWhenViewResumed
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.ui.activity.MainActivityViewModel
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AddRecipeFragment : Fragment(R.layout.fragment_add_recipe) {
|
||||
@@ -30,9 +31,12 @@ class AddRecipeFragment : Fragment(R.layout.fragment_add_recipe) {
|
||||
private val viewModel by viewModels<AddRecipeViewModel>()
|
||||
private val activityViewModel by activityViewModels<MainActivityViewModel>()
|
||||
|
||||
@Inject
|
||||
lateinit var logger: Logger
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
Timber.v("onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState")
|
||||
logger.v { "onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState" }
|
||||
activityViewModel.updateUiState {
|
||||
it.copy(loginButtonVisible = true, titleVisible = false, navigationVisible = true)
|
||||
}
|
||||
@@ -42,12 +46,12 @@ class AddRecipeFragment : Fragment(R.layout.fragment_add_recipe) {
|
||||
}
|
||||
|
||||
private fun observeAddRecipeResult() {
|
||||
Timber.v("observeAddRecipeResult() called")
|
||||
logger.v { "observeAddRecipeResult() called" }
|
||||
collectWhenViewResumed(viewModel.addRecipeResult, ::onRecipeSaveResult)
|
||||
}
|
||||
|
||||
private fun onRecipeSaveResult(isSuccessful: Boolean) = with(binding) {
|
||||
Timber.v("onRecipeSaveResult() called with: isSuccessful = $isSuccessful")
|
||||
logger.v { "onRecipeSaveResult() called with: isSuccessful = $isSuccessful" }
|
||||
|
||||
listOf(clearButton, saveRecipeButton).forEach { it.isEnabled = true }
|
||||
|
||||
@@ -60,12 +64,13 @@ class AddRecipeFragment : Fragment(R.layout.fragment_add_recipe) {
|
||||
}
|
||||
|
||||
private fun setupViews() = with(binding) {
|
||||
Timber.v("setupViews() called")
|
||||
logger.v { "setupViews() called" }
|
||||
saveRecipeButton.setOnClickListener {
|
||||
recipeNameInput.checkIfInputIsEmpty(
|
||||
inputLayout = recipeNameInputLayout,
|
||||
lifecycleOwner = viewLifecycleOwner,
|
||||
stringId = R.string.fragment_add_recipe_name_error
|
||||
stringId = R.string.fragment_add_recipe_name_error,
|
||||
logger = logger,
|
||||
) ?: return@setOnClickListener
|
||||
|
||||
listOf(saveRecipeButton, clearButton).forEach { it.isEnabled = false }
|
||||
@@ -98,7 +103,7 @@ class AddRecipeFragment : Fragment(R.layout.fragment_add_recipe) {
|
||||
}
|
||||
|
||||
private fun inflateInputRow(flow: Flow, @StringRes hintId: Int, text: String? = null) {
|
||||
Timber.v("inflateInputRow() called with: flow = $flow, hintId = $hintId, text = $text")
|
||||
logger.v { "inflateInputRow() called with: flow = $flow, hintId = $hintId, text = $text" }
|
||||
val fragmentRoot = binding.holder
|
||||
val inputBinding = ViewSingleInputBinding.inflate(layoutInflater, fragmentRoot, false)
|
||||
val root = inputBinding.root
|
||||
@@ -116,7 +121,7 @@ class AddRecipeFragment : Fragment(R.layout.fragment_add_recipe) {
|
||||
}
|
||||
|
||||
private fun saveValues() = with(binding) {
|
||||
Timber.v("saveValues() called")
|
||||
logger.v { "saveValues() called" }
|
||||
val instructions = parseInputRows(instructionsFlow).map { AddRecipeInstruction(text = it) }
|
||||
val ingredients = parseInputRows(ingredientsFlow).map { AddRecipeIngredient(note = it) }
|
||||
val settings = AddRecipeSettings(
|
||||
@@ -144,7 +149,7 @@ class AddRecipeFragment : Fragment(R.layout.fragment_add_recipe) {
|
||||
.toList()
|
||||
|
||||
private fun onSavedInputLoaded(request: AddRecipeRequest) = with(binding) {
|
||||
Timber.v("onSavedInputLoaded() called with: request = $request")
|
||||
logger.v { "onSavedInputLoaded() called with: request = $request" }
|
||||
recipeNameInput.setText(request.name)
|
||||
recipeDescriptionInput.setText(request.description)
|
||||
recipeYieldInput.setText(request.recipeYield)
|
||||
@@ -159,13 +164,13 @@ class AddRecipeFragment : Fragment(R.layout.fragment_add_recipe) {
|
||||
}
|
||||
|
||||
private fun Iterable<String>.showIn(flow: Flow, @StringRes hintId: Int) {
|
||||
Timber.v("showIn() called with: flow = $flow, hintId = $hintId")
|
||||
logger.v { "showIn() called with: flow = $flow, hintId = $hintId" }
|
||||
flow.removeAllViews()
|
||||
forEach { inflateInputRow(flow = flow, hintId = hintId, text = it) }
|
||||
}
|
||||
|
||||
private fun Flow.removeAllViews() {
|
||||
Timber.v("removeAllViews() called")
|
||||
logger.v { "removeAllViews() called" }
|
||||
for (id in referencedIds.iterator()) {
|
||||
val view = binding.holder.findViewById<View>(id) ?: continue
|
||||
removeView(view)
|
||||
|
||||
@@ -6,17 +6,18 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import gq.kirmanak.mealient.data.add.AddRecipeRepo
|
||||
import gq.kirmanak.mealient.data.add.models.AddRecipeRequest
|
||||
import gq.kirmanak.mealient.extensions.runCatchingExceptCancel
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class AddRecipeViewModel @Inject constructor(
|
||||
private val addRecipeRepo: AddRecipeRepo,
|
||||
private val logger: Logger,
|
||||
) : ViewModel() {
|
||||
|
||||
private val _addRecipeResultChannel = Channel<Boolean>(Channel.UNLIMITED)
|
||||
@@ -27,19 +28,19 @@ class AddRecipeViewModel @Inject constructor(
|
||||
get() = _preservedAddRecipeRequestChannel.receiveAsFlow()
|
||||
|
||||
fun loadPreservedRequest() {
|
||||
Timber.v("loadPreservedRequest() called")
|
||||
logger.v { "loadPreservedRequest() called" }
|
||||
viewModelScope.launch { doLoadPreservedRequest() }
|
||||
}
|
||||
|
||||
private suspend fun doLoadPreservedRequest() {
|
||||
Timber.v("doLoadPreservedRequest() called")
|
||||
logger.v { "doLoadPreservedRequest() called" }
|
||||
val request = addRecipeRepo.addRecipeRequestFlow.first()
|
||||
Timber.d("doLoadPreservedRequest: request = $request")
|
||||
logger.d { "doLoadPreservedRequest: request = $request" }
|
||||
_preservedAddRecipeRequestChannel.send(request)
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
Timber.v("clear() called")
|
||||
logger.v { "clear() called" }
|
||||
viewModelScope.launch {
|
||||
addRecipeRepo.clear()
|
||||
doLoadPreservedRequest()
|
||||
@@ -47,16 +48,16 @@ class AddRecipeViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
fun preserve(request: AddRecipeRequest) {
|
||||
Timber.v("preserve() called with: request = $request")
|
||||
logger.v { "preserve() called with: request = $request" }
|
||||
viewModelScope.launch { addRecipeRepo.preserve(request) }
|
||||
}
|
||||
|
||||
fun saveRecipe() {
|
||||
Timber.v("saveRecipe() called")
|
||||
logger.v { "saveRecipe() called" }
|
||||
viewModelScope.launch {
|
||||
val isSuccessful = runCatchingExceptCancel { addRecipeRepo.saveRecipe() }
|
||||
.fold(onSuccess = { true }, onFailure = { false })
|
||||
Timber.d("saveRecipe: isSuccessful = $isSuccessful")
|
||||
logger.d { "saveRecipe: isSuccessful = $isSuccessful" }
|
||||
_addRecipeResultChannel.send(isSuccessful)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,19 +12,24 @@ import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.data.network.NetworkError
|
||||
import gq.kirmanak.mealient.databinding.FragmentAuthenticationBinding
|
||||
import gq.kirmanak.mealient.extensions.checkIfInputIsEmpty
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.ui.OperationUiState
|
||||
import gq.kirmanak.mealient.ui.activity.MainActivityViewModel
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AuthenticationFragment : Fragment(R.layout.fragment_authentication) {
|
||||
|
||||
private val binding by viewBinding(FragmentAuthenticationBinding::bind)
|
||||
private val viewModel by viewModels<AuthenticationViewModel>()
|
||||
private val activityViewModel by activityViewModels<MainActivityViewModel>()
|
||||
|
||||
@Inject
|
||||
lateinit var logger: Logger
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
Timber.v("onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState")
|
||||
logger.v { "onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState" }
|
||||
binding.button.setOnClickListener { onLoginClicked() }
|
||||
activityViewModel.updateUiState {
|
||||
it.copy(loginButtonVisible = false, titleVisible = true, navigationVisible = false)
|
||||
@@ -33,12 +38,13 @@ class AuthenticationFragment : Fragment(R.layout.fragment_authentication) {
|
||||
}
|
||||
|
||||
private fun onLoginClicked(): Unit = with(binding) {
|
||||
Timber.v("onLoginClicked() called")
|
||||
logger.v { "onLoginClicked() called" }
|
||||
|
||||
val email: String = emailInput.checkIfInputIsEmpty(
|
||||
inputLayout = emailInputLayout,
|
||||
lifecycleOwner = viewLifecycleOwner,
|
||||
stringId = R.string.fragment_authentication_email_input_empty,
|
||||
logger = logger,
|
||||
) ?: return
|
||||
|
||||
val pass: String = passwordInput.checkIfInputIsEmpty(
|
||||
@@ -46,13 +52,14 @@ class AuthenticationFragment : Fragment(R.layout.fragment_authentication) {
|
||||
lifecycleOwner = viewLifecycleOwner,
|
||||
stringId = R.string.fragment_authentication_password_input_empty,
|
||||
trim = false,
|
||||
logger = logger,
|
||||
) ?: return
|
||||
|
||||
viewModel.authenticate(email, pass)
|
||||
}
|
||||
|
||||
private fun onUiStateChange(uiState: OperationUiState<Unit>) = with(binding) {
|
||||
Timber.v("onUiStateChange() called with: authUiState = $uiState")
|
||||
logger.v { "onUiStateChange() called with: authUiState = $uiState" }
|
||||
if (uiState.isSuccess) {
|
||||
findNavController().popBackStack()
|
||||
return
|
||||
|
||||
@@ -7,21 +7,22 @@ import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||
import gq.kirmanak.mealient.extensions.runCatchingExceptCancel
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.ui.OperationUiState
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class AuthenticationViewModel @Inject constructor(
|
||||
private val authRepo: AuthRepo,
|
||||
private val logger: Logger,
|
||||
) : ViewModel() {
|
||||
|
||||
private val _uiState = MutableLiveData<OperationUiState<Unit>>(OperationUiState.Initial())
|
||||
val uiState: LiveData<OperationUiState<Unit>> get() = _uiState
|
||||
|
||||
fun authenticate(email: String, password: String) {
|
||||
Timber.v("authenticate() called with: email = $email, password = $password")
|
||||
logger.v { "authenticate() called with: email = $email, password = $password" }
|
||||
_uiState.value = OperationUiState.Progress()
|
||||
viewModelScope.launch {
|
||||
val result = runCatchingExceptCancel { authRepo.authenticate(email, password) }
|
||||
|
||||
@@ -12,9 +12,10 @@ import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.data.network.NetworkError
|
||||
import gq.kirmanak.mealient.databinding.FragmentBaseUrlBinding
|
||||
import gq.kirmanak.mealient.extensions.checkIfInputIsEmpty
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.ui.OperationUiState
|
||||
import gq.kirmanak.mealient.ui.activity.MainActivityViewModel
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class BaseURLFragment : Fragment(R.layout.fragment_base_url) {
|
||||
@@ -23,9 +24,12 @@ class BaseURLFragment : Fragment(R.layout.fragment_base_url) {
|
||||
private val viewModel by viewModels<BaseURLViewModel>()
|
||||
private val activityViewModel by activityViewModels<MainActivityViewModel>()
|
||||
|
||||
@Inject
|
||||
lateinit var logger: Logger
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
Timber.v("onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState")
|
||||
logger.v { "onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState" }
|
||||
binding.button.setOnClickListener(::onProceedClick)
|
||||
viewModel.uiState.observe(viewLifecycleOwner, ::onUiStateChange)
|
||||
activityViewModel.updateUiState {
|
||||
@@ -34,17 +38,18 @@ class BaseURLFragment : Fragment(R.layout.fragment_base_url) {
|
||||
}
|
||||
|
||||
private fun onProceedClick(view: View) {
|
||||
Timber.v("onProceedClick() called with: view = $view")
|
||||
logger.v { "onProceedClick() called with: view = $view" }
|
||||
val url = binding.urlInput.checkIfInputIsEmpty(
|
||||
inputLayout = binding.urlInputLayout,
|
||||
lifecycleOwner = viewLifecycleOwner,
|
||||
stringId = R.string.fragment_baseurl_url_input_empty,
|
||||
logger = logger,
|
||||
) ?: return
|
||||
viewModel.saveBaseUrl(url)
|
||||
}
|
||||
|
||||
private fun onUiStateChange(uiState: OperationUiState<Unit>) = with(binding) {
|
||||
Timber.v("onUiStateChange() called with: uiState = $uiState")
|
||||
logger.v { "onUiStateChange() called with: uiState = $uiState" }
|
||||
if (uiState.isSuccess) {
|
||||
findNavController().navigate(BaseURLFragmentDirections.actionBaseURLFragmentToRecipesFragment())
|
||||
return
|
||||
|
||||
@@ -8,22 +8,23 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import gq.kirmanak.mealient.data.baseurl.BaseURLStorage
|
||||
import gq.kirmanak.mealient.data.baseurl.VersionDataSource
|
||||
import gq.kirmanak.mealient.extensions.runCatchingExceptCancel
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.ui.OperationUiState
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class BaseURLViewModel @Inject constructor(
|
||||
private val baseURLStorage: BaseURLStorage,
|
||||
private val versionDataSource: VersionDataSource,
|
||||
private val logger: Logger,
|
||||
) : ViewModel() {
|
||||
|
||||
private val _uiState = MutableLiveData<OperationUiState<Unit>>(OperationUiState.Initial())
|
||||
val uiState: LiveData<OperationUiState<Unit>> get() = _uiState
|
||||
|
||||
fun saveBaseUrl(baseURL: String) {
|
||||
Timber.v("saveBaseUrl() called with: baseURL = $baseURL")
|
||||
logger.v { "saveBaseUrl() called with: baseURL = $baseURL" }
|
||||
_uiState.value = OperationUiState.Progress()
|
||||
val hasPrefix = ALLOWED_PREFIXES.any { baseURL.startsWith(it) }
|
||||
val url = baseURL.takeIf { hasPrefix } ?: WITH_PREFIX_FORMAT.format(baseURL)
|
||||
@@ -31,13 +32,13 @@ class BaseURLViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
private suspend fun checkBaseURL(baseURL: String) {
|
||||
Timber.v("checkBaseURL() called with: baseURL = $baseURL")
|
||||
logger.v { "checkBaseURL() called with: baseURL = $baseURL" }
|
||||
val result = runCatchingExceptCancel {
|
||||
// If it returns proper version info then it must be a Mealie
|
||||
versionDataSource.getVersionInfo(baseURL)
|
||||
baseURLStorage.storeBaseURL(baseURL)
|
||||
}
|
||||
Timber.i("checkBaseURL: result is $result")
|
||||
logger.i { "checkBaseURL: result is $result" }
|
||||
_uiState.value = OperationUiState.fromResult(result)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,40 +10,45 @@ import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.databinding.FragmentDisclaimerBinding
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.ui.activity.MainActivityViewModel
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class DisclaimerFragment : Fragment(R.layout.fragment_disclaimer) {
|
||||
|
||||
private val binding by viewBinding(FragmentDisclaimerBinding::bind)
|
||||
private val viewModel by viewModels<DisclaimerViewModel>()
|
||||
private val activityViewModel by activityViewModels<MainActivityViewModel>()
|
||||
|
||||
@Inject
|
||||
lateinit var logger: Logger
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Timber.v("onCreate() called with: savedInstanceState = $savedInstanceState")
|
||||
logger.v { "onCreate() called with: savedInstanceState = $savedInstanceState" }
|
||||
viewModel.isAccepted.observe(this, ::onAcceptStateChange)
|
||||
}
|
||||
|
||||
private fun onAcceptStateChange(isAccepted: Boolean) {
|
||||
Timber.v("onAcceptStateChange() called with: isAccepted = $isAccepted")
|
||||
logger.v { "onAcceptStateChange() called with: isAccepted = $isAccepted" }
|
||||
if (isAccepted) navigateNext()
|
||||
}
|
||||
|
||||
private fun navigateNext() {
|
||||
Timber.v("navigateNext() called")
|
||||
logger.v { "navigateNext() called" }
|
||||
findNavController().navigate(DisclaimerFragmentDirections.actionDisclaimerFragmentToBaseURLFragment())
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
Timber.v("onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState")
|
||||
logger.v { "onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState" }
|
||||
binding.okay.setOnClickListener {
|
||||
Timber.v("onViewCreated: okay clicked")
|
||||
logger.v { "onViewCreated: okay clicked" }
|
||||
viewModel.acceptDisclaimer()
|
||||
}
|
||||
viewModel.okayCountDown.observe(viewLifecycleOwner) {
|
||||
Timber.d("onViewCreated: new count $it")
|
||||
logger.d { "onViewCreated: new count $it" }
|
||||
binding.okay.text = if (it > 0) resources.getQuantityString(
|
||||
R.plurals.fragment_disclaimer_button_okay_timer, it, it
|
||||
) else getString(R.string.fragment_disclaimer_button_okay)
|
||||
|
||||
@@ -4,19 +4,20 @@ import androidx.annotation.VisibleForTesting
|
||||
import androidx.lifecycle.*
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import gq.kirmanak.mealient.data.disclaimer.DisclaimerStorage
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class DisclaimerViewModel @Inject constructor(
|
||||
private val disclaimerStorage: DisclaimerStorage
|
||||
private val disclaimerStorage: DisclaimerStorage,
|
||||
private val logger: Logger,
|
||||
) : ViewModel() {
|
||||
|
||||
val isAccepted: LiveData<Boolean>
|
||||
@@ -26,12 +27,12 @@ class DisclaimerViewModel @Inject constructor(
|
||||
private var isCountDownStarted = false
|
||||
|
||||
fun acceptDisclaimer() {
|
||||
Timber.v("acceptDisclaimer() called")
|
||||
logger.v { "acceptDisclaimer() called" }
|
||||
viewModelScope.launch { disclaimerStorage.acceptDisclaimer() }
|
||||
}
|
||||
|
||||
fun startCountDown() {
|
||||
Timber.v("startCountDown() called")
|
||||
logger.v { "startCountDown() called" }
|
||||
if (isCountDownStarted) return
|
||||
isCountDownStarted = true
|
||||
tickerFlow(COUNT_DOWN_TICK_PERIOD_SEC.toLong(), TimeUnit.SECONDS)
|
||||
@@ -48,7 +49,7 @@ class DisclaimerViewModel @Inject constructor(
|
||||
*/
|
||||
@VisibleForTesting
|
||||
fun tickerFlow(period: Long, timeUnit: TimeUnit) = flow {
|
||||
Timber.v("tickerFlow() called with: period = $period, timeUnit = $timeUnit")
|
||||
logger.v { "tickerFlow() called with: period = $period, timeUnit = $timeUnit" }
|
||||
val periodMillis = timeUnit.toMillis(period)
|
||||
var counter = 0
|
||||
while (true) {
|
||||
|
||||
@@ -4,25 +4,42 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import gq.kirmanak.mealient.databinding.ViewHolderRecipeBinding
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.ui.recipes.images.RecipeImageLoader
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
class RecipeViewHolder(
|
||||
class RecipeViewHolder private constructor(
|
||||
private val logger: Logger,
|
||||
private val binding: ViewHolderRecipeBinding,
|
||||
private val recipeImageLoader: RecipeImageLoader,
|
||||
private val clickListener: (RecipeSummaryEntity) -> Unit,
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
@Singleton
|
||||
class Factory @Inject constructor(
|
||||
private val logger: Logger,
|
||||
) {
|
||||
|
||||
fun build(
|
||||
recipeImageLoader: RecipeImageLoader,
|
||||
binding: ViewHolderRecipeBinding,
|
||||
clickListener: (RecipeSummaryEntity) -> Unit,
|
||||
) = RecipeViewHolder(logger, binding, recipeImageLoader, clickListener)
|
||||
|
||||
}
|
||||
|
||||
private val loadingPlaceholder by lazy {
|
||||
binding.root.resources.getString(R.string.view_holder_recipe_text_placeholder)
|
||||
}
|
||||
|
||||
fun bind(item: RecipeSummaryEntity?) {
|
||||
Timber.v("bind() called with: item = $item")
|
||||
logger.v { "bind() called with: item = $item" }
|
||||
binding.name.text = item?.name ?: loadingPlaceholder
|
||||
recipeImageLoader.loadRecipeImage(binding.image, item)
|
||||
item?.let { entity ->
|
||||
binding.root.setOnClickListener {
|
||||
Timber.d("bind: item clicked $entity")
|
||||
logger.d { "bind: item clicked $entity" }
|
||||
clickListener(entity)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,27 +13,34 @@ import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import gq.kirmanak.mealient.databinding.FragmentRecipesBinding
|
||||
import gq.kirmanak.mealient.extensions.collectWhenViewResumed
|
||||
import gq.kirmanak.mealient.extensions.refreshRequestFlow
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.ui.activity.MainActivityViewModel
|
||||
import gq.kirmanak.mealient.ui.recipes.images.RecipeImageLoader
|
||||
import gq.kirmanak.mealient.ui.recipes.images.RecipePreloaderFactory
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class RecipesFragment : Fragment(R.layout.fragment_recipes) {
|
||||
|
||||
private val binding by viewBinding(FragmentRecipesBinding::bind)
|
||||
private val viewModel by viewModels<RecipeViewModel>()
|
||||
private val activityViewModel by activityViewModels<MainActivityViewModel>()
|
||||
|
||||
@Inject
|
||||
lateinit var logger: Logger
|
||||
|
||||
@Inject
|
||||
lateinit var recipeImageLoader: RecipeImageLoader
|
||||
|
||||
@Inject
|
||||
lateinit var recipePagingAdapterFactory: RecipesPagingAdapter.Factory
|
||||
|
||||
@Inject
|
||||
lateinit var recipePreloaderFactory: RecipePreloaderFactory
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
Timber.v("onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState")
|
||||
logger.v { "onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState" }
|
||||
activityViewModel.updateUiState {
|
||||
it.copy(loginButtonVisible = true, titleVisible = false, navigationVisible = true)
|
||||
}
|
||||
@@ -41,7 +48,7 @@ class RecipesFragment : Fragment(R.layout.fragment_recipes) {
|
||||
}
|
||||
|
||||
private fun navigateToRecipeInfo(recipeSummaryEntity: RecipeSummaryEntity) {
|
||||
Timber.v("navigateToRecipeInfo() called with: recipeSummaryEntity = $recipeSummaryEntity")
|
||||
logger.v { "navigateToRecipeInfo() called with: recipeSummaryEntity = $recipeSummaryEntity" }
|
||||
findNavController().navigate(
|
||||
RecipesFragmentDirections.actionRecipesFragmentToRecipeInfoFragment(
|
||||
recipeSlug = recipeSummaryEntity.slug,
|
||||
@@ -51,29 +58,32 @@ class RecipesFragment : Fragment(R.layout.fragment_recipes) {
|
||||
}
|
||||
|
||||
private fun setupRecipeAdapter() {
|
||||
Timber.v("setupRecipeAdapter() called")
|
||||
val recipesAdapter = RecipesPagingAdapter(recipeImageLoader, ::navigateToRecipeInfo)
|
||||
logger.v { "setupRecipeAdapter() called" }
|
||||
val recipesAdapter = recipePagingAdapterFactory.build(
|
||||
recipeImageLoader = recipeImageLoader,
|
||||
clickListener = ::navigateToRecipeInfo
|
||||
)
|
||||
with(binding.recipes) {
|
||||
adapter = recipesAdapter
|
||||
addOnScrollListener(recipePreloaderFactory.create(recipesAdapter))
|
||||
}
|
||||
collectWhenViewResumed(viewModel.pagingData) {
|
||||
Timber.v("setupRecipeAdapter: received data update")
|
||||
logger.v { "setupRecipeAdapter: received data update" }
|
||||
recipesAdapter.submitData(lifecycle, it)
|
||||
}
|
||||
collectWhenViewResumed(recipesAdapter.onPagesUpdatedFlow) {
|
||||
Timber.v("setupRecipeAdapter: pages updated")
|
||||
logger.v { "setupRecipeAdapter: pages updated" }
|
||||
binding.refresher.isRefreshing = false
|
||||
}
|
||||
collectWhenViewResumed(binding.refresher.refreshRequestFlow()) {
|
||||
Timber.v("setupRecipeAdapter: received refresh request")
|
||||
collectWhenViewResumed(binding.refresher.refreshRequestFlow(logger)) {
|
||||
logger.v { "setupRecipeAdapter: received refresh request" }
|
||||
recipesAdapter.refresh()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
Timber.v("onDestroyView() called")
|
||||
logger.v { "onDestroyView() called" }
|
||||
// Prevent RV leaking through mObservers list in adapter
|
||||
binding.recipes.adapter = null
|
||||
}
|
||||
|
||||
@@ -6,24 +6,40 @@ import androidx.paging.PagingDataAdapter
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import gq.kirmanak.mealient.databinding.ViewHolderRecipeBinding
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.ui.recipes.images.RecipeImageLoader
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
class RecipesPagingAdapter(
|
||||
class RecipesPagingAdapter private constructor(
|
||||
private val logger: Logger,
|
||||
private val recipeImageLoader: RecipeImageLoader,
|
||||
private val recipeViewHolderFactory: RecipeViewHolder.Factory,
|
||||
private val clickListener: (RecipeSummaryEntity) -> Unit
|
||||
) : PagingDataAdapter<RecipeSummaryEntity, RecipeViewHolder>(RecipeDiffCallback) {
|
||||
|
||||
@Singleton
|
||||
class Factory @Inject constructor(
|
||||
private val logger: Logger,
|
||||
private val recipeViewHolderFactory: RecipeViewHolder.Factory,
|
||||
) {
|
||||
|
||||
fun build(
|
||||
recipeImageLoader: RecipeImageLoader,
|
||||
clickListener: (RecipeSummaryEntity) -> Unit,
|
||||
) = RecipesPagingAdapter(logger, recipeImageLoader, recipeViewHolderFactory, clickListener)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecipeViewHolder, position: Int) {
|
||||
val item = getItem(position)
|
||||
holder.bind(item)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecipeViewHolder {
|
||||
Timber.v("onCreateViewHolder() called with: parent = $parent, viewType = $viewType")
|
||||
logger.v { "onCreateViewHolder() called with: parent = $parent, viewType = $viewType" }
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
val binding = ViewHolderRecipeBinding.inflate(inflater, parent, false)
|
||||
return RecipeViewHolder(binding, recipeImageLoader, clickListener)
|
||||
return recipeViewHolderFactory.build(recipeImageLoader, binding, clickListener)
|
||||
}
|
||||
|
||||
private object RecipeDiffCallback : DiffUtil.ItemCallback<RecipeSummaryEntity>() {
|
||||
|
||||
@@ -6,17 +6,18 @@ import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import dagger.hilt.android.scopes.FragmentScoped
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import timber.log.Timber
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import javax.inject.Inject
|
||||
|
||||
@FragmentScoped
|
||||
class RecipeImageLoaderImpl @Inject constructor(
|
||||
private val fragment: Fragment,
|
||||
private val requestOptions: RequestOptions,
|
||||
private val logger: Logger,
|
||||
) : RecipeImageLoader {
|
||||
|
||||
override fun loadRecipeImage(view: ImageView, recipe: RecipeSummaryEntity?) {
|
||||
Timber.v("loadRecipeImage() called with: view = $view, recipe = $recipe")
|
||||
logger.v { "loadRecipeImage() called with: view = $view, recipe = $recipe" }
|
||||
Glide.with(fragment).load(recipe).apply(requestOptions).into(view)
|
||||
}
|
||||
}
|
||||
@@ -7,16 +7,32 @@ import com.bumptech.glide.load.model.ModelLoader
|
||||
import com.bumptech.glide.load.model.stream.BaseGlideUrlLoader
|
||||
import gq.kirmanak.mealient.data.recipes.impl.RecipeImageUrlProvider
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import timber.log.Timber
|
||||
import java.io.InputStream
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
class RecipeModelLoader(
|
||||
class RecipeModelLoader private constructor(
|
||||
private val recipeImageUrlProvider: RecipeImageUrlProvider,
|
||||
private val logger: Logger,
|
||||
concreteLoader: ModelLoader<GlideUrl, InputStream>,
|
||||
cache: ModelCache<RecipeSummaryEntity, GlideUrl>,
|
||||
) : BaseGlideUrlLoader<RecipeSummaryEntity>(concreteLoader, cache) {
|
||||
|
||||
@Singleton
|
||||
class Factory @Inject constructor(
|
||||
private val recipeImageUrlProvider: RecipeImageUrlProvider,
|
||||
private val logger: Logger,
|
||||
) {
|
||||
|
||||
fun build(
|
||||
concreteLoader: ModelLoader<GlideUrl, InputStream>,
|
||||
cache: ModelCache<RecipeSummaryEntity, GlideUrl>,
|
||||
) = RecipeModelLoader(recipeImageUrlProvider, logger, concreteLoader, cache)
|
||||
|
||||
}
|
||||
|
||||
override fun handles(model: RecipeSummaryEntity): Boolean = true
|
||||
|
||||
override fun getUrl(
|
||||
@@ -25,7 +41,7 @@ class RecipeModelLoader(
|
||||
height: Int,
|
||||
options: Options?
|
||||
): String? {
|
||||
Timber.v("getUrl() called with: model = $model, width = $width, height = $height, options = $options")
|
||||
logger.v { "getUrl() called with: model = $model, width = $width, height = $height, options = $options" }
|
||||
return runBlocking { recipeImageUrlProvider.generateImageUrl(model?.slug) }
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,24 @@
|
||||
package gq.kirmanak.mealient.ui.recipes.images
|
||||
|
||||
import com.bumptech.glide.load.model.*
|
||||
import gq.kirmanak.mealient.data.recipes.impl.RecipeImageUrlProvider
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import timber.log.Timber
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import java.io.InputStream
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class RecipeModelLoaderFactory @Inject constructor(
|
||||
private val recipeImageUrlProvider: RecipeImageUrlProvider,
|
||||
private val recipeModelLoaderFactory: RecipeModelLoader.Factory,
|
||||
private val logger: Logger,
|
||||
) : ModelLoaderFactory<RecipeSummaryEntity, InputStream> {
|
||||
|
||||
private val cache = ModelCache<RecipeSummaryEntity, GlideUrl>()
|
||||
|
||||
override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader<RecipeSummaryEntity, InputStream> {
|
||||
Timber.v("build() called with: multiFactory = $multiFactory")
|
||||
logger.v { "build() called with: multiFactory = $multiFactory" }
|
||||
val concreteLoader = multiFactory.build(GlideUrl::class.java, InputStream::class.java)
|
||||
return RecipeModelLoader(recipeImageUrlProvider, concreteLoader, cache)
|
||||
return recipeModelLoaderFactory.build(concreteLoader, cache)
|
||||
}
|
||||
|
||||
override fun teardown() {
|
||||
|
||||
@@ -8,22 +8,23 @@ import com.bumptech.glide.RequestBuilder
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import dagger.hilt.android.scopes.FragmentScoped
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
|
||||
import timber.log.Timber
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import javax.inject.Inject
|
||||
|
||||
class RecipePreloadModelProvider(
|
||||
private val adapter: PagingDataAdapter<RecipeSummaryEntity, *>,
|
||||
private val fragment: Fragment,
|
||||
private val requestOptions: RequestOptions,
|
||||
private val logger: Logger,
|
||||
) : ListPreloader.PreloadModelProvider<RecipeSummaryEntity> {
|
||||
|
||||
override fun getPreloadItems(position: Int): List<RecipeSummaryEntity> {
|
||||
Timber.v("getPreloadItems() called with: position = $position")
|
||||
logger.v { "getPreloadItems() called with: position = $position" }
|
||||
return adapter.peek(position)?.let { listOf(it) } ?: emptyList()
|
||||
}
|
||||
|
||||
override fun getPreloadRequestBuilder(item: RecipeSummaryEntity): RequestBuilder<*> {
|
||||
Timber.v("getPreloadRequestBuilder() called with: item = $item")
|
||||
logger.v { "getPreloadRequestBuilder() called with: item = $item" }
|
||||
return Glide.with(fragment).load(item).apply(requestOptions)
|
||||
}
|
||||
|
||||
@@ -31,10 +32,11 @@ class RecipePreloadModelProvider(
|
||||
class Factory @Inject constructor(
|
||||
private val fragment: Fragment,
|
||||
private val requestOptions: RequestOptions,
|
||||
private val logger: Logger,
|
||||
) {
|
||||
|
||||
fun create(
|
||||
adapter: PagingDataAdapter<RecipeSummaryEntity, *>,
|
||||
) = RecipePreloadModelProvider(adapter, fragment, requestOptions)
|
||||
) = RecipePreloadModelProvider(adapter, fragment, requestOptions, logger)
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,8 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.databinding.FragmentRecipeInfoBinding
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.ui.recipes.images.RecipeImageLoader
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
@@ -24,8 +24,17 @@ class RecipeInfoFragment : BottomSheetDialogFragment() {
|
||||
private val binding by viewBinding(FragmentRecipeInfoBinding::bind)
|
||||
private val arguments by navArgs<RecipeInfoFragmentArgs>()
|
||||
private val viewModel by viewModels<RecipeInfoViewModel>()
|
||||
private val ingredientsAdapter = RecipeIngredientsAdapter()
|
||||
private val instructionsAdapter = RecipeInstructionsAdapter()
|
||||
private val ingredientsAdapter by lazy { recipeIngredientsAdapterFactory.build() }
|
||||
private val instructionsAdapter by lazy { recipeInstructionsAdapterFactory.build() }
|
||||
|
||||
@Inject
|
||||
lateinit var recipeInstructionsAdapterFactory: RecipeInstructionsAdapter.Factory
|
||||
|
||||
@Inject
|
||||
lateinit var recipeIngredientsAdapterFactory: RecipeIngredientsAdapter.Factory
|
||||
|
||||
@Inject
|
||||
lateinit var logger: Logger
|
||||
|
||||
@Inject
|
||||
lateinit var recipeImageLoader: RecipeImageLoader
|
||||
@@ -35,13 +44,13 @@ class RecipeInfoFragment : BottomSheetDialogFragment() {
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
Timber.v("onCreateView() called")
|
||||
logger.v { "onCreateView() called" }
|
||||
return FragmentRecipeInfoBinding.inflate(inflater, container, false).root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
Timber.v("onViewCreated() called")
|
||||
logger.v { "onViewCreated() called" }
|
||||
|
||||
with(binding) {
|
||||
ingredientsList.adapter = ingredientsAdapter
|
||||
@@ -55,7 +64,7 @@ class RecipeInfoFragment : BottomSheetDialogFragment() {
|
||||
}
|
||||
|
||||
private fun onUiStateChange(uiState: RecipeInfoUiState) = with(binding) {
|
||||
Timber.v("onUiStateChange() called")
|
||||
logger.v { "onUiStateChange() called" }
|
||||
ingredientsHolder.isVisible = uiState.areIngredientsVisible
|
||||
instructionsGroup.isVisible = uiState.areInstructionsVisible
|
||||
uiState.recipeInfo?.let {
|
||||
@@ -72,7 +81,7 @@ class RecipeInfoFragment : BottomSheetDialogFragment() {
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
Timber.v("onDestroyView() called")
|
||||
logger.v { "onDestroyView() called" }
|
||||
// Prevent RV leaking through mObservers list in adapter
|
||||
with(binding) {
|
||||
ingredientsList.adapter = null
|
||||
|
||||
@@ -7,32 +7,33 @@ import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import gq.kirmanak.mealient.data.recipes.RecipeRepo
|
||||
import gq.kirmanak.mealient.extensions.runCatchingExceptCancel
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class RecipeInfoViewModel @Inject constructor(
|
||||
private val recipeRepo: RecipeRepo,
|
||||
private val logger: Logger,
|
||||
) : ViewModel() {
|
||||
|
||||
private val _uiState = MutableLiveData(RecipeInfoUiState())
|
||||
val uiState: LiveData<RecipeInfoUiState> get() = _uiState
|
||||
|
||||
fun loadRecipeInfo(recipeId: Long, recipeSlug: String) {
|
||||
Timber.v("loadRecipeInfo() called with: recipeId = $recipeId, recipeSlug = $recipeSlug")
|
||||
logger.v { "loadRecipeInfo() called with: recipeId = $recipeId, recipeSlug = $recipeSlug" }
|
||||
_uiState.value = RecipeInfoUiState()
|
||||
viewModelScope.launch {
|
||||
runCatchingExceptCancel { recipeRepo.loadRecipeInfo(recipeId, recipeSlug) }
|
||||
.onSuccess {
|
||||
Timber.d("loadRecipeInfo: received recipe info = $it")
|
||||
logger.d { "loadRecipeInfo: received recipe info = $it" }
|
||||
_uiState.value = RecipeInfoUiState(
|
||||
areIngredientsVisible = it.recipeIngredients.isNotEmpty(),
|
||||
areInstructionsVisible = it.recipeInstructions.isNotEmpty(),
|
||||
recipeInfo = it,
|
||||
)
|
||||
}
|
||||
.onFailure { Timber.e(it, "loadRecipeInfo: can't load recipe info") }
|
||||
.onFailure { logger.e(it) { "loadRecipeInfo: can't load recipe info" } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,17 +7,40 @@ import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeIngredientEntity
|
||||
import gq.kirmanak.mealient.databinding.ViewHolderIngredientBinding
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.ui.recipes.info.RecipeIngredientsAdapter.RecipeIngredientViewHolder
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
class RecipeIngredientsAdapter :
|
||||
ListAdapter<RecipeIngredientEntity, RecipeIngredientViewHolder>(RecipeIngredientDiffCallback) {
|
||||
class RecipeIngredientsAdapter private constructor(
|
||||
private val recipeIngredientViewHolderFactory: RecipeIngredientViewHolder.Factory,
|
||||
private val logger: Logger,
|
||||
) : ListAdapter<RecipeIngredientEntity, RecipeIngredientViewHolder>(RecipeIngredientDiffCallback) {
|
||||
|
||||
class RecipeIngredientViewHolder(
|
||||
private val binding: ViewHolderIngredientBinding
|
||||
@Singleton
|
||||
class Factory @Inject constructor(
|
||||
private val recipeIngredientViewHolderFactory: RecipeIngredientViewHolder.Factory,
|
||||
private val logger: Logger,
|
||||
) {
|
||||
fun build() = RecipeIngredientsAdapter(recipeIngredientViewHolderFactory, logger)
|
||||
}
|
||||
|
||||
class RecipeIngredientViewHolder private constructor(
|
||||
private val binding: ViewHolderIngredientBinding,
|
||||
private val logger: Logger,
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
@Singleton
|
||||
class Factory @Inject constructor(
|
||||
private val logger: Logger,
|
||||
) {
|
||||
|
||||
fun build(binding: ViewHolderIngredientBinding) =
|
||||
RecipeIngredientViewHolder(binding, logger)
|
||||
}
|
||||
|
||||
fun bind(item: RecipeIngredientEntity) {
|
||||
Timber.v("bind() called with: item = $item")
|
||||
logger.v { "bind() called with: item = $item" }
|
||||
binding.checkBox.text = item.note
|
||||
}
|
||||
}
|
||||
@@ -35,17 +58,17 @@ class RecipeIngredientsAdapter :
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecipeIngredientViewHolder {
|
||||
Timber.v("onCreateViewHolder() called with: parent = $parent, viewType = $viewType")
|
||||
logger.v { "onCreateViewHolder() called with: parent = $parent, viewType = $viewType" }
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
return RecipeIngredientViewHolder(
|
||||
return recipeIngredientViewHolderFactory.build(
|
||||
ViewHolderIngredientBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecipeIngredientViewHolder, position: Int) {
|
||||
Timber.v("onBindViewHolder() called with: holder = $holder, position = $position")
|
||||
logger.v { "onBindViewHolder() called with: holder = $holder, position = $position" }
|
||||
val item = getItem(position)
|
||||
Timber.d("onBindViewHolder: item is $item")
|
||||
logger.d { "onBindViewHolder: item is $item" }
|
||||
holder.bind(item)
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,23 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.database.recipe.entity.RecipeInstructionEntity
|
||||
import gq.kirmanak.mealient.databinding.ViewHolderInstructionBinding
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import gq.kirmanak.mealient.ui.recipes.info.RecipeInstructionsAdapter.RecipeInstructionViewHolder
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
class RecipeInstructionsAdapter :
|
||||
ListAdapter<RecipeInstructionEntity, RecipeInstructionViewHolder>(RecipeInstructionDiffCallback) {
|
||||
class RecipeInstructionsAdapter private constructor(
|
||||
private val logger: Logger,
|
||||
private val recipeInstructionViewHolderFactory: RecipeInstructionViewHolder.Factory,
|
||||
) : ListAdapter<RecipeInstructionEntity, RecipeInstructionViewHolder>(RecipeInstructionDiffCallback) {
|
||||
|
||||
@Singleton
|
||||
class Factory @Inject constructor(
|
||||
private val logger: Logger,
|
||||
private val recipeInstructionViewHolderFactory: RecipeInstructionViewHolder.Factory,
|
||||
) {
|
||||
fun build() = RecipeInstructionsAdapter(logger, recipeInstructionViewHolderFactory)
|
||||
}
|
||||
|
||||
private object RecipeInstructionDiffCallback :
|
||||
DiffUtil.ItemCallback<RecipeInstructionEntity>() {
|
||||
@@ -27,11 +39,19 @@ class RecipeInstructionsAdapter :
|
||||
): Boolean = oldItem == newItem
|
||||
}
|
||||
|
||||
class RecipeInstructionViewHolder(
|
||||
private val binding: ViewHolderInstructionBinding
|
||||
class RecipeInstructionViewHolder private constructor(
|
||||
private val binding: ViewHolderInstructionBinding,
|
||||
private val logger: Logger,
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
@Singleton
|
||||
class Factory @Inject constructor(private val logger: Logger) {
|
||||
fun build(binding: ViewHolderInstructionBinding) =
|
||||
RecipeInstructionViewHolder(binding, logger)
|
||||
}
|
||||
|
||||
fun bind(item: RecipeInstructionEntity, position: Int) {
|
||||
Timber.v("bind() called with: item = $item, position = $position")
|
||||
logger.v { "bind() called with: item = $item, position = $position" }
|
||||
binding.step.text = binding.root.resources.getString(
|
||||
R.string.view_holder_recipe_instructions_step, position + 1
|
||||
)
|
||||
@@ -40,17 +60,17 @@ class RecipeInstructionsAdapter :
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecipeInstructionViewHolder {
|
||||
Timber.v("onCreateViewHolder() called with: parent = $parent, viewType = $viewType")
|
||||
logger.v { "onCreateViewHolder() called with: parent = $parent, viewType = $viewType" }
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
return RecipeInstructionViewHolder(
|
||||
ViewHolderInstructionBinding.inflate(inflater, parent, false)
|
||||
return recipeInstructionViewHolderFactory.build(
|
||||
ViewHolderInstructionBinding.inflate(inflater, parent, false),
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecipeInstructionViewHolder, position: Int) {
|
||||
Timber.v("onBindViewHolder() called with: holder = $holder, position = $position")
|
||||
logger.v { "onBindViewHolder() called with: holder = $holder, position = $position" }
|
||||
val item = getItem(position)
|
||||
Timber.d("onBindViewHolder: item is $item")
|
||||
logger.d { "onBindViewHolder: item is $item" }
|
||||
holder.bind(item, position)
|
||||
}
|
||||
}
|
||||
@@ -11,38 +11,43 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||
import gq.kirmanak.mealient.R
|
||||
import gq.kirmanak.mealient.extensions.setActionBarVisibility
|
||||
import gq.kirmanak.mealient.extensions.setSystemUiVisibility
|
||||
import timber.log.Timber
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SplashFragment : Fragment(R.layout.fragment_splash) {
|
||||
|
||||
private val viewModel by viewModels<SplashViewModel>()
|
||||
|
||||
@Inject
|
||||
lateinit var logger: Logger
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Timber.v("onCreate() called with: savedInstanceState = $savedInstanceState")
|
||||
logger.v { "onCreate() called with: savedInstanceState = $savedInstanceState" }
|
||||
viewModel.nextDestination.observe(this, ::onNextDestination)
|
||||
}
|
||||
|
||||
private fun onNextDestination(navDirections: NavDirections) {
|
||||
Timber.v("onNextDestination() called with: navDirections = $navDirections")
|
||||
logger.v { "onNextDestination() called with: navDirections = $navDirections" }
|
||||
findNavController().navigate(navDirections)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
Timber.v("onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState")
|
||||
logger.v { "onViewCreated() called with: view = $view, savedInstanceState = $savedInstanceState" }
|
||||
changeFullscreenState(true)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
Timber.v("onDestroyView() called")
|
||||
logger.v { "onDestroyView() called" }
|
||||
changeFullscreenState(false)
|
||||
}
|
||||
|
||||
private fun changeFullscreenState(isFullscreen: Boolean) {
|
||||
Timber.v("changeFullscreenState() called with: isFullscreen = $isFullscreen")
|
||||
(activity as? AppCompatActivity)?.setActionBarVisibility(!isFullscreen)
|
||||
activity?.setSystemUiVisibility(!isFullscreen)
|
||||
logger.v { "changeFullscreenState() called with: isFullscreen = $isFullscreen" }
|
||||
(activity as? AppCompatActivity)?.setActionBarVisibility(!isFullscreen, logger)
|
||||
activity?.setSystemUiVisibility(!isFullscreen, logger)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user