Edge-to-edge ui

This commit is contained in:
Koitharu
2020-12-16 11:49:10 +02:00
parent b8d2fa69c4
commit 6463023736
51 changed files with 434 additions and 164 deletions

View File

@@ -7,13 +7,18 @@ import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
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 org.koin.android.ext.android.get
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.R
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
private set
@@ -23,6 +28,7 @@ abstract class BaseActivity<B : ViewBinding> : AppCompatActivity() {
setTheme(R.style.AppTheme_Amoled)
}
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
}
@Deprecated("Use ViewBinding", level = DeprecationLevel.ERROR)
@@ -41,10 +47,12 @@ abstract class BaseActivity<B : ViewBinding> : AppCompatActivity() {
this.binding = binding
super.setContentView(binding.root)
(binding.root.findViewById<View>(R.id.toolbar) as? Toolbar)?.let(this::setSupportActionBar)
ViewCompat.setOnApplyWindowInsetsListener(binding.root, this)
}
private fun setupToolbar() {
(findViewById<View>(R.id.toolbar) as? Toolbar)?.let(this::setSupportActionBar)
override fun onApplyWindowInsets(v: View, insets: WindowInsetsCompat): WindowInsetsCompat {
onWindowInsetsChanged(insets.getInsets(WindowInsetsCompat.Type.systemBars()))
return insets
}
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)
}
protected abstract fun onWindowInsetsChanged(insets: Insets)
private fun setupToolbar() {
(findViewById<View>(R.id.toolbar) as? Toolbar)?.let(this::setSupportActionBar)
}
}

View File

@@ -5,10 +5,14 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
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.viewbinding.ViewBinding
abstract class BaseFragment<B : ViewBinding> : Fragment() {
abstract class BaseFragment<B : ViewBinding> : Fragment(), OnApplyWindowInsetsListener {
private var viewBinding: B? = null
@@ -25,6 +29,11 @@ abstract class BaseFragment<B : ViewBinding> : Fragment() {
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
ViewCompat.setOnApplyWindowInsetsListener(view, this)
}
override fun onDestroyView() {
viewBinding = null
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 abstract fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): B
protected abstract fun onWindowInsetsChanged(insets: Insets)
}

View File

@@ -3,16 +3,22 @@ package org.koitharu.kotatsu.base.ui
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.view.View
import android.view.WindowManager
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.viewbinding.ViewBinding
abstract class BaseFullscreenActivity<B : ViewBinding> : BaseActivity<B>() {
private lateinit var insetsControllerCompat: WindowInsetsControllerCompat
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
with(window) {
insetsControllerCompat = WindowInsetsControllerCompat(this, decorView)
insetsControllerCompat.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
statusBarColor = Color.TRANSPARENT
navigationBarColor = Color.TRANSPARENT
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
@@ -24,20 +30,11 @@ abstract class BaseFullscreenActivity<B : ViewBinding> : BaseActivity<B>() {
}
protected fun hideSystemUI() {
window.decorView.systemUiVisibility = (
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
)
insetsControllerCompat.hide(WindowInsetsCompat.Type.systemBars())
}
protected fun showSystemUI() {
window.decorView.systemUiVisibility =
(View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
insetsControllerCompat.show(WindowInsetsCompat.Type.systemBars())
}
}

View File

@@ -1,17 +1,39 @@
package org.koitharu.kotatsu.base.ui
import android.os.Bundle
import android.view.View
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 org.koin.android.ext.android.inject
import org.koitharu.kotatsu.core.prefs.AppSettings
abstract class BasePreferenceFragment(@StringRes private val titleId: Int) :
PreferenceFragmentCompat() {
PreferenceFragmentCompat(), OnApplyWindowInsetsListener {
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() {
super.onResume()
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
}
}

View File

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

View File

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

View File

@@ -9,9 +9,12 @@ import android.view.MenuItem
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.Insets
import androidx.core.net.toFile
import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope
import com.google.android.material.snackbar.Snackbar
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) {
val tab = binding.tabs.getTabAt(1) ?: return
if (newChapters == 0) {

View File

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

View File

@@ -3,6 +3,8 @@ package org.koitharu.kotatsu.favourites.ui
import android.os.Bundle
import android.os.Parcelable
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.tabs.TabLayoutMediator
import org.koin.androidx.viewmodel.ext.android.viewModel
@@ -66,6 +68,13 @@ class FavouritesContainerFragment : BaseFragment<FragmentFavouritesBinding>(),
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>) {
val data = ArrayList<FavouriteCategory>(categories.size + 1)
data += FavouriteCategory(0L, getString(R.string.all_favourites), -1, Date())

View File

@@ -6,7 +6,11 @@ import android.content.res.ColorStateList
import android.graphics.Color
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import androidx.core.graphics.Insets
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
@@ -70,6 +74,24 @@ class CategoriesActivity : BaseActivity<ActivityCategoriesBinding>(),
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>) {
// TODO check if not moved

View File

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

View File

@@ -4,9 +4,11 @@ import android.os.Bundle
import android.view.*
import androidx.annotation.CallSuper
import androidx.appcompat.widget.PopupMenu
import androidx.core.graphics.Insets
import androidx.core.view.GravityCompat
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.drawerlayout.widget.DrawerLayout
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.GridLayoutManager
@@ -195,6 +197,19 @@ abstract class MangaListFragment : BaseFragment<FragmentListBinding>(),
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) {
spanSizeLookup.invalidateCache()
spanResolver.setGridSize(scale, binding.recyclerView)

View File

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

View File

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

View File

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

View File

@@ -10,6 +10,9 @@ import android.view.Menu
import android.view.MenuItem
import android.view.inputmethod.EditorInfo
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.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseActivity
@@ -20,6 +23,7 @@ class ProtectActivity : BaseActivity<ActivityProtectBinding>(), TextView.OnEdito
TextWatcher {
private val viewModel by viewModel<ProtectViewModel>()
private val appProtectHelper by inject<AppProtectHelper>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -49,6 +53,14 @@ class ProtectActivity : BaseActivity<ActivityProtectBinding>(), TextView.OnEdito
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 {
return if (actionId == EditorInfo.IME_ACTION_DONE) {
viewModel.tryUnlock(binding.editPassword.text.toString().orEmpty())
@@ -67,7 +79,7 @@ class ProtectActivity : BaseActivity<ActivityProtectBinding>(), TextView.OnEdito
}
private fun onUnlockSuccess(unit: Unit) {
AppProtectHelper.unlock(this)
appProtectHelper.unlock(this)
}
private fun onError(e: Throwable) {

View File

@@ -54,8 +54,7 @@ import org.koitharu.kotatsu.utils.ext.showAnimated
class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
ChaptersDialog.OnChapterChangeListener,
GridTouchHelper.OnGridTouchListener, OnPageSelectListener, ReaderConfigDialog.Callback,
ActivityResultCallback<Boolean>, OnApplyWindowInsetsListener,
ReaderControlDelegate.OnInteractionListener {
ActivityResultCallback<Boolean>, ReaderControlDelegate.OnInteractionListener {
private val viewModel by viewModel<ReaderViewModel> {
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 orientationHelper: ScreenOrientationHelper
private lateinit var controlDelegate: ReaderControlDelegate
private var gestureInsets: Insets = Insets.NONE
private val reader
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.setOnMenuItemClickListener(::onOptionsItemSelected)
ViewCompat.setOnApplyWindowInsetsListener(binding.rootLayout, this)
orientationHelper.observeAutoOrientation()
.onEach {
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 {
return if (binding.appbarTop.hasGlobalPoint(rawX, rawY)
|| binding.appbarBottom.hasGlobalPoint(rawX, rawY)
return if (
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
} else {
val targets = binding.rootLayout.hitTest(rawX, rawY)
val targets = binding.root.hitTest(rawX, rawY)
targets.none { it.hasOnClickListeners() }
}
}
@@ -273,6 +276,8 @@ class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
viewModel.switchMode(mode)
}
override fun onWindowInsetsChanged(insets: Insets) = Unit
private fun onPageSaved(uri: Uri?) {
if (uri != null) {
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 {
gestureInsets = insets.getInsets(WindowInsetsCompat.Type.systemGestures())
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
binding.appbarTop.updatePadding(
top = systemBars.top,

View File

@@ -3,6 +3,8 @@ package org.koitharu.kotatsu.reader.ui
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.core.graphics.Insets
import androidx.core.view.updatePadding
import androidx.fragment.app.commit
import org.koitharu.kotatsu.BuildConfig
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 {
private const val ACTION_READER =

View File

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

View File

@@ -5,6 +5,8 @@ import android.content.Intent
import android.os.Bundle
import android.os.Parcelable
import androidx.appcompat.widget.SearchView
import androidx.core.graphics.Insets
import androidx.core.view.updatePadding
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseActivity
import org.koitharu.kotatsu.core.model.MangaSource
@@ -44,6 +46,14 @@ class SearchActivity : BaseActivity<ActivitySearchBinding>(), SearchView.OnQuery
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 {
return if (!query.isNullOrBlank()) {
title = query

View File

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

View File

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

View File

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

View File

@@ -3,6 +3,8 @@ package org.koitharu.kotatsu.settings
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.core.graphics.Insets
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.commit
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 {
fun newIntent(context: Context) = Intent(context, SettingsActivity::class.java)

View File

@@ -4,6 +4,8 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.graphics.Insets
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
@@ -49,6 +51,14 @@ class SourcesSettingsFragment : BaseFragment<FragmentSettingsSourcesBinding>(),
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) {
(activity as? SettingsActivity)?.openMangaSourceSettings(item)
}

View File

@@ -2,7 +2,8 @@ package org.koitharu.kotatsu.tracker.ui
import android.os.Bundle
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 org.koin.android.ext.android.get
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.work.TrackWorker
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.hasItems
class FeedFragment : BaseFragment<FragmentFeedBinding>(), PaginationScrollListener.Callback,
OnListItemClickListener<Manga> {
@@ -52,9 +52,7 @@ class FeedFragment : BaseFragment<FragmentFeedBinding>(), PaginationScrollListen
}
viewModel.content.observe(viewLifecycleOwner, this::onListChanged)
viewModel.isLoading.observe(viewLifecycleOwner, this::onLoadingStateChanged)
viewModel.onError.observe(viewLifecycleOwner, this::onError)
viewModel.isEmptyState.observe(viewLifecycleOwner, this::onEmptyStateChanged)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
@@ -80,41 +78,24 @@ class FeedFragment : BaseFragment<FragmentFeedBinding>(), PaginationScrollListen
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>) {
feedAdapter?.items = list
}
private fun onError(e: Throwable) {
if (binding.recyclerView.hasItems) {
Snackbar.make(
binding.recyclerView,
e.getDisplayMessage(resources),
Snackbar.LENGTH_SHORT
).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
Snackbar.make(
binding.recyclerView,
e.getDisplayMessage(resources),
Snackbar.LENGTH_SHORT
).show()
}
override fun onScrolledToEnd() {
@@ -125,13 +106,6 @@ class FeedFragment : BaseFragment<FragmentFeedBinding>(), PaginationScrollListen
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 {
fun newInstance() = FeedFragment()

View File

@@ -9,8 +9,10 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOn
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseViewModel
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.LoadingState
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
@@ -34,7 +36,11 @@ class FeedViewModel(
},
hasNextPage
) { 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))
init {

View File

@@ -9,7 +9,11 @@ import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.core.graphics.Insets
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar
@@ -77,6 +81,24 @@ class ShelfConfigActivity : BaseActivity<ActivityCategoriesBinding>(), OnListIte
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>) {
adapter.items = categories
}