Edge-to-edge ui

pull/26/head
Koitharu 5 years ago
parent b8d2fa69c4
commit 6463023736

@ -7,13 +7,18 @@ import android.view.View
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.graphics.Insets
import androidx.core.view.OnApplyWindowInsetsListener
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import org.koin.android.ext.android.get import org.koin.android.ext.android.get
import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
abstract class BaseActivity<B : ViewBinding> : AppCompatActivity() { abstract class BaseActivity<B : ViewBinding> : AppCompatActivity(), OnApplyWindowInsetsListener {
protected lateinit var binding: B protected lateinit var binding: B
private set private set
@ -23,6 +28,7 @@ abstract class BaseActivity<B : ViewBinding> : AppCompatActivity() {
setTheme(R.style.AppTheme_Amoled) setTheme(R.style.AppTheme_Amoled)
} }
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
} }
@Deprecated("Use ViewBinding", level = DeprecationLevel.ERROR) @Deprecated("Use ViewBinding", level = DeprecationLevel.ERROR)
@ -41,10 +47,12 @@ abstract class BaseActivity<B : ViewBinding> : AppCompatActivity() {
this.binding = binding this.binding = binding
super.setContentView(binding.root) super.setContentView(binding.root)
(binding.root.findViewById<View>(R.id.toolbar) as? Toolbar)?.let(this::setSupportActionBar) (binding.root.findViewById<View>(R.id.toolbar) as? Toolbar)?.let(this::setSupportActionBar)
ViewCompat.setOnApplyWindowInsetsListener(binding.root, this)
} }
private fun setupToolbar() { override fun onApplyWindowInsets(v: View, insets: WindowInsetsCompat): WindowInsetsCompat {
(findViewById<View>(R.id.toolbar) as? Toolbar)?.let(this::setSupportActionBar) onWindowInsetsChanged(insets.getInsets(WindowInsetsCompat.Type.systemBars()))
return insets
} }
override fun onOptionsItemSelected(item: MenuItem) = if (item.itemId == android.R.id.home) { override fun onOptionsItemSelected(item: MenuItem) = if (item.itemId == android.R.id.home) {
@ -59,4 +67,11 @@ abstract class BaseActivity<B : ViewBinding> : AppCompatActivity() {
} }
return super.onKeyDown(keyCode, event) return super.onKeyDown(keyCode, event)
} }
protected abstract fun onWindowInsetsChanged(insets: Insets)
private fun setupToolbar() {
(findViewById<View>(R.id.toolbar) as? Toolbar)?.let(this::setSupportActionBar)
}
} }

@ -5,10 +5,14 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.graphics.Insets
import androidx.core.view.OnApplyWindowInsetsListener
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
abstract class BaseFragment<B : ViewBinding> : Fragment() { abstract class BaseFragment<B : ViewBinding> : Fragment(), OnApplyWindowInsetsListener {
private var viewBinding: B? = null private var viewBinding: B? = null
@ -25,6 +29,11 @@ abstract class BaseFragment<B : ViewBinding> : Fragment() {
return binding.root return binding.root
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
ViewCompat.setOnApplyWindowInsetsListener(view, this)
}
override fun onDestroyView() { override fun onDestroyView() {
viewBinding = null viewBinding = null
super.onDestroyView() super.onDestroyView()
@ -39,7 +48,14 @@ abstract class BaseFragment<B : ViewBinding> : Fragment() {
} }
} }
override fun onApplyWindowInsets(v: View?, insets: WindowInsetsCompat): WindowInsetsCompat {
onWindowInsetsChanged(insets.getInsets(WindowInsetsCompat.Type.systemBars()))
return insets
}
protected fun bindingOrNull() = viewBinding protected fun bindingOrNull() = viewBinding
protected abstract fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): B protected abstract fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): B
protected abstract fun onWindowInsetsChanged(insets: Insets)
} }

@ -3,16 +3,22 @@ package org.koitharu.kotatsu.base.ui
import android.graphics.Color import android.graphics.Color
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.View
import android.view.WindowManager import android.view.WindowManager
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
abstract class BaseFullscreenActivity<B : ViewBinding> : BaseActivity<B>() { abstract class BaseFullscreenActivity<B : ViewBinding> : BaseActivity<B>() {
private lateinit var insetsControllerCompat: WindowInsetsControllerCompat
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
with(window) { with(window) {
insetsControllerCompat = WindowInsetsControllerCompat(this, decorView)
insetsControllerCompat.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
statusBarColor = Color.TRANSPARENT statusBarColor = Color.TRANSPARENT
navigationBarColor = Color.TRANSPARENT navigationBarColor = Color.TRANSPARENT
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
@ -24,20 +30,11 @@ abstract class BaseFullscreenActivity<B : ViewBinding> : BaseActivity<B>() {
} }
protected fun hideSystemUI() { protected fun hideSystemUI() {
window.decorView.systemUiVisibility = ( insetsControllerCompat.hide(WindowInsetsCompat.Type.systemBars())
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // прячем панель навигации
or View.SYSTEM_UI_FLAG_FULLSCREEN // прячем строку состояния
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
)
} }
protected fun showSystemUI() { protected fun showSystemUI() {
window.decorView.systemUiVisibility = insetsControllerCompat.show(WindowInsetsCompat.Type.systemBars())
(View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
} }
} }

@ -1,17 +1,39 @@
package org.koitharu.kotatsu.base.ui package org.koitharu.kotatsu.base.ui
import android.os.Bundle
import android.view.View
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.core.view.OnApplyWindowInsetsListener
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
abstract class BasePreferenceFragment(@StringRes private val titleId: Int) : abstract class BasePreferenceFragment(@StringRes private val titleId: Int) :
PreferenceFragmentCompat() { PreferenceFragmentCompat(), OnApplyWindowInsetsListener {
protected val settings by inject<AppSettings>() protected val settings by inject<AppSettings>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
listView.clipToPadding = false
ViewCompat.setOnApplyWindowInsetsListener(view, this)
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
activity?.setTitle(titleId) activity?.setTitle(titleId)
} }
override fun onApplyWindowInsets(v: View?, insets: WindowInsetsCompat): WindowInsetsCompat {
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
listView.updatePadding(
left = systemBars.left,
right = systemBars.right,
bottom = systemBars.bottom
)
return WindowInsetsCompat.CONSUMED
}
} }

@ -8,7 +8,9 @@ import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import androidx.core.graphics.Insets
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.base.ui.BaseActivity
import org.koitharu.kotatsu.databinding.ActivityBrowserBinding import org.koitharu.kotatsu.databinding.ActivityBrowserBinding
@ -85,6 +87,11 @@ class BrowserActivity : BaseActivity<ActivityBrowserBinding>(), BrowserCallback
supportActionBar?.subtitle = subtitle supportActionBar?.subtitle = subtitle
} }
override fun onWindowInsetsChanged(insets: Insets) {
binding.appbar.updatePadding(top = insets.top)
binding.webView.updatePadding(bottom = insets.bottom)
}
companion object { companion object {
fun newIntent(context: Context, url: String) = Intent(context, BrowserActivity::class.java) fun newIntent(context: Context, url: String) = Intent(context, BrowserActivity::class.java)

@ -5,7 +5,9 @@ import android.os.Bundle
import android.view.* import android.view.*
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.core.graphics.Insets
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.koin.androidx.viewmodel.ext.android.sharedViewModel import org.koin.androidx.viewmodel.ext.android.sharedViewModel
@ -149,4 +151,12 @@ class ChaptersFragment : BaseFragment<FragmentChaptersBinding>(),
binding.recyclerViewChapters.invalidateItemDecorations() binding.recyclerViewChapters.invalidateItemDecorations()
actionMode = null actionMode = null
} }
override fun onWindowInsetsChanged(insets: Insets) {
binding.recyclerViewChapters.updatePadding(
left = insets.left,
right = insets.right,
bottom = insets.bottom
)
}
} }

@ -9,9 +9,12 @@ import android.view.MenuItem
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.Insets
import androidx.core.net.toFile import androidx.core.net.toFile
import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
@ -77,6 +80,20 @@ class DetailsActivity : BaseActivity<ActivityDetailsBinding>(),
} }
} }
override fun onWindowInsetsChanged(insets: Insets) {
binding.toolbar.updatePadding(
top = insets.top,
left = insets.left,
right = insets.right
)
if (binding.tabs.parent !is Toolbar) {
binding.tabs.updatePadding(
left = insets.left,
right = insets.right
)
}
}
private fun onNewChaptersChanged(newChapters: Int) { private fun onNewChaptersChanged(newChapters: Int) {
val tab = binding.tabs.getTabAt(1) ?: return val tab = binding.tabs.getTabAt(1) ?: return
if (newChapters == 0) { if (newChapters == 0) {

@ -5,9 +5,11 @@ import android.text.Spanned
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.graphics.Insets
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.text.parseAsHtml import androidx.core.text.parseAsHtml
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import coil.ImageLoader import coil.ImageLoader
import coil.util.CoilUtils import coil.util.CoilUtils
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -174,4 +176,12 @@ class DetailsFragment : BaseFragment<FragmentDetailsBinding>(), View.OnClickList
else -> return false else -> return false
} }
} }
override fun onWindowInsetsChanged(insets: Insets) {
binding.root.updatePadding(
left = insets.left,
right = insets.right,
bottom = insets.bottom
)
}
} }

@ -3,6 +3,8 @@ package org.koitharu.kotatsu.favourites.ui
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.* import android.view.*
import androidx.core.graphics.Insets
import androidx.core.view.updatePadding
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
@ -66,6 +68,13 @@ class FavouritesContainerFragment : BaseFragment<FragmentFavouritesBinding>(),
outState.putParcelable(KEY_ADAPTER_STATE, adapterState) outState.putParcelable(KEY_ADAPTER_STATE, adapterState)
} }
override fun onWindowInsetsChanged(insets: Insets) {
binding.tabs.updatePadding(
left = insets.left,
right = insets.right
)
}
private fun onCategoriesChanged(categories: List<FavouriteCategory>) { private fun onCategoriesChanged(categories: List<FavouriteCategory>) {
val data = ArrayList<FavouriteCategory>(categories.size + 1) val data = ArrayList<FavouriteCategory>(categories.size + 1)
data += FavouriteCategory(0L, getString(R.string.all_favourites), -1, Date()) data += FavouriteCategory(0L, getString(R.string.all_favourites), -1, Date())

@ -6,7 +6,11 @@ import android.content.res.ColorStateList
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.core.graphics.Insets
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -70,6 +74,24 @@ class CategoriesActivity : BaseActivity<ActivityCategoriesBinding>(),
return true return true
} }
override fun onWindowInsetsChanged(insets: Insets) {
binding.fabAdd.updateLayoutParams<ViewGroup.MarginLayoutParams> {
rightMargin = topMargin + insets.right
leftMargin = topMargin + insets.left
bottomMargin = topMargin + insets.bottom
}
binding.recyclerView.updatePadding(
left = insets.left,
right = insets.right,
bottom = insets.bottom
)
binding.toolbar.updatePadding(
left = insets.left,
right = insets.right,
top = insets.top
)
}
private fun onCategoriesChanged(categories: List<FavouriteCategory>) { private fun onCategoriesChanged(categories: List<FavouriteCategory>) {
// TODO check if not moved // TODO check if not moved

@ -6,6 +6,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.SeekBar import android.widget.SeekBar
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
@ -44,6 +45,8 @@ class ListModeSelectDialog : AlertDialogFragment<DialogListModeBinding>(), View.
binding.buttonList.isChecked = mode == ListMode.LIST binding.buttonList.isChecked = mode == ListMode.LIST
binding.buttonListDetailed.isChecked = mode == ListMode.DETAILED_LIST binding.buttonListDetailed.isChecked = mode == ListMode.DETAILED_LIST
binding.buttonGrid.isChecked = mode == ListMode.GRID binding.buttonGrid.isChecked = mode == ListMode.GRID
binding.textViewGridTitle.isVisible = mode == ListMode.GRID
binding.seekbarGrid.isVisible = mode == ListMode.GRID
with(binding.seekbarGrid) { with(binding.seekbarGrid) {
progress = pendingGridSize - 50 progress = pendingGridSize - 50
@ -71,6 +74,8 @@ class ListModeSelectDialog : AlertDialogFragment<DialogListModeBinding>(), View.
R.id.button_list_detailed -> mode = ListMode.DETAILED_LIST R.id.button_list_detailed -> mode = ListMode.DETAILED_LIST
R.id.button_grid -> mode = ListMode.GRID R.id.button_grid -> mode = ListMode.GRID
} }
binding.textViewGridTitle.isVisible = mode == ListMode.GRID
binding.seekbarGrid.isVisible = mode == ListMode.GRID
settings.listMode = mode settings.listMode = mode
} }

@ -4,9 +4,11 @@ import android.os.Bundle
import android.view.* import android.view.*
import androidx.annotation.CallSuper import androidx.annotation.CallSuper
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.graphics.Insets
import androidx.core.view.GravityCompat import androidx.core.view.GravityCompat
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
@ -195,6 +197,19 @@ abstract class MangaListFragment : BaseFragment<FragmentListBinding>(),
binding.drawer?.closeDrawers() binding.drawer?.closeDrawers()
} }
override fun onWindowInsetsChanged(insets: Insets) {
binding.recyclerView.updatePadding(
bottom = insets.bottom
)
binding.recyclerViewFilter.updatePadding(
bottom = insets.bottom
)
binding.root.updatePadding(
left = insets.left,
right = insets.right
)
}
private fun onGridScaleChanged(scale: Float) { private fun onGridScaleChanged(scale: Float) {
spanSizeLookup.invalidateCache() spanSizeLookup.invalidateCache()
spanResolver.setGridSize(scale, binding.recyclerView) spanResolver.setGridSize(scale, binding.recyclerView)

@ -3,10 +3,12 @@ package org.koitharu.kotatsu.main
import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module import org.koin.dsl.module
import org.koitharu.kotatsu.main.ui.MainViewModel import org.koitharu.kotatsu.main.ui.MainViewModel
import org.koitharu.kotatsu.main.ui.protect.AppProtectHelper
import org.koitharu.kotatsu.main.ui.protect.ProtectViewModel import org.koitharu.kotatsu.main.ui.protect.ProtectViewModel
val mainModule val mainModule
get() = module { get() = module {
single { AppProtectHelper(get()) }
viewModel { MainViewModel(get(), get()) } viewModel { MainViewModel(get(), get()) }
viewModel { ProtectViewModel(get()) } viewModel { ProtectViewModel(get()) }
} }

@ -9,12 +9,15 @@ import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.core.graphics.Insets
import androidx.core.view.* import androidx.core.view.*
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.swiperefreshlayout.widget.CircularProgressDrawable import androidx.swiperefreshlayout.widget.CircularProgressDrawable
import com.google.android.material.navigation.NavigationView import com.google.android.material.navigation.NavigationView
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.base.ui.BaseActivity
@ -42,6 +45,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(),
View.OnClickListener { View.OnClickListener {
private val viewModel by viewModel<MainViewModel>() private val viewModel by viewModel<MainViewModel>()
private val protectHelper by inject<AppProtectHelper>()
private lateinit var drawerToggle: ActionBarDrawerToggle private lateinit var drawerToggle: ActionBarDrawerToggle
private var closeable: Closeable? = null private var closeable: Closeable? = null
@ -72,7 +76,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>(),
} ?: run { } ?: run {
openDefaultSection() openDefaultSection()
} }
if (AppProtectHelper.check(this)) { if (protectHelper.check(this)) {
finish()
return return
} }
TrackWorker.setup(applicationContext) TrackWorker.setup(applicationContext)
@ -86,7 +91,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(),
override fun onDestroy() { override fun onDestroy() {
closeable?.close() closeable?.close()
AppProtectHelper.lock() protectHelper.lock()
super.onDestroy() super.onDestroy()
} }
@ -159,6 +164,19 @@ class MainActivity : BaseActivity<ActivityMainBinding>(),
return true return true
} }
override fun onWindowInsetsChanged(insets: Insets) {
binding.toolbar.updatePadding(
top = insets.top,
left = insets.left,
right = insets.right
)
binding.fab.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = insets.bottom + topMargin
leftMargin = insets.left + topMargin
rightMargin = insets.right + topMargin
}
}
private fun onOpenReader(manga: Manga) { private fun onOpenReader(manga: Manga) {
val options = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val options = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
ActivityOptions.makeClipRevealAnimation( ActivityOptions.makeClipRevealAnimation(

@ -2,22 +2,18 @@ package org.koitharu.kotatsu.main.ui.protect
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.main.ui.MainActivity import org.koitharu.kotatsu.main.ui.MainActivity
@Deprecated("TODO not object") class AppProtectHelper(private val settings: AppSettings) {
object AppProtectHelper : KoinComponent {
val settings by inject<AppSettings>()
private var isUnlocked = settings.appPassword.isNullOrEmpty() private var isUnlocked = settings.appPassword.isNullOrEmpty()
fun unlock(activity: Activity) { fun unlock(activity: Activity) {
isUnlocked = true isUnlocked = true
with(activity) { with(activity) {
startActivity(Intent(this, MainActivity::class.java)) startActivity(Intent(this, MainActivity::class.java)
finishAfterTransition() .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP))
} }
} }
@ -27,10 +23,8 @@ object AppProtectHelper : KoinComponent {
fun check(activity: Activity): Boolean { fun check(activity: Activity): Boolean {
return if (!isUnlocked) { return if (!isUnlocked) {
with(activity) { activity.startActivity(ProtectActivity.newIntent(activity)
startActivity(ProtectActivity.newIntent(this)) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP))
finishAfterTransition()
}
true true
} else { } else {
false false

@ -10,6 +10,9 @@ import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import android.widget.TextView import android.widget.TextView
import androidx.core.graphics.Insets
import androidx.core.view.updatePadding
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.base.ui.BaseActivity
@ -20,6 +23,7 @@ class ProtectActivity : BaseActivity<ActivityProtectBinding>(), TextView.OnEdito
TextWatcher { TextWatcher {
private val viewModel by viewModel<ProtectViewModel>() private val viewModel by viewModel<ProtectViewModel>()
private val appProtectHelper by inject<AppProtectHelper>()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -49,6 +53,14 @@ class ProtectActivity : BaseActivity<ActivityProtectBinding>(), TextView.OnEdito
else -> super.onOptionsItemSelected(item) else -> super.onOptionsItemSelected(item)
} }
override fun onWindowInsetsChanged(insets: Insets) {
binding.toolbar.updatePadding(
left = insets.left,
right = insets.right,
top = insets.top
)
}
override fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?): Boolean { override fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?): Boolean {
return if (actionId == EditorInfo.IME_ACTION_DONE) { return if (actionId == EditorInfo.IME_ACTION_DONE) {
viewModel.tryUnlock(binding.editPassword.text.toString().orEmpty()) viewModel.tryUnlock(binding.editPassword.text.toString().orEmpty())
@ -67,7 +79,7 @@ class ProtectActivity : BaseActivity<ActivityProtectBinding>(), TextView.OnEdito
} }
private fun onUnlockSuccess(unit: Unit) { private fun onUnlockSuccess(unit: Unit) {
AppProtectHelper.unlock(this) appProtectHelper.unlock(this)
} }
private fun onError(e: Throwable) { private fun onError(e: Throwable) {

@ -54,8 +54,7 @@ import org.koitharu.kotatsu.utils.ext.showAnimated
class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(), class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
ChaptersDialog.OnChapterChangeListener, ChaptersDialog.OnChapterChangeListener,
GridTouchHelper.OnGridTouchListener, OnPageSelectListener, ReaderConfigDialog.Callback, GridTouchHelper.OnGridTouchListener, OnPageSelectListener, ReaderConfigDialog.Callback,
ActivityResultCallback<Boolean>, OnApplyWindowInsetsListener, ActivityResultCallback<Boolean>, ReaderControlDelegate.OnInteractionListener {
ReaderControlDelegate.OnInteractionListener {
private val viewModel by viewModel<ReaderViewModel> { private val viewModel by viewModel<ReaderViewModel> {
parametersOf(MangaIntent.from(intent), intent?.getParcelableExtra<ReaderState>(EXTRA_STATE)) parametersOf(MangaIntent.from(intent), intent?.getParcelableExtra<ReaderState>(EXTRA_STATE))
@ -64,6 +63,7 @@ class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
private lateinit var touchHelper: GridTouchHelper private lateinit var touchHelper: GridTouchHelper
private lateinit var orientationHelper: ScreenOrientationHelper private lateinit var orientationHelper: ScreenOrientationHelper
private lateinit var controlDelegate: ReaderControlDelegate private lateinit var controlDelegate: ReaderControlDelegate
private var gestureInsets: Insets = Insets.NONE
private val reader private val reader
get() = supportFragmentManager.findFragmentById(R.id.container) as? BaseReader<*> get() = supportFragmentManager.findFragmentById(R.id.container) as? BaseReader<*>
@ -78,8 +78,6 @@ class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
binding.toolbarBottom.inflateMenu(R.menu.opt_reader_bottom) binding.toolbarBottom.inflateMenu(R.menu.opt_reader_bottom)
binding.toolbarBottom.setOnMenuItemClickListener(::onOptionsItemSelected) binding.toolbarBottom.setOnMenuItemClickListener(::onOptionsItemSelected)
ViewCompat.setOnApplyWindowInsetsListener(binding.rootLayout, this)
orientationHelper.observeAutoOrientation() orientationHelper.observeAutoOrientation()
.onEach { .onEach {
binding.toolbarBottom.menu.findItem(R.id.action_screen_rotate).isVisible = !it binding.toolbarBottom.menu.findItem(R.id.action_screen_rotate).isVisible = !it
@ -230,12 +228,17 @@ class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
} }
override fun onProcessTouch(rawX: Int, rawY: Int): Boolean { override fun onProcessTouch(rawX: Int, rawY: Int): Boolean {
return if (binding.appbarTop.hasGlobalPoint(rawX, rawY) return if (
|| binding.appbarBottom.hasGlobalPoint(rawX, rawY) rawX <= gestureInsets.left ||
rawY <= gestureInsets.top ||
rawX >= binding.root.width - gestureInsets.right ||
rawY >= binding.root.height - gestureInsets.bottom ||
binding.appbarTop.hasGlobalPoint(rawX, rawY) ||
binding.appbarBottom.hasGlobalPoint(rawX, rawY)
) { ) {
false false
} else { } else {
val targets = binding.rootLayout.hitTest(rawX, rawY) val targets = binding.root.hitTest(rawX, rawY)
targets.none { it.hasOnClickListeners() } targets.none { it.hasOnClickListeners() }
} }
} }
@ -273,6 +276,8 @@ class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
viewModel.switchMode(mode) viewModel.switchMode(mode)
} }
override fun onWindowInsetsChanged(insets: Insets) = Unit
private fun onPageSaved(uri: Uri?) { private fun onPageSaved(uri: Uri?) {
if (uri != null) { if (uri != null) {
Snackbar.make(binding.container, R.string.page_saved, Snackbar.LENGTH_LONG) Snackbar.make(binding.container, R.string.page_saved, Snackbar.LENGTH_LONG)
@ -308,6 +313,7 @@ class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
} }
override fun onApplyWindowInsets(v: View, insets: WindowInsetsCompat): WindowInsetsCompat { override fun onApplyWindowInsets(v: View, insets: WindowInsetsCompat): WindowInsetsCompat {
gestureInsets = insets.getInsets(WindowInsetsCompat.Type.systemGestures())
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
binding.appbarTop.updatePadding( binding.appbarTop.updatePadding(
top = systemBars.top, top = systemBars.top,

@ -3,6 +3,8 @@ package org.koitharu.kotatsu.reader.ui
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.core.graphics.Insets
import androidx.core.view.updatePadding
import androidx.fragment.app.commit import androidx.fragment.app.commit
import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
@ -29,6 +31,14 @@ class SimpleSettingsActivity : BaseActivity<ActivitySettingsSimpleBinding>() {
} }
} }
override fun onWindowInsetsChanged(insets: Insets) {
binding.toolbar.updatePadding(
top = insets.top,
left = insets.left,
right = insets.right
)
}
companion object { companion object {
private const val ACTION_READER = private const val ACTION_READER =

@ -2,6 +2,7 @@ package org.koitharu.kotatsu.reader.ui.pager
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.core.graphics.Insets
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import org.koin.android.ext.android.get import org.koin.android.ext.android.get
@ -42,6 +43,8 @@ abstract class BaseReader<B : ViewBinding> : BaseFragment<B>() {
outState.putParcelable(KEY_STATE, lastReaderState) outState.putParcelable(KEY_STATE, lastReaderState)
} }
override fun onWindowInsetsChanged(insets: Insets) = Unit
abstract fun switchPageBy(delta: Int) abstract fun switchPageBy(delta: Int)
abstract fun switchPageTo(position: Int, smooth: Boolean) abstract fun switchPageTo(position: Int, smooth: Boolean)

@ -5,6 +5,8 @@ import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.core.graphics.Insets
import androidx.core.view.updatePadding
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.base.ui.BaseActivity
import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.model.MangaSource
@ -44,6 +46,14 @@ class SearchActivity : BaseActivity<ActivitySearchBinding>(), SearchView.OnQuery
super.onDestroy() super.onDestroy()
} }
override fun onWindowInsetsChanged(insets: Insets) {
binding.toolbar.updatePadding(
top = insets.top,
left = insets.left,
right = insets.right
)
}
override fun onQueryTextSubmit(query: String?): Boolean { override fun onQueryTextSubmit(query: String?): Boolean {
return if (!query.isNullOrBlank()) { return if (!query.isNullOrBlank()) {
title = query title = query

@ -3,6 +3,8 @@ package org.koitharu.kotatsu.search.ui.global
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.core.graphics.Insets
import androidx.core.view.updatePadding
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.base.ui.BaseActivity
import org.koitharu.kotatsu.databinding.ActivitySearchGlobalBinding import org.koitharu.kotatsu.databinding.ActivitySearchGlobalBinding
@ -28,6 +30,14 @@ class GlobalSearchActivity : BaseActivity<ActivitySearchGlobalBinding>() {
.commit() .commit()
} }
override fun onWindowInsetsChanged(insets: Insets) {
binding.toolbar.updatePadding(
top = insets.top,
left = insets.left,
right = insets.right
)
}
companion object { companion object {
private const val EXTRA_QUERY = "query" private const val EXTRA_QUERY = "query"

@ -52,6 +52,8 @@ class MainSettingsFragment : BasePreferenceFragment(R.string.settings),
MultiSummaryProvider(R.string.gestures_only) MultiSummaryProvider(R.string.gestures_only)
findPreference<MultiSelectListPreference>(AppSettings.KEY_TRACK_SOURCES)?.summaryProvider = findPreference<MultiSelectListPreference>(AppSettings.KEY_TRACK_SOURCES)?.summaryProvider =
MultiSummaryProvider(R.string.dont_check) MultiSummaryProvider(R.string.dont_check)
findPreference<Preference>(AppSettings.KEY_GRID_SIZE)?.isEnabled =
settings.listMode == ListMode.GRID
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -91,6 +93,10 @@ class MainSettingsFragment : BasePreferenceFragment(R.string.settings),
?: getString(R.string.not_available) ?: getString(R.string.not_available)
} }
} }
AppSettings.KEY_LIST_MODE -> {
findPreference<Preference>(AppSettings.KEY_GRID_SIZE)?.isEnabled =
settings.listMode == ListMode.GRID
}
} }
} }

@ -1,9 +1,11 @@
package org.koitharu.kotatsu.settings package org.koitharu.kotatsu.settings
import android.content.Context
import android.media.RingtoneManager import android.media.RingtoneManager
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.preference.Preference import androidx.preference.Preference
import org.koin.android.ext.android.get
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BasePreferenceFragment import org.koitharu.kotatsu.base.ui.BasePreferenceFragment
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
@ -12,6 +14,15 @@ import org.koitharu.kotatsu.utils.ext.toUriOrNull
class NotificationSettingsLegacyFragment : BasePreferenceFragment(R.string.notifications) { class NotificationSettingsLegacyFragment : BasePreferenceFragment(R.string.notifications) {
private val ringtonePickContract = registerForActivityResult(
RingtonePickContract(get<Context>().getString(R.string.notification_sound))
) { uri ->
settings.notificationSound = uri?.toString().orEmpty()
findPreference<Preference>(AppSettings.KEY_NOTIFICATIONS_SOUND)?.run {
summary = RingtoneManager.getRingtone(context, uri).getTitle(context)
}
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.pref_notifications) addPreferencesFromResource(R.xml.pref_notifications)
} }
@ -27,12 +38,7 @@ class NotificationSettingsLegacyFragment : BasePreferenceFragment(R.string.notif
override fun onPreferenceTreeClick(preference: Preference?): Boolean { override fun onPreferenceTreeClick(preference: Preference?): Boolean {
return when (preference?.key) { return when (preference?.key) {
AppSettings.KEY_NOTIFICATIONS_SOUND -> { AppSettings.KEY_NOTIFICATIONS_SOUND -> {
registerForActivityResult(RingtonePickContract(preference.title.toString())) { uri -> ringtonePickContract.launch(settings.notificationSound.toUriOrNull())
settings.notificationSound = uri?.toString().orEmpty()
findPreference<Preference>(AppSettings.KEY_NOTIFICATIONS_SOUND)?.run {
summary = RingtoneManager.getRingtone(context, uri).getTitle(context)
}
}.launch(settings.notificationSound.toUriOrNull())
true true
} }
else -> super.onPreferenceTreeClick(preference) else -> super.onPreferenceTreeClick(preference)

@ -3,6 +3,8 @@ package org.koitharu.kotatsu.settings
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.core.graphics.Insets
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.commit import androidx.fragment.app.commit
import androidx.preference.Preference import androidx.preference.Preference
@ -56,6 +58,14 @@ class SettingsActivity : BaseActivity<ActivitySettingsBinding>(),
} }
} }
override fun onWindowInsetsChanged(insets: Insets) {
binding.toolbar.updatePadding(
top = insets.top,
left = insets.left,
right = insets.right
)
}
companion object { companion object {
fun newIntent(context: Context) = Intent(context, SettingsActivity::class.java) fun newIntent(context: Context) = Intent(context, SettingsActivity::class.java)

@ -4,6 +4,8 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.graphics.Insets
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -49,6 +51,14 @@ class SourcesSettingsFragment : BaseFragment<FragmentSettingsSourcesBinding>(),
super.onDestroyView() super.onDestroyView()
} }
override fun onWindowInsetsChanged(insets: Insets) {
binding.recyclerView.updatePadding(
bottom = insets.bottom,
left = insets.left,
right = insets.right
)
}
override fun onItemClick(item: MangaSource, view: View) { override fun onItemClick(item: MangaSource, view: View) {
(activity as? SettingsActivity)?.openMangaSourceSettings(item) (activity as? SettingsActivity)?.openMangaSourceSettings(item)
} }

@ -2,7 +2,8 @@ package org.koitharu.kotatsu.tracker.ui
import android.os.Bundle import android.os.Bundle
import android.view.* import android.view.*
import androidx.core.view.isVisible import androidx.core.graphics.Insets
import androidx.core.view.updatePadding
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import org.koin.android.ext.android.get import org.koin.android.ext.android.get
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
@ -18,7 +19,6 @@ import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.tracker.ui.adapter.FeedAdapter import org.koitharu.kotatsu.tracker.ui.adapter.FeedAdapter
import org.koitharu.kotatsu.tracker.work.TrackWorker import org.koitharu.kotatsu.tracker.work.TrackWorker
import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.hasItems
class FeedFragment : BaseFragment<FragmentFeedBinding>(), PaginationScrollListener.Callback, class FeedFragment : BaseFragment<FragmentFeedBinding>(), PaginationScrollListener.Callback,
OnListItemClickListener<Manga> { OnListItemClickListener<Manga> {
@ -52,9 +52,7 @@ class FeedFragment : BaseFragment<FragmentFeedBinding>(), PaginationScrollListen
} }
viewModel.content.observe(viewLifecycleOwner, this::onListChanged) viewModel.content.observe(viewLifecycleOwner, this::onListChanged)
viewModel.isLoading.observe(viewLifecycleOwner, this::onLoadingStateChanged)
viewModel.onError.observe(viewLifecycleOwner, this::onError) viewModel.onError.observe(viewLifecycleOwner, this::onError)
viewModel.isEmptyState.observe(viewLifecycleOwner, this::onEmptyStateChanged)
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
@ -80,41 +78,24 @@ class FeedFragment : BaseFragment<FragmentFeedBinding>(), PaginationScrollListen
super.onDestroyView() super.onDestroyView()
} }
override fun onWindowInsetsChanged(insets: Insets) {
binding.recyclerView.updatePadding(
left = insets.left,
right = insets.right,
bottom = insets.bottom
)
}
private fun onListChanged(list: List<ListModel>) { private fun onListChanged(list: List<ListModel>) {
feedAdapter?.items = list feedAdapter?.items = list
} }
private fun onError(e: Throwable) { private fun onError(e: Throwable) {
if (binding.recyclerView.hasItems) { Snackbar.make(
Snackbar.make( binding.recyclerView,
binding.recyclerView, e.getDisplayMessage(resources),
e.getDisplayMessage(resources), Snackbar.LENGTH_SHORT
Snackbar.LENGTH_SHORT ).show()
).show()
} else {
with(binding.textViewHolder) {
text = e.getDisplayMessage(resources)
setCompoundDrawablesRelativeWithIntrinsicBounds(
0,
R.drawable.ic_error_large,
0,
0
)
isVisible = true
}
}
}
private fun onLoadingStateChanged(isLoading: Boolean) {
val hasItems = binding.recyclerView.hasItems
binding.progressBar.isVisible = isLoading && !hasItems
}
private fun onEmptyStateChanged(isEmpty: Boolean) {
if (isEmpty) {
setUpEmptyListHolder()
}
binding.layoutHolder.isVisible = isEmpty
} }
override fun onScrolledToEnd() { override fun onScrolledToEnd() {
@ -125,13 +106,6 @@ class FeedFragment : BaseFragment<FragmentFeedBinding>(), PaginationScrollListen
startActivity(DetailsActivity.newIntent(context ?: return, item)) startActivity(DetailsActivity.newIntent(context ?: return, item))
} }
private fun setUpEmptyListHolder() {
with(binding.textViewHolder) {
setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, null, null)
setText(R.string.text_feed_holder)
}
}
companion object { companion object {
fun newInstance() = FeedFragment() fun newInstance() = FeedFragment()

@ -9,8 +9,10 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseViewModel import org.koitharu.kotatsu.base.ui.BaseViewModel
import org.koitharu.kotatsu.core.model.TrackingLogItem import org.koitharu.kotatsu.core.model.TrackingLogItem
import org.koitharu.kotatsu.list.ui.model.EmptyState
import org.koitharu.kotatsu.list.ui.model.LoadingFooter import org.koitharu.kotatsu.list.ui.model.LoadingFooter
import org.koitharu.kotatsu.list.ui.model.LoadingState import org.koitharu.kotatsu.list.ui.model.LoadingState
import org.koitharu.kotatsu.tracker.domain.TrackingRepository import org.koitharu.kotatsu.tracker.domain.TrackingRepository
@ -34,7 +36,11 @@ class FeedViewModel(
}, },
hasNextPage hasNextPage
) { list, isHasNextPage -> ) { list, isHasNextPage ->
if (isHasNextPage && list.isNotEmpty()) list + LoadingFooter else list when {
list.isEmpty() -> listOf(EmptyState(R.string.text_feed_holder))
isHasNextPage -> list + LoadingFooter
else -> list
}
}.flowOn(Dispatchers.Default).asLiveData(viewModelScope.coroutineContext, listOf(LoadingState)) }.flowOn(Dispatchers.Default).asLiveData(viewModelScope.coroutineContext, listOf(LoadingState))
init { init {

@ -9,7 +9,11 @@ import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.core.graphics.Insets
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
@ -77,6 +81,24 @@ class ShelfConfigActivity : BaseActivity<ActivityCategoriesBinding>(), OnListIte
viewModel.checkedId = item.id viewModel.checkedId = item.id
} }
override fun onWindowInsetsChanged(insets: Insets) {
binding.fabAdd.updateLayoutParams<ViewGroup.MarginLayoutParams> {
rightMargin = topMargin + insets.right
leftMargin = topMargin + insets.left
bottomMargin = topMargin + insets.bottom
}
binding.recyclerView.updatePadding(
left = insets.left,
right = insets.right,
bottom = insets.bottom
)
binding.toolbar.updatePadding(
left = insets.left,
right = insets.right,
top = insets.top
)
}
private fun onContentChanged(categories: List<CategoryItem>) { private fun onContentChanged(categories: List<CategoryItem>) {
adapter.items = categories adapter.items = categories
} }

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".details.ui.DetailsActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?colorPrimary"
android:theme="@style/AppToolbarTheme">
<com.google.android.material.appbar.MaterialToolbar
android:id="@id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/AppPopupTheme">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
style="@style/Widget.MaterialComponents.TabLayout.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:background="@android:color/transparent"
app:tabGravity="start"
app:tabMode="scrollable" />
</com.google.android.material.appbar.MaterialToolbar>
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

@ -3,6 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:clipToPadding="false"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">

@ -11,7 +11,6 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?colorPrimary" android:background="?colorPrimary"
android:fitsSystemWindows="true"
android:theme="@style/AppToolbarTheme" android:theme="@style/AppToolbarTheme"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"

@ -11,7 +11,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?colorPrimary" android:background="?colorPrimary"
android:fitsSystemWindows="true"
android:theme="@style/AppToolbarTheme"> android:theme="@style/AppToolbarTheme">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
@ -28,6 +27,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:scrollbars="vertical" android:scrollbars="vertical"
android:clipToPadding="false"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" /> app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />

@ -7,6 +7,7 @@
tools:context=".details.ui.DetailsActivity"> tools:context=".details.ui.DetailsActivity">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?colorPrimary" android:background="?colorPrimary"

@ -39,6 +39,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="16dp" android:layout_margin="16dp"
android:contentDescription="@string/_continue"
android:src="@drawable/ic_read_fill" android:src="@drawable/ic_read_fill"
android:visibility="gone" android:visibility="gone"
app:backgroundTint="?colorAccent" app:backgroundTint="?colorAccent"
@ -56,6 +57,7 @@
android:layout_width="260dp" android:layout_width="260dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="start" android:layout_gravity="start"
app:insetForeground="@android:color/transparent"
app:menu="@menu/nav_drawer" /> app:menu="@menu/nav_drawer" />
</androidx.drawerlayout.widget.DrawerLayout> </androidx.drawerlayout.widget.DrawerLayout>

@ -2,7 +2,6 @@
<FrameLayout <FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/rootLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:keepScreenOn="true"> android:keepScreenOn="true">

@ -10,7 +10,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?colorPrimary" android:background="?colorPrimary"
android:fitsSystemWindows="true"
android:theme="@style/AppToolbarTheme"> android:theme="@style/AppToolbarTheme">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar

@ -9,7 +9,6 @@
android:id="@+id/appbar" android:id="@+id/appbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:background="?colorPrimary" android:background="?colorPrimary"
android:theme="@style/AppToolbarTheme"> android:theme="@style/AppToolbarTheme">

@ -7,19 +7,20 @@
android:orientation="vertical"> android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent" android:id="@+id/appbar"
android:layout_height="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?colorPrimary" android:background="?colorPrimary"
android:theme="@style/AppToolbarTheme"> android:theme="@style/AppToolbarTheme">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@id/toolbar" android:id="@id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|enterAlways" app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/AppPopupTheme" /> app:popupTheme="@style/AppPopupTheme" />
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<androidx.fragment.app.FragmentContainerView <androidx.fragment.app.FragmentContainerView
android:id="@id/container" android:id="@id/container"

@ -10,7 +10,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?colorPrimary" android:background="?colorPrimary"
android:fitsSystemWindows="true"
android:theme="@style/AppToolbarTheme"> android:theme="@style/AppToolbarTheme">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar

@ -22,8 +22,10 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:imeOptions="actionDone" android:imeOptions="actionDone"
android:paddingStart="0dp" android:paddingStart="0dp"
android:paddingTop="28dp"
android:paddingEnd="0dp" android:paddingEnd="0dp"
android:singleLine="true" android:singleLine="true"
tools:hint="@tools:sample/cities"
tools:text="@tools:sample/lorem[2]" /> tools:text="@tools:sample/lorem[2]" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>

@ -5,6 +5,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:orientation="vertical"> android:orientation="vertical">
<com.google.android.material.button.MaterialButtonToggleGroup <com.google.android.material.button.MaterialButtonToggleGroup
@ -42,13 +43,16 @@
</com.google.android.material.button.MaterialButtonToggleGroup> </com.google.android.material.button.MaterialButtonToggleGroup>
<TextView <TextView
android:id="@+id/textView_grid_title"
style="?android:attr/windowTitleStyle" style="?android:attr/windowTitleStyle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingLeft="?attr/dialogPreferredPadding" android:paddingLeft="?attr/dialogPreferredPadding"
android:paddingRight="?attr/dialogPreferredPadding" android:paddingRight="?attr/dialogPreferredPadding"
android:singleLine="true" android:singleLine="true"
android:text="@string/grid_size" /> android:text="@string/grid_size"
android:visibility="gone"
tools:visibility="visible" />
<SeekBar <SeekBar
android:id="@+id/seekbar_grid" android:id="@+id/seekbar_grid"
@ -56,6 +60,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="16dp" android:layout_margin="16dp"
android:max="100" android:max="100"
tools:progress="50" /> android:visibility="gone"
tools:progress="50"
tools:visibility="visible" />
</LinearLayout> </LinearLayout>

@ -3,6 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:clipToPadding="false"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">

@ -1,47 +1,18 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout <androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:clipToPadding="false"
<androidx.recyclerview.widget.RecyclerView android:orientation="vertical"
android:id="@+id/recyclerView" android:scrollbars="vertical"
android:layout_width="match_parent" app:fastScrollEnabled="true"
android:layout_height="match_parent" app:fastScrollHorizontalThumbDrawable="@drawable/list_thumb"
android:orientation="vertical" app:fastScrollHorizontalTrackDrawable="@drawable/list_track"
android:scrollbars="vertical" app:fastScrollVerticalThumbDrawable="@drawable/list_thumb"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:fastScrollVerticalTrackDrawable="@drawable/list_track"
tools:listitem="@layout/item_manga_list" /> app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_tracklog" />
<LinearLayout
android:id="@+id/layout_holder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/textView_holder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:textAppearance="?android:textAppearanceMedium"
android:textColor="?android:textColorSecondary"
tools:text="@tools:sample/lorem[3]" />
</LinearLayout>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true" />
</FrameLayout>

@ -21,6 +21,7 @@
android:id="@+id/recyclerView" android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clipToPadding="false"
android:orientation="vertical" android:orientation="vertical"
android:scrollbars="vertical" android:scrollbars="vertical"
app:fastScrollEnabled="true" app:fastScrollEnabled="true"
@ -39,6 +40,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="end" android:layout_gravity="end"
android:background="?android:windowBackground" android:background="?android:windowBackground"
android:clipToPadding="false"
android:orientation="vertical" android:orientation="vertical"
android:scrollbars="vertical" android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"

@ -6,6 +6,7 @@
android:id="@+id/recyclerView" android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clipToPadding="false"
android:orientation="vertical" android:orientation="vertical"
android:scrollbars="vertical" android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"

@ -1,14 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content">
app:cardElevation="0dp"
app:cardMaxElevation="0dp"
app:strokeColor="?colorOnSurface"
app:strokeWidth="1px">
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"

@ -4,11 +4,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/manga_list_details_item_height" android:layout_height="@dimen/manga_list_details_item_height">
app:cardElevation="0dp"
app:cardMaxElevation="0dp"
app:strokeColor="?colorOnSurface"
app:strokeWidth="1px">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

@ -1,15 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content">
app:cardBackgroundColor="?android:windowBackground"
app:cardElevation="0dp"
app:cardMaxElevation="0dp"
app:strokeColor="?android:colorControlNormal"
app:strokeWidth="1px">
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="BaseAppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="windowActionModeOverlay">true</item>
<item name="actionModeCloseDrawable">@drawable/ic_cross</item>
<item name="actionModeStyle">@style/AppActionMode</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
<item name="badgeStyle">@style/Widget.MaterialComponents.Badge</item>
<item name="android:statusBarColor">@color/status_bar</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
</resources>

@ -8,4 +8,5 @@
<color name="grey_dark">#212121</color> <color name="grey_dark">#212121</color>
<color name="dim">#99000000</color> <color name="dim">#99000000</color>
<color name="error">#D32F2F</color> <color name="error">#D32F2F</color>
<color name="status_bar">#33000000</color>
</resources> </resources>

@ -6,6 +6,7 @@
<item name="actionModeStyle">@style/AppActionMode</item> <item name="actionModeStyle">@style/AppActionMode</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item> <item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
<item name="badgeStyle">@style/Widget.MaterialComponents.Badge</item> <item name="badgeStyle">@style/Widget.MaterialComponents.Badge</item>
<item name="android:statusBarColor">@color/status_bar</item>
</style> </style>
<style name="AppTheme" parent="BaseAppTheme"> <style name="AppTheme" parent="BaseAppTheme">

Loading…
Cancel
Save