From ad201d2bcde9529ba7762970a261c96f271da8da Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sat, 29 Feb 2020 12:47:03 +0200 Subject: [PATCH] Optimize layout for tablet --- README.md | 2 + .../kotatsu/ui/common/BaseBottomSheet.kt | 12 +- .../ui/details/MangaDetailsFragment.kt | 3 +- .../kotatsu/ui/main/list/MangaListFragment.kt | 16 +- .../reader/thumbnails/PagesThumbnailsSheet.kt | 15 +- .../kotatsu/ui/settings/SettingsActivity.kt | 48 +++++- .../ui/settings/SettingsHeadersFragment.kt | 1 + .../org/koitharu/kotatsu/utils/UiUtils.kt | 29 +++- .../res/layout-sw600dp/activity_settings.xml | 54 +++++++ .../res/layout-sw600dp/fragment_details.xml | 139 ++++++++++++++++++ .../main/res/layout-sw600dp/fragment_list.xml | 79 ++++++++++ app/src/main/res/layout/item_page.xml | 20 +-- app/src/main/res/layout/item_page_thumb.xml | 24 +-- app/src/main/res/values-sw600dp/bools.xml | 4 + app/src/main/res/values/bools.xml | 4 + app/src/main/res/xml/pref_headers.xml | 3 +- 16 files changed, 412 insertions(+), 41 deletions(-) create mode 100644 app/src/main/res/layout-sw600dp/activity_settings.xml create mode 100644 app/src/main/res/layout-sw600dp/fragment_details.xml create mode 100644 app/src/main/res/layout-sw600dp/fragment_list.xml create mode 100644 app/src/main/res/values-sw600dp/bools.xml create mode 100644 app/src/main/res/values/bools.xml diff --git a/README.md b/README.md index 55fad1e76..f917c1a64 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,11 @@ _Coming soon_ ### Main Features * Online manga catalogues +* Search manga by name and genre * Reading history * Favourites with custom categories * Saving manga and reading it offline +* Tablet-optimized modern UI * Reading third-party comics from CBZ * Standard and Webtoon-optimized reader diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/common/BaseBottomSheet.kt b/app/src/main/java/org/koitharu/kotatsu/ui/common/BaseBottomSheet.kt index 32a65e01b..090d58c3b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/common/BaseBottomSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/common/BaseBottomSheet.kt @@ -1,17 +1,27 @@ package org.koitharu.kotatsu.ui.common +import android.app.Dialog import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.annotation.LayoutRes +import androidx.appcompat.app.AppCompatDialog import moxy.MvpBottomSheetDialogFragment +import org.koitharu.kotatsu.utils.UiUtils -abstract class BaseBottomSheet(@LayoutRes private val layoutResId: Int) : MvpBottomSheetDialogFragment() { +abstract class BaseBottomSheet(@LayoutRes private val layoutResId: Int) : + MvpBottomSheetDialogFragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? = inflater.inflate(layoutResId, container, false) + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + return if (UiUtils.isTablet(requireContext())) { + AppCompatDialog(context, theme) + } else super.onCreateDialog(savedInstanceState) + } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/details/MangaDetailsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/details/MangaDetailsFragment.kt index 2b2be4a1b..d4a2a7692 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/details/MangaDetailsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/details/MangaDetailsFragment.kt @@ -14,6 +14,7 @@ import org.koitharu.kotatsu.ui.common.BaseFragment import org.koitharu.kotatsu.ui.main.list.favourites.categories.FavouriteCategoriesDialog import org.koitharu.kotatsu.ui.reader.ReaderActivity import org.koitharu.kotatsu.utils.ext.setChips +import org.koitharu.kotatsu.utils.ext.textAndVisible import kotlin.math.roundToInt class MangaDetailsFragment : BaseFragment(R.layout.fragment_details), MangaDetailsView { @@ -31,7 +32,7 @@ class MangaDetailsFragment : BaseFragment(R.layout.fragment_details), MangaDetai crossfade(true) } textView_title.text = manga.title - textView_subtitle.text = manga.altTitle + textView_subtitle.textAndVisible = manga.altTitle textView_description.text = manga.description?.parseAsHtml()?.takeUnless(Spanned::isBlank) ?: getString(R.string.no_description) if (manga.rating == Manga.NO_RATING) { diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/MangaListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/MangaListFragment.kt index 3dae9a03f..f5a7f239d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/main/list/MangaListFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/main/list/MangaListFragment.kt @@ -51,7 +51,7 @@ abstract class MangaListFragment : BaseFragment(R.layout.fragment_list), Mang override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) + drawer?.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) adapter = MangaListAdapter(this) initListMode(settings.listMode) recyclerView.adapter = adapter @@ -88,15 +88,15 @@ abstract class MangaListFragment : BaseFragment(R.layout.fragment_list), Mang true } R.id.action_filter -> { - drawer.toggleDrawer(GravityCompat.END) + drawer?.toggleDrawer(GravityCompat.END) true } else -> super.onOptionsItemSelected(item) } override fun onPrepareOptionsMenu(menu: Menu) { - menu.findItem(R.id.action_filter).isVisible = - drawer.getDrawerLockMode(GravityCompat.END) != DrawerLayout.LOCK_MODE_LOCKED_CLOSED + menu.findItem(R.id.action_filter).isVisible = drawer != null && + drawer?.getDrawerLockMode(GravityCompat.END) != DrawerLayout.LOCK_MODE_LOCKED_CLOSED super.onPrepareOptionsMenu(menu) } @@ -184,7 +184,7 @@ abstract class MangaListFragment : BaseFragment(R.layout.fragment_list), Mang currentFilter: MangaFilter? ) { recyclerView_filter.adapter = FilterAdapter(sortOrders, tags, currentFilter, this) - drawer.setDrawerLockMode( + drawer?.setDrawerLockMode( if (sortOrders.isEmpty() && tags.isEmpty()) { DrawerLayout.LOCK_MODE_LOCKED_CLOSED } else { @@ -196,7 +196,7 @@ abstract class MangaListFragment : BaseFragment(R.layout.fragment_list), Mang @CallSuper override fun onFilterChanged(filter: MangaFilter) { - drawer.closeDrawers() + drawer?.closeDrawers() } protected open fun setUpEmptyListHolder() { @@ -210,6 +210,7 @@ abstract class MangaListFragment : BaseFragment(R.layout.fragment_list), Mang recyclerView.adapter = null recyclerView.layoutManager = null recyclerView.clearItemDecorations() + recyclerView.removeOnLayoutChangeListener(UiUtils.SpanCountResolver) adapter?.listMode = mode recyclerView.layoutManager = when (mode) { ListMode.GRID -> GridLayoutManager(ctx, UiUtils.resolveGridSpanCount(ctx)) @@ -225,6 +226,9 @@ abstract class MangaListFragment : BaseFragment(R.layout.fragment_list), Mang ) } ) + if(mode == ListMode.GRID) { + recyclerView.addOnLayoutChangeListener(UiUtils.SpanCountResolver) + } adapter?.notifyDataSetChanged() recyclerView.firstItem = position } diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/thumbnails/PagesThumbnailsSheet.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/thumbnails/PagesThumbnailsSheet.kt index 093f5aa79..c672e0bd0 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/thumbnails/PagesThumbnailsSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/thumbnails/PagesThumbnailsSheet.kt @@ -4,7 +4,6 @@ import android.os.Bundle import android.view.View import androidx.core.view.isVisible import androidx.fragment.app.FragmentManager -import androidx.recyclerview.widget.GridLayoutManager import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import kotlinx.android.synthetic.main.sheet_pages.* @@ -33,17 +32,22 @@ class PagesThumbnailsSheet : BaseBottomSheet(R.layout.sheet_pages), dismissAllowingStateLoss() return } - (recyclerView.layoutManager as? GridLayoutManager)?.spanCount = UiUtils.resolveGridSpanCount(view.context) val title = arguments?.getString(ARG_TITLE) toolbar.title = title toolbar.setNavigationOnClickListener { dismiss() } toolbar.subtitle = resources.getQuantityString(R.plurals.pages, pages.size, pages.size) textView_title.text = title + if (dialog !is BottomSheetDialog) { + toolbar.isVisible = true + textView_title.isVisible = false + appbar.elevation = resources.getDimension(R.dimen.elevation_large) + } + recyclerView.addOnLayoutChangeListener(UiUtils.SpanCountResolver) } override fun onCreateDialog(savedInstanceState: Bundle?) = super.onCreateDialog(savedInstanceState).also { - val behavior = (it as BottomSheetDialog).behavior + val behavior = (it as? BottomSheetDialog)?.behavior ?: return@also behavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() { private val elevation = resources.getDimension(R.dimen.elevation_large) @@ -52,15 +56,16 @@ class PagesThumbnailsSheet : BaseBottomSheet(R.layout.sheet_pages), override fun onStateChanged(bottomSheet: View, newState: Int) { if (newState == BottomSheetBehavior.STATE_EXPANDED) { toolbar.isVisible = true + textView_title.isVisible = false appbar.elevation = elevation } else { toolbar.isVisible = false + textView_title.isVisible = true appbar.elevation = 0f } } }) -// behavior.peekHeight = BottomSheetBehavior.PEEK_HEIGHT_AUTO -// behavior.isFitToContents = false + } override fun onDestroyView() { diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/settings/SettingsActivity.kt b/app/src/main/java/org/koitharu/kotatsu/ui/settings/SettingsActivity.kt index 3e5f1c0f8..9e49a1ad6 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/settings/SettingsActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/settings/SettingsActivity.kt @@ -3,22 +3,62 @@ package org.koitharu.kotatsu.ui.settings import android.content.Context import android.content.Intent import android.os.Bundle +import androidx.fragment.app.FragmentManager import androidx.fragment.app.commit +import androidx.preference.Preference +import androidx.preference.PreferenceFragmentCompat +import kotlinx.android.synthetic.main.activity_settings.* import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.ui.common.BaseActivity -class SettingsActivity : BaseActivity() { +class SettingsActivity : BaseActivity(), + PreferenceFragmentCompat.OnPreferenceStartFragmentCallback { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_settings) supportActionBar?.setDisplayHomeAsUpEnabled(true) + val isTablet = container_side != null if (supportFragmentManager.findFragmentById(R.id.container) == null) { - supportFragmentManager.beginTransaction() - .replace(R.id.container, SettingsHeadersFragment()) - .commit() + supportFragmentManager.commit { + if (isTablet) { + replace(R.id.container_side, SettingsHeadersFragment()) + replace(R.id.container, AppearanceSettingsFragment()) + } else { + replace(R.id.container, SettingsHeadersFragment()) + } + } + } + } + + override fun onPreferenceStartFragment(caller: PreferenceFragmentCompat, pref: Preference): Boolean { + val fm = supportFragmentManager + if (container_side != null && caller is SettingsHeadersFragment) { + fm.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) + } + val fragment = fm.fragmentFactory.instantiate(classLoader, pref.fragment) + fragment.arguments = pref.extras + fragment.setTargetFragment(caller, 0) + fm.commit { + replace(R.id.container, fragment) + if (container_side == null || caller !is SettingsHeadersFragment) { + addToBackStack(null) + } + } + return true + } + + override fun onTitleChanged(title: CharSequence?, color: Int) { + if (container_side == null) { + super.onTitleChanged(title, color) + } else { + if (supportFragmentManager.backStackEntryCount == 0) { + supportActionBar?.subtitle = null + } else { + supportActionBar?.subtitle = title + } } } diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/settings/SettingsHeadersFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/settings/SettingsHeadersFragment.kt index 1b4badafc..670befdc0 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/settings/SettingsHeadersFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/settings/SettingsHeadersFragment.kt @@ -9,4 +9,5 @@ class SettingsHeadersFragment : BasePreferenceFragment(R.string.settings) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.pref_headers) } + } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/UiUtils.kt b/app/src/main/java/org/koitharu/kotatsu/utils/UiUtils.kt index 16aeb0c8c..1acec2160 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/UiUtils.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/UiUtils.kt @@ -2,8 +2,12 @@ package org.koitharu.kotatsu.utils import android.content.Context import android.graphics.Color +import android.view.View import androidx.annotation.ColorInt +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView import org.koitharu.kotatsu.R +import kotlin.math.abs import kotlin.math.roundToInt object UiUtils { @@ -19,10 +23,31 @@ object UiUtils { } @JvmStatic - fun resolveGridSpanCount(context: Context): Int { + fun resolveGridSpanCount(context: Context, width: Int = 0): Int { val cellWidth = context.resources.getDimensionPixelSize(R.dimen.preferred_grid_width) - val screenWidth = context.resources.displayMetrics.widthPixels.toDouble() + val screenWidth = (if (width <= 0) { + context.resources.displayMetrics.widthPixels + } else width).toDouble() val estimatedCount = (screenWidth / cellWidth).roundToInt() return estimatedCount.coerceAtLeast(2) } + + @JvmStatic + fun isTablet(context: Context) = context.resources.getBoolean(R.bool.is_tablet) + + object SpanCountResolver : View.OnLayoutChangeListener { + override fun onLayoutChange( + v: View?, left: Int, top: Int, right: Int, bottom: Int, + oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int + ) { + val rv = v as? RecyclerView ?: return + val width = abs(right - left) + if (width == 0) { + return + } + (rv.layoutManager as? GridLayoutManager)?.spanCount = + resolveGridSpanCount(rv.context, width) + } + + } } \ No newline at end of file diff --git a/app/src/main/res/layout-sw600dp/activity_settings.xml b/app/src/main/res/layout-sw600dp/activity_settings.xml new file mode 100644 index 000000000..3ebfadc14 --- /dev/null +++ b/app/src/main/res/layout-sw600dp/activity_settings.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-sw600dp/fragment_details.xml b/app/src/main/res/layout-sw600dp/fragment_details.xml new file mode 100644 index 000000000..9057dd7c7 --- /dev/null +++ b/app/src/main/res/layout-sw600dp/fragment_details.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-sw600dp/fragment_list.xml b/app/src/main/res/layout-sw600dp/fragment_list.xml new file mode 100644 index 000000000..830f116c5 --- /dev/null +++ b/app/src/main/res/layout-sw600dp/fragment_list.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_page.xml b/app/src/main/res/layout/item_page.xml index d8fa73526..f5e2d66c0 100644 --- a/app/src/main/res/layout/item_page.xml +++ b/app/src/main/res/layout/item_page.xml @@ -1,9 +1,9 @@ + android:layout_height="match_parent"> + android:orientation="vertical" + android:visibility="gone" + tools:visibility="visible"> + android:textAppearance="@style/TextAppearance.AppCompat.Small" + tools:text="@tools:sample/lorem[6]" /> diff --git a/app/src/main/res/layout/item_page_thumb.xml b/app/src/main/res/layout/item_page_thumb.xml index e99c00813..c04029a67 100644 --- a/app/src/main/res/layout/item_page_thumb.xml +++ b/app/src/main/res/layout/item_page_thumb.xml @@ -1,26 +1,28 @@ + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?attr/scrimBackground"> + android:layout_height="wrap_content" + android:scaleType="centerCrop" + tools:src="@drawable/ic_placeholder" /> + android:ellipsize="none" + android:gravity="end|bottom" + android:padding="12dp" + android:singleLine="true" + android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6" + tools:text="2" /> \ No newline at end of file diff --git a/app/src/main/res/values-sw600dp/bools.xml b/app/src/main/res/values-sw600dp/bools.xml new file mode 100644 index 000000000..f8f94c770 --- /dev/null +++ b/app/src/main/res/values-sw600dp/bools.xml @@ -0,0 +1,4 @@ + + + true + \ No newline at end of file diff --git a/app/src/main/res/values/bools.xml b/app/src/main/res/values/bools.xml new file mode 100644 index 000000000..07aafe1c4 --- /dev/null +++ b/app/src/main/res/values/bools.xml @@ -0,0 +1,4 @@ + + + false + \ No newline at end of file diff --git a/app/src/main/res/xml/pref_headers.xml b/app/src/main/res/xml/pref_headers.xml index 46933b843..ec0c46027 100644 --- a/app/src/main/res/xml/pref_headers.xml +++ b/app/src/main/res/xml/pref_headers.xml @@ -1,7 +1,6 @@ + xmlns:android="http://schemas.android.com/apk/res/android">