diff --git a/app/src/main/java/net/opendasharchive/openarchive/features/main/MainActivity.kt b/app/src/main/java/net/opendasharchive/openarchive/features/main/MainActivity.kt index 2903b487..34e4aefc 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/features/main/MainActivity.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/features/main/MainActivity.kt @@ -2,8 +2,6 @@ package net.opendasharchive.openarchive.features.main import android.Manifest import android.annotation.SuppressLint -import android.content.BroadcastReceiver -import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.os.Build @@ -16,7 +14,7 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.widget.TooltipCompat import androidx.core.content.ContextCompat import androidx.recyclerview.widget.LinearLayoutManager -import androidx.viewpager.widget.ViewPager +import androidx.viewpager2.widget.ViewPager2 import com.esafirm.imagepicker.features.ImagePickerLauncher import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar.SnackbarLayout @@ -33,13 +31,12 @@ import net.opendasharchive.openarchive.databinding.ActivityMainBinding import net.opendasharchive.openarchive.db.Project import net.opendasharchive.openarchive.db.Space import net.opendasharchive.openarchive.features.core.BaseActivity +import net.opendasharchive.openarchive.features.folders.AddFolderActivity import net.opendasharchive.openarchive.features.media.AddMediaDialogFragment import net.opendasharchive.openarchive.features.media.Picker import net.opendasharchive.openarchive.features.media.PreviewActivity import net.opendasharchive.openarchive.features.onboarding.Onboarding23Activity import net.opendasharchive.openarchive.features.onboarding.SpaceSetupActivity -import net.opendasharchive.openarchive.features.folders.AddFolderActivity -import net.opendasharchive.openarchive.upload.BroadcastManager import net.opendasharchive.openarchive.upload.UploadManagerActivity import net.opendasharchive.openarchive.upload.UploadService import net.opendasharchive.openarchive.util.AlertHelper @@ -81,31 +78,6 @@ class MainActivity : BaseActivity(), FolderAdapterListener, SpaceAdapterListener updateBottomNavbar(value) } - private val mCurrentFragment - get() = mPagerAdapter.getRegisteredMediaFragment(mCurrentItem) - - private val mMessageReceiver: BroadcastReceiver = object : BroadcastReceiver() { - - override fun onReceive(context: Context, intent: Intent) { - if (mCurrentItem >= mPagerAdapter.settingsIndex) return - - val action = BroadcastManager.getAction(intent) - val mediaId = action?.mediaId ?: return - - if (mediaId < 0) return - - when (action) { - BroadcastManager.Action.Change -> { - mCurrentFragment?.updateItem(mediaId) - } - - BroadcastManager.Action.Delete -> { - mCurrentFragment?.refresh() - } - } - } - } - private val mNewFolderResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { if (it.resultCode == RESULT_OK) { @@ -140,13 +112,10 @@ class MainActivity : BaseActivity(), FolderAdapterListener, SpaceAdapterListener startActivity(Intent(this, UploadManagerActivity::class.java)) } - mPagerAdapter = ProjectAdapter(supportFragmentManager) + mPagerAdapter = ProjectAdapter(supportFragmentManager, lifecycle) mBinding.pager.adapter = mPagerAdapter - // final int pageMargin = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 8, getResources() .getDisplayMetrics()); - mBinding.pager.pageMargin = 0 - - mBinding.pager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { + mBinding.pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { override fun onPageScrolled( position: Int, positionOffset: Float, positionOffsetPixels: Int @@ -223,11 +192,17 @@ class MainActivity : BaseActivity(), FolderAdapterListener, SpaceAdapterListener true } - supportFragmentManager.setFragmentResultListener(AddMediaDialogFragment.RESP_PHOTO_GALLERY, this) { - _, _ -> addClicked() + supportFragmentManager.setFragmentResultListener( + AddMediaDialogFragment.RESP_PHOTO_GALLERY, + this + ) { _, _ -> + addClicked() } - supportFragmentManager.setFragmentResultListener(AddMediaDialogFragment.RESP_FILES, this) { - _, _ -> addClicked(typeFiles = true) + supportFragmentManager.setFragmentResultListener( + AddMediaDialogFragment.RESP_FILES, + this + ) { _, _ -> + addClicked(typeFiles = true) } } } @@ -246,14 +221,9 @@ class MainActivity : BaseActivity(), FolderAdapterListener, SpaceAdapterListener override fun onResume() { super.onResume() - BroadcastManager.register(this, mMessageReceiver) - refreshSpace() - if (mLastItem == mPagerAdapter.settingsIndex) { - // Display settings when returning from deeper setting activities. - mCurrentItem = mLastItem - } + mCurrentItem = mLastItem if (Space.current?.host.isNullOrEmpty()) { startActivity(Intent(this, Onboarding23Activity::class.java)) @@ -282,12 +252,6 @@ class MainActivity : BaseActivity(), FolderAdapterListener, SpaceAdapterListener } } - override fun onPause() { - super.onPause() - - BroadcastManager.unregister(this, mMessageReceiver) - } - override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) @@ -302,16 +266,7 @@ class MainActivity : BaseActivity(), FolderAdapterListener, SpaceAdapterListener } override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.menu_delete -> { - AlertHelper.show(this, R.string.confirm_remove_media, null, buttons = listOf( - AlertHelper.positiveButton(R.string.remove) { _, _ -> - mCurrentFragment?.deleteSelected() - }, - AlertHelper.negativeButton())) - - } - + return when (item.itemId) { R.id.menu_folders -> { // https://stackoverflow.com/questions/21796209/how-to-create-a-custom-navigation-drawer-in-android @@ -320,10 +275,11 @@ class MainActivity : BaseActivity(), FolderAdapterListener, SpaceAdapterListener } else { mBinding.root.openDrawer(mBinding.folderBar) } + true } - } - return super.onOptionsItemSelected(item) + else -> super.onOptionsItemSelected(item) + } } fun updateAfterDelete(done: Boolean) { @@ -360,12 +316,13 @@ class MainActivity : BaseActivity(), FolderAdapterListener, SpaceAdapterListener private fun refreshProjects(setProjectId: Long? = null) { val projects = Space.current?.projects ?: emptyList() - val project = projects.firstOrNull { it.id == setProjectId } ?: getSelectedProject() - mPagerAdapter.updateData(projects) mBinding.pager.adapter = mPagerAdapter - mCurrentItem = mPagerAdapter.getIndex(project) + + setProjectId?.let { + mCurrentItem = mPagerAdapter.getProjectIndexById(it, default = 0) + } mFolderAdapter.update(projects) @@ -376,7 +333,7 @@ class MainActivity : BaseActivity(), FolderAdapterListener, SpaceAdapterListener val project = getSelectedProject() if (project != null) { - mCurrentFragment?.refresh() + mPagerAdapter.notifyProjectChanged(project) project.space?.setAvatar(mBinding.currentFolderIcon) mBinding.currentFolderIcon.show() @@ -401,8 +358,7 @@ class MainActivity : BaseActivity(), FolderAdapterListener, SpaceAdapterListener mBinding.currentFolderCount.show() mBinding.uploadEditButton.toggle(project.isUploading) - } - else { + } else { mBinding.currentFolderCount.cloak() mBinding.uploadEditButton.hide() } @@ -513,8 +469,7 @@ class MainActivity : BaseActivity(), FolderAdapterListener, SpaceAdapterListener if (getSelectedProject() != null) { if (typeFiles && Picker.canPickFiles(this)) { Picker.pickFiles(mFilePickerLauncher) - } - else { + } else { pickMedia() } } else { @@ -548,4 +503,4 @@ class MainActivity : BaseActivity(), FolderAdapterListener, SpaceAdapterListener mBinding.settingsButton.setIconResource(R.drawable.ic_settings) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/net/opendasharchive/openarchive/features/main/MainMediaFragment.kt b/app/src/main/java/net/opendasharchive/openarchive/features/main/MainMediaFragment.kt index 637f0a05..8aaa3f7e 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/features/main/MainMediaFragment.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/features/main/MainMediaFragment.kt @@ -1,17 +1,24 @@ package net.opendasharchive.openarchive.features.main +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent import android.os.Bundle import android.view.LayoutInflater +import android.view.MenuItem import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.recyclerview.widget.GridLayoutManager +import net.opendasharchive.openarchive.R import net.opendasharchive.openarchive.databinding.FragmentMainMediaBinding import net.opendasharchive.openarchive.databinding.ViewSectionBinding import net.opendasharchive.openarchive.db.Collection import net.opendasharchive.openarchive.db.Media import net.opendasharchive.openarchive.db.MediaAdapter import net.opendasharchive.openarchive.db.MediaViewHolder +import net.opendasharchive.openarchive.upload.BroadcastManager +import net.opendasharchive.openarchive.util.AlertHelper import net.opendasharchive.openarchive.util.extensions.toggle import kotlin.collections.set @@ -39,6 +46,59 @@ class MainMediaFragment : Fragment() { private lateinit var mBinding: FragmentMainMediaBinding + private val mMessageReceiver: BroadcastReceiver = object : BroadcastReceiver() { + + override fun onReceive(context: Context, intent: Intent) { + val action = BroadcastManager.getAction(intent) + val mediaId = action?.mediaId ?: return + + if (mediaId < 0) return + + when (action) { + BroadcastManager.Action.Change -> { + updateItem(mediaId) + } + + BroadcastManager.Action.Delete -> { + refresh() + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + override fun onStart() { + super.onStart() + BroadcastManager.register(requireContext(), mMessageReceiver) + } + + override fun onStop() { + super.onStop() + BroadcastManager.unregister(requireContext(), mMessageReceiver) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.menu_delete -> { + AlertHelper.show( + requireContext(), R.string.confirm_remove_media, null, buttons = listOf( + AlertHelper.positiveButton(R.string.remove) { _, _ -> + deleteSelected() + }, + AlertHelper.negativeButton() + ) + ) + true + } + + else -> super.onOptionsItemSelected(item) + } + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -63,8 +123,8 @@ class MainMediaFragment : Fragment() { mCollections = ArrayList(Collection.getByProject(mProjectId)) // Remove all sections, which' collections don't exist anymore. - val toDelete = mAdapters.keys.filter { - id -> mCollections.firstOrNull { it.id == id } == null + val toDelete = mAdapters.keys.filter { id -> + mCollections.firstOrNull { it.id == id } == null }.toMutableList() mCollections.forEach { collection -> @@ -82,8 +142,7 @@ class MainMediaFragment : Fragment() { if (adapter != null) { adapter.updateData(media) holder?.setHeader(collection, media) - } - else if (media.isNotEmpty()) { + } else if (media.isNotEmpty()) { val view = createMediaList(collection, media) mBinding.mediaContainer.addView(view, 0) @@ -94,7 +153,9 @@ class MainMediaFragment : Fragment() { // while adding images. deleteCollections(toDelete, false) - mBinding.addMediaHint.toggle(mCollections.isEmpty()) + if (::mBinding.isInitialized) { + mBinding.addMediaHint.toggle(mCollections.isEmpty()) + } } fun deleteSelected() { @@ -106,8 +167,7 @@ class MainMediaFragment : Fragment() { if (media.isEmpty()) { toDelete.add(collection.id) - } - else { + } else { mSection[collection.id]?.setHeader(collection, media) } } diff --git a/app/src/main/java/net/opendasharchive/openarchive/features/main/ProjectAdapter.kt b/app/src/main/java/net/opendasharchive/openarchive/features/main/ProjectAdapter.kt index 01df41ed..35e1d99c 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/features/main/ProjectAdapter.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/features/main/ProjectAdapter.kt @@ -1,68 +1,61 @@ package net.opendasharchive.openarchive.features.main -import android.os.Parcelable import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager +import androidx.lifecycle.Lifecycle +import androidx.viewpager2.adapter.FragmentStateAdapter import net.opendasharchive.openarchive.db.Project import net.opendasharchive.openarchive.features.settings.SettingsFragment -import net.opendasharchive.openarchive.util.SmartFragmentStatePagerAdapter -import timber.log.Timber import kotlin.math.max -class ProjectAdapter(fragmentManager: FragmentManager) : SmartFragmentStatePagerAdapter(fragmentManager) { +class ProjectAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) : + FragmentStateAdapter(fragmentManager, lifecycle) { var projects = listOf() private set - override fun getItem(position: Int): Fragment { - if (position == settingsIndex) { - return SettingsFragment() - } - - val project = getProject(position) - - return MainMediaFragment.newInstance(project?.id ?: -1) - } - - override fun getCount(): Int { - return max(1, projects.size) + 1 - } - fun getProject(i: Int): Project? { return if (i > -1 && i < projects.size) projects[i] else null } val settingsIndex: Int - get() = count - 1 + get() = projects.size fun updateData(projects: List) { this.projects = projects - - notifyDataSetChanged() + notifyItemRangeChanged(0, projects.size) } - fun getIndex(project: Project?): Int { - if (project == null) { - return 0 - } - - return (projects.indexOf(project)).coerceAtLeast(0) + fun getPageTitle(position: Int): CharSequence? { + return getProject(position)?.description } - override fun getPageTitle(position: Int): CharSequence? { - return getProject(position)?.description + override fun getItemCount(): Int { + return max(1, projects.size) + 1 } - fun getRegisteredMediaFragment(position: Int): MainMediaFragment? { - return getRegisteredFragment(position) as? MainMediaFragment + fun getProjectIndexById(id: Long, default: Int = 0): Int { + val index = projects.indexOfFirst { it.id == id } + return when (index) { + -1 -> default + else -> index + } } - override fun restoreState(state: Parcelable?, loader: ClassLoader?) { - try { - super.restoreState(state, loader) + fun notifyProjectChanged(project: Project) { + val index = projects.indexOf(project) + if (index != -1) { + notifyItemChanged(index) } - catch (e: Exception) { - Timber.e("restoreState failed", e) + } + + override fun createFragment(position: Int): Fragment { + return when (position) { + settingsIndex -> SettingsFragment() + else -> { + val project = getProject(position) + return MainMediaFragment.newInstance(project?.id ?: -1) + } } } -} \ No newline at end of file +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index a508a2ae..5fce1099 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -103,7 +103,7 @@ -