diff --git a/README.md b/README.md index de182812..e83e6c31 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,10 @@ Comment is supported right now but comment like or dislike and some other functi ## 更新内容 +### v0.5.2 + +修复“没有登录失败的校验”的问题。 + ### v0.5.1 修复搜索栏逻辑问题。 diff --git a/app/build.gradle.kts b/app/build.gradle.kts index eec8f6ea..573cadc4 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -16,7 +16,7 @@ android { minSdk = 24 targetSdk = 33 versionCode = createVersionCode() - versionName = createVersionName(major = 0, minor = 5, patch = 1) + versionName = createVersionName(major = 0, minor = 5, patch = 2) testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/java/com/yenaly/han1meviewer/ui/activity/AboutActivity.kt b/app/src/main/java/com/yenaly/han1meviewer/ui/activity/AboutActivity.kt index 3632bc5d..a1180bd5 100644 --- a/app/src/main/java/com/yenaly/han1meviewer/ui/activity/AboutActivity.kt +++ b/app/src/main/java/com/yenaly/han1meviewer/ui/activity/AboutActivity.kt @@ -114,16 +114,6 @@ class AboutActivity : AbsAboutActivity() { ), gapWidth = 4.dp ) } - newline(2) - "切換清晰度退出全屏會導致影片重置".span { - quote( - Color.rgb( - (0..255).random(), - (0..255).random(), - (0..255).random() - ), gapWidth = 4.dp - ) - } } )) } diff --git a/app/src/main/java/com/yenaly/han1meviewer/ui/activity/LoginActivity.kt b/app/src/main/java/com/yenaly/han1meviewer/ui/activity/LoginActivity.kt index ede5ba49..f796ff34 100644 --- a/app/src/main/java/com/yenaly/han1meviewer/ui/activity/LoginActivity.kt +++ b/app/src/main/java/com/yenaly/han1meviewer/ui/activity/LoginActivity.kt @@ -10,6 +10,7 @@ import android.webkit.CookieManager import android.webkit.WebResourceRequest import android.webkit.WebView import android.webkit.WebViewClient +import androidx.core.os.BuildCompat.PrereleaseSdkCheck import androidx.databinding.DataBindingUtil import com.itxca.spannablex.spannable import com.yenaly.han1meviewer.HANIME_BASE_URL @@ -29,6 +30,7 @@ class LoginActivity : FrameActivity() { SystemStatusUtil.fullScreen(window, true) } + @PrereleaseSdkCheck override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.activity_login) @@ -88,7 +90,7 @@ class LoginActivity : FrameActivity() { view: WebView, request: WebResourceRequest ): Boolean { - if (request.isRedirect && request.url.toString().contains(HANIME_BASE_URL)) { + if (request.isRedirect && request.url.toString() == HANIME_BASE_URL) { val url = request.url val cookieManager = CookieManager.getInstance().getCookie(url.host) Log.d("login_cookie", cookieManager.toString()) diff --git a/app/src/main/java/com/yenaly/han1meviewer/ui/activity/MainActivity.kt b/app/src/main/java/com/yenaly/han1meviewer/ui/activity/MainActivity.kt index 7286f72e..b7fb9fe1 100644 --- a/app/src/main/java/com/yenaly/han1meviewer/ui/activity/MainActivity.kt +++ b/app/src/main/java/com/yenaly/han1meviewer/ui/activity/MainActivity.kt @@ -5,8 +5,6 @@ import android.content.Intent import android.graphics.Color import android.graphics.Typeface import android.os.Bundle -import android.view.Menu -import android.view.MenuItem import android.widget.ImageView import android.widget.TextView import androidx.activity.result.contract.ActivityResultContracts @@ -23,13 +21,15 @@ import coil.transform.CircleCropTransformation import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar import com.itxca.spannablex.spannable -import com.yenaly.han1meviewer.* -import com.yenaly.han1meviewer.util.checkNeedUpdate +import com.yenaly.han1meviewer.R +import com.yenaly.han1meviewer.VIDEO_CODE import com.yenaly.han1meviewer.databinding.ActivityMainBinding +import com.yenaly.han1meviewer.isAlreadyLogin import com.yenaly.han1meviewer.logic.model.VersionModel import com.yenaly.han1meviewer.logic.state.WebsiteState import com.yenaly.han1meviewer.logout import com.yenaly.han1meviewer.ui.viewmodel.MainViewModel +import com.yenaly.han1meviewer.util.checkNeedUpdate import com.yenaly.yenaly_libs.base.YenalyActivity import com.yenaly.yenaly_libs.utils.* import kotlinx.coroutines.launch @@ -63,6 +63,22 @@ class MainActivity : YenalyActivity() { */ override fun initData(savedInstanceState: Bundle?) { setSupportActionBar(binding.toolbar) + + // 暂时先这样 + addMenu(R.menu.menu_main_toolbar) { item -> + when (item.itemId) { + R.id.tb_search -> { + startActivity() + return@addMenu true + } + R.id.tb_previews -> { + startActivity() + return@addMenu true + } + } + return@addMenu item.onNavDestinationSelected(navController) + } + supportActionBar?.let { it.title = spannable { "H".span { @@ -140,25 +156,6 @@ class MainActivity : YenalyActivity() { } } - override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.menu_main_toolbar, menu) - return super.onCreateOptionsMenu(menu) - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.tb_search -> { - startActivity() - return true - } - R.id.tb_previews -> { - startActivity() - return true - } - } - return item.onNavDestinationSelected(navController) || super.onOptionsItemSelected(item) - } - override fun onSupportNavigateUp(): Boolean { return navController.navigateUp() || super.onSupportNavigateUp() } diff --git a/app/src/main/java/com/yenaly/han1meviewer/ui/activity/VideoActivity.kt b/app/src/main/java/com/yenaly/han1meviewer/ui/activity/VideoActivity.kt index 40a01ab5..94333046 100644 --- a/app/src/main/java/com/yenaly/han1meviewer/ui/activity/VideoActivity.kt +++ b/app/src/main/java/com/yenaly/han1meviewer/ui/activity/VideoActivity.kt @@ -163,12 +163,9 @@ class VideoActivity : YenalyActivity(), private fun initViewPager() { - binding.videoVp.setUpFragmentStateAdapter(this, 2) { position -> - when (position) { - 0 -> VideoIntroductionFragment() - 1 -> CommentFragment().makeBundle(COMMENT_TYPE to VIDEO_COMMENT_PREFIX) - else -> null - } + binding.videoVp.setUpFragmentStateAdapter(this) { + addFragment { VideoIntroductionFragment() } + addFragment { CommentFragment().makeBundle(COMMENT_TYPE to VIDEO_COMMENT_PREFIX) } } binding.videoTl.attach(binding.videoVp) { tab, position -> diff --git a/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/DownloadFragment.kt b/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/DownloadFragment.kt index 9c19104f..c97e668c 100644 --- a/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/DownloadFragment.kt +++ b/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/DownloadFragment.kt @@ -1,7 +1,6 @@ package com.yenaly.han1meviewer.ui.fragment.home import android.os.Bundle -import androidx.fragment.app.Fragment import com.yenaly.han1meviewer.R import com.yenaly.han1meviewer.databinding.FragmentTabViewPagerOnlyBinding import com.yenaly.han1meviewer.ui.fragment.home.download.DownloadedFragment @@ -28,12 +27,9 @@ class DownloadFragment : YenalyFragment - when (position) { - 0 -> DownloadingFragment() - 1 -> DownloadedFragment() - else -> null - } + binding.viewPager.setUpFragmentStateAdapter(this) { + addFragment { DownloadingFragment() } + addFragment { DownloadedFragment() } } binding.tabLayout.attach(binding.viewPager) { tab, position -> diff --git a/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/MyFavVideoFragment.kt b/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/MyFavVideoFragment.kt index dc1e05af..e5ead49a 100644 --- a/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/MyFavVideoFragment.kt +++ b/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/MyFavVideoFragment.kt @@ -5,9 +5,6 @@ import android.content.res.Configuration import android.os.Bundle import android.util.Log import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem import android.widget.TextView import androidx.lifecycle.lifecycleScope import androidx.lifecycle.whenStarted @@ -46,7 +43,20 @@ class MyFavVideoFragment : YenalyFragment + when (menuItem.itemId) { + R.id.tb_help -> { + MaterialAlertDialogBuilder(requireContext()) + .setTitle("使用注意!") + .setMessage("左劃可以取消喜愛!") + .setPositiveButton("OK", null) + .show() + return@addMenu true + } + } + return@addMenu false + } binding.rvPageList.apply { layoutManager = GridLayoutManager(context, VIDEO_IN_ONE_LINE) @@ -151,25 +161,6 @@ class MyFavVideoFragment : YenalyFragment { - MaterialAlertDialogBuilder(requireContext()) - .setTitle("使用注意!") - .setMessage("左劃可以取消喜愛!") - .setPositiveButton("OK", null) - .show() - return true - } - } - return super.onOptionsItemSelected(item) - } - override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) binding.rvPageList.layoutManager = GridLayoutManager(context, VIDEO_IN_ONE_LINE) diff --git a/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/MyPlayListFragment.kt b/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/MyPlayListFragment.kt index 1f494ebb..b769cbd4 100644 --- a/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/MyPlayListFragment.kt +++ b/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/MyPlayListFragment.kt @@ -1,9 +1,6 @@ package com.yenaly.han1meviewer.ui.fragment.home import android.os.Bundle -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.yenaly.han1meviewer.R import com.yenaly.han1meviewer.databinding.FragmentPageListBinding @@ -19,30 +16,23 @@ import com.yenaly.yenaly_libs.base.YenalyFragment class MyPlayListFragment : YenalyFragment() { override fun initData(savedInstanceState: Bundle?) { (activity as? MainActivity)?.setToolbarSubtitle(getString(R.string.play_list)) - setHasOptionsMenu(true) + addMenu(R.menu.menu_my_list_toolbar, viewLifecycleOwner) { menuItem -> + when (menuItem.itemId) { + R.id.tb_help -> { + MaterialAlertDialogBuilder(requireContext()) + .setTitle("使用注意!") + .setMessage("我還沒做這塊,如果有想幫忙的非常歡迎!") + .setPositiveButton("OK", null) + .show() + return@addMenu true + } + } + return@addMenu false + } } override fun liveDataObserve() { } - - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - menu.clear() - inflater.inflate(R.menu.menu_my_list_toolbar, menu) - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.tb_help -> { - MaterialAlertDialogBuilder(requireContext()) - .setTitle("使用注意!") - .setMessage("我還沒做這塊,如果有想幫忙的非常歡迎!") - .setPositiveButton("OK", null) - .show() - return true - } - } - return super.onOptionsItemSelected(item) - } } \ No newline at end of file diff --git a/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/MyWatchLaterFragment.kt b/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/MyWatchLaterFragment.kt index 12fc8043..6cacee82 100644 --- a/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/MyWatchLaterFragment.kt +++ b/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/MyWatchLaterFragment.kt @@ -5,9 +5,6 @@ import android.content.res.Configuration import android.os.Bundle import android.util.Log import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem import android.widget.TextView import androidx.lifecycle.lifecycleScope import androidx.lifecycle.whenStarted @@ -46,7 +43,20 @@ class MyWatchLaterFragment : YenalyFragment + when (menuItem.itemId) { + R.id.tb_help -> { + MaterialAlertDialogBuilder(requireContext()) + .setTitle("使用注意!") + .setMessage("左劃可以取消待看!") + .setPositiveButton("OK", null) + .show() + return@addMenu true + } + } + return@addMenu false + } binding.rvPageList.apply { layoutManager = GridLayoutManager(context, VIDEO_IN_ONE_LINE) @@ -150,25 +160,6 @@ class MyWatchLaterFragment : YenalyFragment { - MaterialAlertDialogBuilder(requireContext()) - .setTitle("使用注意!") - .setMessage("左劃可以取消待看!") - .setPositiveButton("OK", null) - .show() - return true - } - } - return super.onOptionsItemSelected(item) - } - override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) binding.rvPageList.layoutManager = GridLayoutManager(context, VIDEO_IN_ONE_LINE) diff --git a/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/WatchHistoryFragment.kt b/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/WatchHistoryFragment.kt index c3fc68df..87f8a9ce 100644 --- a/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/WatchHistoryFragment.kt +++ b/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/WatchHistoryFragment.kt @@ -1,9 +1,6 @@ package com.yenaly.han1meviewer.ui.fragment.home import android.os.Bundle -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.ItemTouchHelper @@ -35,7 +32,32 @@ class WatchHistoryFragment : YenalyFragment + when (item.itemId) { + R.id.tb_delete -> { + // todo: strings.xml + MaterialAlertDialogBuilder(requireContext()) + .setTitle("看這裏!") + .setMessage("是否將影片觀看歷史記錄全部刪除🤔") + .setPositiveButton("是的!") { _, _ -> + viewModel.deleteAllWatchHistories() + } + .setNegativeButton("算了!", null) + .show() + return@addMenu true + } + R.id.tb_help -> { + MaterialAlertDialogBuilder(requireContext()) + .setTitle("使用注意!") + .setMessage("左劃可以刪除歷史記錄哦,右上角的刪除按鈕是負責刪除全部歷史記錄的!") + .setPositiveButton("OK", null) + .show() + return@addMenu true + } + } + return@addMenu false + } + binding.rvList.apply { layoutManager = LinearLayoutManager(context) adapter = historyAdapter @@ -86,34 +108,4 @@ class WatchHistoryFragment : YenalyFragment { - // todo: strings.xml - MaterialAlertDialogBuilder(requireContext()) - .setTitle("看這裏!") - .setMessage("是否將影片觀看歷史記錄全部刪除🤔") - .setPositiveButton("是的!") { _, _ -> - viewModel.deleteAllWatchHistories() - } - .setNegativeButton("算了!", null) - .show() - return true - } - R.id.tb_help -> { - MaterialAlertDialogBuilder(requireContext()) - .setTitle("使用注意!") - .setMessage("左劃可以刪除歷史記錄哦,右上角的刪除按鈕是負責刪除全部歷史記錄的!") - .setPositiveButton("OK", null) - .show() - } - } - return super.onOptionsItemSelected(item) - } } \ No newline at end of file diff --git a/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/download/DownloadedFragment.kt b/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/download/DownloadedFragment.kt index c084b90c..835cd6cb 100644 --- a/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/download/DownloadedFragment.kt +++ b/app/src/main/java/com/yenaly/han1meviewer/ui/fragment/home/download/DownloadedFragment.kt @@ -1,8 +1,6 @@ package com.yenaly.han1meviewer.ui.fragment.home.download import android.os.Bundle -import android.view.Menu -import android.view.MenuInflater import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager @@ -27,7 +25,8 @@ class DownloadedFragment : YenalyFragment Boolean + ) { + addMenuProvider(object : MenuProvider { + override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { + menuInflater.inflate(menuRes, menu) + } + + override fun onMenuItemSelected(menuItem: MenuItem): Boolean { + return action.invoke(menuItem) + } + }) + } + + /** + * 详情 [addMenu] + */ + open fun addMenu( + @MenuRes menuRes: Int, + owner: LifecycleOwner, + action: (menuItem: MenuItem) -> Boolean + ) { + addMenuProvider(object : MenuProvider { + override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { + menuInflater.inflate(menuRes, menu) + } + + override fun onMenuItemSelected(menuItem: MenuItem): Boolean { + return action.invoke(menuItem) + } + }, owner) + } + + /** + * 详情 [addMenu] + */ + open fun addMenu( + @MenuRes menuRes: Int, + owner: LifecycleOwner, + state: Lifecycle.State, + action: (menuItem: MenuItem) -> Boolean + ) { + addMenuProvider(object : MenuProvider { + override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { + menuInflater.inflate(menuRes, menu) + } + + override fun onMenuItemSelected(menuItem: MenuItem): Boolean { + return action.invoke(menuItem) + } + }, owner, state) } } \ No newline at end of file diff --git a/yenaly_libs/src/main/java/com/yenaly/yenaly_libs/base/frame/FrameFragment.kt b/yenaly_libs/src/main/java/com/yenaly/yenaly_libs/base/frame/FrameFragment.kt index 4e2c2480..3b714d21 100644 --- a/yenaly_libs/src/main/java/com/yenaly/yenaly_libs/base/frame/FrameFragment.kt +++ b/yenaly_libs/src/main/java/com/yenaly/yenaly_libs/base/frame/FrameFragment.kt @@ -1,28 +1,29 @@ package com.yenaly.yenaly_libs.base.frame -import android.view.LayoutInflater -import android.view.ViewGroup +import android.view.* import android.widget.TextView +import androidx.annotation.MenuRes import androidx.appcompat.app.AlertDialog +import androidx.core.view.MenuProvider import androidx.fragment.app.Fragment +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.yenaly.yenaly_libs.R import com.yenaly.yenaly_libs.utils.dp /** - * @ProjectName : YenalyModule - * @Author : Yenaly Liew - * @Time : 2022/04/16 016 20:25 - * @Description : Description... + * @author : Yenaly Liew + * @time : 2022/04/16 016 20:25 */ abstract class FrameFragment : Fragment() { - val window get() = requireActivity().window + val window: Window get() = requireActivity().window private lateinit var loadingDialog: AlertDialog @JvmOverloads - fun showLoadingDialog( + open fun showLoadingDialog( loadingText: String = getString(R.string.yenaly_loading), cancelable: Boolean = false, dialogWidth: Int = 260.dp, @@ -39,7 +40,7 @@ abstract class FrameFragment : Fragment() { loadingDialog.window?.setLayout(dialogWidth, dialogHeight) } - fun hideLoadingDialog() { + open fun hideLoadingDialog() { if (this::loadingDialog.isInitialized) { loadingDialog.hide() } @@ -51,4 +52,92 @@ abstract class FrameFragment : Fragment() { loadingDialog.dismiss() } } + + /** + * 快捷构建 Menu + * + * 使用了最新 API,创建菜单更简单。 + * + * @param menuRes menuRes。 + * @param clear 是否清除从上一界面尤其是 Activity 继承过来的 Menu Item,默认为 true。 + * @param action 和 [onOptionsItemSelected] 用法一致。 + */ + open fun addMenu( + @MenuRes menuRes: Int, + clear: Boolean = true, + action: (menuItem: MenuItem) -> Boolean + ) { + requireActivity().addMenuProvider(object : MenuProvider { + override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { + // (Optional) 加入 menu.clear() 的原因是,若不加入,如果 Activity 有构建过 Menu,切换到 + // Fragment 会导致 Activity 的 Menu 继承到 Fragment 里,clear 一下 + // 可以使得该 Fragment 能使用自己唯一的 Menu. + // Fragment 之间的 Menu 问题只需要通过指定 lifecycle 就能搞定,用下面的方法。 + if (clear) menu.clear() + menuInflater.inflate(menuRes, menu) + } + + override fun onMenuItemSelected(menuItem: MenuItem): Boolean { + return action.invoke(menuItem) + } + }) + } + + /** + * 详情 [addMenu],最好使用该带 lifecycleOwner 的方法。 + */ + open fun addMenu( + @MenuRes menuRes: Int, + owner: LifecycleOwner, + clear: Boolean = true, + action: (menuItem: MenuItem) -> Boolean + ) { + requireActivity().addMenuProvider(object : MenuProvider { + override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { + if (clear) menu.clear() + menuInflater.inflate(menuRes, menu) + } + + override fun onMenuItemSelected(menuItem: MenuItem): Boolean { + return action.invoke(menuItem) + } + }, owner) + } + + /** + * 详情 [addMenu] + */ + open fun addMenu( + @MenuRes menuRes: Int, + owner: LifecycleOwner, + state: Lifecycle.State, + clear: Boolean = true, + action: (menuItem: MenuItem) -> Boolean + ) { + requireActivity().addMenuProvider(object : MenuProvider { + override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { + if (clear) menu.clear() + menuInflater.inflate(menuRes, menu) + } + + override fun onMenuItemSelected(menuItem: MenuItem): Boolean { + return action.invoke(menuItem) + } + }, owner, state) + } + + /** + * 清除 Menu 所有元素 + */ + open fun clearMenu() { + requireActivity().addMenuProvider(object : MenuProvider { + override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { + menu.clear() + } + + override fun onMenuItemSelected(menuItem: MenuItem): Boolean { + return false + } + }, viewLifecycleOwner) + } } \ No newline at end of file diff --git a/yenaly_libs/src/main/java/com/yenaly/yenaly_libs/utils/ActivityUtil.kt b/yenaly_libs/src/main/java/com/yenaly/yenaly_libs/utils/ActivityUtil.kt deleted file mode 100644 index 67092ef1..00000000 --- a/yenaly_libs/src/main/java/com/yenaly/yenaly_libs/utils/ActivityUtil.kt +++ /dev/null @@ -1,10 +0,0 @@ -@file:JvmName("ActivityUtil") - -package com.yenaly.yenaly_libs.utils - -import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.FragmentTransaction - -inline fun AppCompatActivity.inFragmentManagerTransaction( - action: FragmentTransaction.() -> Unit -) = supportFragmentManager.beginTransaction().apply(action).commit() \ No newline at end of file diff --git a/yenaly_libs/src/main/java/com/yenaly/yenaly_libs/utils/view/BottomNavViewUtil.kt b/yenaly_libs/src/main/java/com/yenaly/yenaly_libs/utils/view/BottomNavViewUtil.kt index bb47cb19..d74d9099 100644 --- a/yenaly_libs/src/main/java/com/yenaly/yenaly_libs/utils/view/BottomNavViewUtil.kt +++ b/yenaly_libs/src/main/java/com/yenaly/yenaly_libs/utils/view/BottomNavViewUtil.kt @@ -6,6 +6,7 @@ import android.view.View import android.view.ViewGroup import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.view.updateLayoutParams +import androidx.viewpager2.widget.ViewPager2 import com.google.android.material.behavior.HideBottomViewOnScrollBehavior import com.google.android.material.bottomnavigation.BottomNavigationView @@ -37,4 +38,10 @@ fun BottomNavigationView.toggleBottomNavBehavior(view: View, scrollToHide: Boole ) } } -} \ No newline at end of file +} + +fun BottomNavigationView.attachViewPager2( + viewPager2: ViewPager2, + smoothScroll: Boolean = true, + addAction: SimpleBottomNavViewMediator.() -> Unit +) = SimpleBottomNavViewMediator(this, viewPager2, smoothScroll).apply(addAction).attach() \ No newline at end of file diff --git a/yenaly_libs/src/main/java/com/yenaly/yenaly_libs/utils/view/SimpleBottomNavViewMediator.kt b/yenaly_libs/src/main/java/com/yenaly/yenaly_libs/utils/view/SimpleBottomNavViewMediator.kt new file mode 100644 index 00000000..9ec74825 --- /dev/null +++ b/yenaly_libs/src/main/java/com/yenaly/yenaly_libs/utils/view/SimpleBottomNavViewMediator.kt @@ -0,0 +1,111 @@ +package com.yenaly.yenaly_libs.utils.view + +import android.util.SparseArray +import android.util.SparseIntArray +import androidx.annotation.IdRes +import androidx.core.util.isNotEmpty +import androidx.core.util.set +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.recyclerview.widget.RecyclerView +import androidx.viewpager2.adapter.FragmentStateAdapter +import androidx.viewpager2.widget.ViewPager2 +import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback +import com.google.android.material.bottomnavigation.BottomNavigationView +import com.google.android.material.navigation.NavigationBarView +import com.yenaly.yenaly_libs.utils.activity + +/** + * @project Han1meViewer + * @author Yenaly Liew + * @time 2022/11/09 009 14:39 + */ +class SimpleBottomNavViewMediator constructor( + private val bottomNavigationView: BottomNavigationView, + private val viewPager2: ViewPager2, + var smoothScroll: Boolean = true +) { + + private val fragmentActivity = bottomNavigationView.context.activity as? FragmentActivity + ?: throw IllegalStateException("context cannot be cast to FragmentActivity!") + + private var currentFragment: Fragment? = null + private var onFragmentChangedListener: OnFragmentChangedListener? = null + + private var attached = false + + private var index = 0 + private val newFragmentMap = SparseArray() + private val indexMap = SparseIntArray() + private val newFragmentList = mutableListOf>() + + private var viewPager2Adapter: RecyclerView.Adapter<*>? = null + private var onItemSelectedListener: NavigationBarView.OnItemSelectedListener? = null + private var onPageChangeCallback: OnPageChangeCallback? = null + + fun attach() = apply { + check(!attached) { "${javaClass.simpleName} is already attached" } + check(newFragmentMap.isNotEmpty()) { "fragment list could not be empty!" } + + viewPager2Adapter = object : FragmentStateAdapter(fragmentActivity) { + override fun getItemCount(): Int { + return index + } + + override fun createFragment(position: Int): Fragment { + return newFragmentList[position].second.invoke() + } + } + + onItemSelectedListener = NavigationBarView.OnItemSelectedListener { item -> + val currentIndex = indexMap[item.itemId] + viewPager2.setCurrentItem(currentIndex, smoothScroll) + return@OnItemSelectedListener true + } + + onPageChangeCallback = object : OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + bottomNavigationView.selectedItemId = newFragmentList[position].first + onFragmentChangedListener?.onFragmentChanged( + fragmentActivity.supportFragmentManager.findFragmentByTag( + "f${position}" + )!!.also { currentFragment = it } + ) + } + } + + viewPager2.adapter = viewPager2Adapter + bottomNavigationView.setOnItemSelectedListener(onItemSelectedListener!!) + viewPager2.registerOnPageChangeCallback(onPageChangeCallback!!) + + attached = true + } + + fun detach() = apply { + TODO("I am lazy!") + } + + /** + * For example: + * + * `R.id.xxx with { XXFragment() }` + */ + infix fun @receiver:IdRes Int.with(newFragment: NewFragment) { + newFragmentMap[this] = newFragment + indexMap[this] = index + newFragmentList += this to newFragment + index++ + } + + /** + * Set on a callback interface that is optionally + * implemented to listen the latest selected fragment. + */ + fun setOnFragmentChangedListener(listener: OnFragmentChangedListener) { + this.onFragmentChangedListener = listener + } + + fun interface OnFragmentChangedListener { + fun onFragmentChanged(currentFragment: Fragment) + } +} \ No newline at end of file diff --git a/yenaly_libs/src/main/java/com/yenaly/yenaly_libs/utils/view/SimpleFragmentStateAdapter.kt b/yenaly_libs/src/main/java/com/yenaly/yenaly_libs/utils/view/SimpleFragmentStateAdapter.kt new file mode 100644 index 00000000..1c8b71ca --- /dev/null +++ b/yenaly_libs/src/main/java/com/yenaly/yenaly_libs/utils/view/SimpleFragmentStateAdapter.kt @@ -0,0 +1,37 @@ +package com.yenaly.yenaly_libs.utils.view + +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.fragment.app.FragmentManager +import androidx.lifecycle.Lifecycle +import androidx.viewpager2.adapter.FragmentStateAdapter + +/** + * @author Yenaly Liew + * @time 2022/11/09 009 14:05 + */ +class SimpleFragmentStateAdapter : FragmentStateAdapter { + + private val newFragmentList = mutableListOf() + + constructor(fragmentActivity: FragmentActivity) : + super(fragmentActivity) + + constructor(fragment: Fragment) : + super(fragment) + + constructor(fragmentManager: FragmentManager, lifecycle: Lifecycle) : + super(fragmentManager, lifecycle) + + override fun getItemCount(): Int { + return newFragmentList.size + } + + override fun createFragment(position: Int): Fragment { + return newFragmentList[position].invoke() + } + + fun addFragment(newFragment: NewFragment) { + newFragmentList.add(newFragment) + } +} \ No newline at end of file diff --git a/yenaly_libs/src/main/java/com/yenaly/yenaly_libs/utils/view/ViewPagerUtil.kt b/yenaly_libs/src/main/java/com/yenaly/yenaly_libs/utils/view/ViewPagerUtil.kt index 1e095fe4..58a533d7 100644 --- a/yenaly_libs/src/main/java/com/yenaly/yenaly_libs/utils/view/ViewPagerUtil.kt +++ b/yenaly_libs/src/main/java/com/yenaly/yenaly_libs/utils/view/ViewPagerUtil.kt @@ -5,9 +5,10 @@ package com.yenaly.yenaly_libs.utils.view import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.recyclerview.widget.RecyclerView -import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.widget.ViewPager2 +typealias NewFragment = () -> Fragment + /** * 如果直接给ViewPager2设置overScrollMode会无效, * 这种方式能设置有效的overScrollMode @@ -34,15 +35,9 @@ inline var ViewPager2.realOverScrollMode: Int @Suppress("NOTHING_TO_INLINE") inline fun ViewPager2.setUpFragmentStateAdapter( fragmentActivity: FragmentActivity, - itemCount: Int, - crossinline init: (position: Int) -> Fragment? + crossinline addAction: SimpleFragmentStateAdapter.() -> Unit ) { - adapter = object : FragmentStateAdapter(fragmentActivity) { - override fun getItemCount() = itemCount - - override fun createFragment(position: Int): Fragment = - init.invoke(position) ?: throw IllegalStateException("WTF? Fragment is null?") - } + adapter = SimpleFragmentStateAdapter(fragmentActivity).apply(addAction) } /** @@ -51,13 +46,7 @@ inline fun ViewPager2.setUpFragmentStateAdapter( @Suppress("NOTHING_TO_INLINE") inline fun ViewPager2.setUpFragmentStateAdapter( fragment: Fragment, - itemCount: Int, - crossinline init: (position: Int) -> Fragment? + crossinline addAction: SimpleFragmentStateAdapter.() -> Unit ) { - adapter = object : FragmentStateAdapter(fragment) { - override fun getItemCount() = itemCount - - override fun createFragment(position: Int) = - init.invoke(position) ?: throw IllegalStateException("WTF? Fragment is null?") - } + adapter = SimpleFragmentStateAdapter(fragment).apply(addAction) } \ No newline at end of file