From 1d69bd48beb9df0e5761c80f0ab4e7bae38900a4 Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Sun, 28 Nov 2021 16:13:14 +0100 Subject: [PATCH 01/12] Moved import/export options to menu --- .../subscription/SubscriptionFragment.kt | 121 ++++++++++------- .../subscription/item/FeedImportExportItem.kt | 122 ------------------ .../res/layout/feed_import_export_group.xml | 114 ---------------- .../subscription_import_export_item.xml | 32 ----- 4 files changed, 73 insertions(+), 316 deletions(-) delete mode 100644 app/src/main/java/org/schabi/newpipe/local/subscription/item/FeedImportExportItem.kt delete mode 100644 app/src/main/res/layout/feed_import_export_group.xml delete mode 100644 app/src/main/res/layout/subscription_import_export_item.xml diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt index 008228083..a980e940c 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt @@ -1,24 +1,24 @@ package org.schabi.newpipe.local.subscription import android.app.Activity -import android.content.BroadcastReceiver import android.content.Context import android.content.DialogInterface import android.content.Intent -import android.content.IntentFilter import android.os.Bundle import android.os.Parcelable import android.view.LayoutInflater import android.view.Menu import android.view.MenuInflater +import android.view.MenuItem +import android.view.SubMenu import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.activity.result.ActivityResult import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult +import androidx.annotation.StringRes import androidx.appcompat.app.AlertDialog import androidx.lifecycle.ViewModelProvider -import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.recyclerview.widget.GridLayoutManager import com.xwray.groupie.Group import com.xwray.groupie.GroupAdapter @@ -34,7 +34,9 @@ import org.schabi.newpipe.databinding.FeedItemCarouselBinding import org.schabi.newpipe.databinding.FragmentSubscriptionBinding import org.schabi.newpipe.error.ErrorInfo import org.schabi.newpipe.error.UserAction +import org.schabi.newpipe.extractor.NewPipe import org.schabi.newpipe.extractor.channel.ChannelInfoItem +import org.schabi.newpipe.extractor.exceptions.ExtractionException import org.schabi.newpipe.fragments.BaseStateFragment import org.schabi.newpipe.ktx.animate import org.schabi.newpipe.local.subscription.SubscriptionViewModel.SubscriptionState @@ -45,13 +47,10 @@ import org.schabi.newpipe.local.subscription.item.EmptyPlaceholderItem import org.schabi.newpipe.local.subscription.item.FeedGroupAddItem import org.schabi.newpipe.local.subscription.item.FeedGroupCardItem import org.schabi.newpipe.local.subscription.item.FeedGroupCarouselItem -import org.schabi.newpipe.local.subscription.item.FeedImportExportItem import org.schabi.newpipe.local.subscription.item.HeaderWithMenuItem import org.schabi.newpipe.local.subscription.item.HeaderWithMenuItem.Companion.PAYLOAD_UPDATE_VISIBILITY_MENU_ITEM import org.schabi.newpipe.local.subscription.services.SubscriptionsExportService -import org.schabi.newpipe.local.subscription.services.SubscriptionsExportService.EXPORT_COMPLETE_ACTION import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService -import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.IMPORT_COMPLETE_ACTION import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_MODE import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_VALUE import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.PREVIOUS_EXPORT_MODE @@ -65,6 +64,7 @@ import org.schabi.newpipe.util.external_communication.ShareUtils import java.text.SimpleDateFormat import java.util.Date import java.util.Locale +import java.util.function.Supplier class SubscriptionFragment : BaseStateFragment() { private var _binding: FragmentSubscriptionBinding? = null @@ -74,12 +74,9 @@ class SubscriptionFragment : BaseStateFragment() { private lateinit var subscriptionManager: SubscriptionManager private val disposables: CompositeDisposable = CompositeDisposable() - private var subscriptionBroadcastReceiver: BroadcastReceiver? = null - private val groupAdapter = GroupAdapter>() private val feedGroupsSection = Section() private var feedGroupsCarousel: FeedGroupCarouselItem? = null - private lateinit var importExportItem: FeedImportExportItem private lateinit var feedGroupsSortMenuItem: HeaderWithMenuItem private val subscriptionsSection = Section() @@ -94,9 +91,6 @@ class SubscriptionFragment : BaseStateFragment() { @State @JvmField var feedGroupsListState: Parcelable? = null - @State - @JvmField - var importExportItemExpandedState: Boolean? = null init { setHasOptionsMenu(true) @@ -120,20 +114,10 @@ class SubscriptionFragment : BaseStateFragment() { return inflater.inflate(R.layout.fragment_subscription, container, false) } - override fun onResume() { - super.onResume() - setupBroadcastReceiver() - } - override fun onPause() { super.onPause() itemsListState = binding.itemsList.layoutManager?.onSaveInstanceState() feedGroupsListState = feedGroupsCarousel?.onSaveInstanceState() - importExportItemExpandedState = importExportItem.isExpanded - - if (subscriptionBroadcastReceiver != null && activity != null) { - LocalBroadcastManager.getInstance(activity).unregisterReceiver(subscriptionBroadcastReceiver!!) - } } override fun onDestroy() { @@ -150,28 +134,75 @@ class SubscriptionFragment : BaseStateFragment() { activity.supportActionBar?.setDisplayShowTitleEnabled(true) activity.supportActionBar?.setTitle(R.string.tab_subscriptions) + + buildImportExportMenu(menu) } - private fun setupBroadcastReceiver() { - if (activity == null) return + private fun buildImportExportMenu(menu: Menu) { + // -- Import -- - if (subscriptionBroadcastReceiver != null) { - LocalBroadcastManager.getInstance(activity).unregisterReceiver(subscriptionBroadcastReceiver!!) + val importSubMenu = menu.addSubMenu(R.string.import_from) + + addMenuItem(importSubMenu, R.string.previous_export) { + onImportPreviousSelected() } - val filters = IntentFilter() - filters.addAction(EXPORT_COMPLETE_ACTION) - filters.addAction(IMPORT_COMPLETE_ACTION) - subscriptionBroadcastReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - _binding?.itemsList?.post { - importExportItem.isExpanded = false - importExportItem.notifyChanged(FeedImportExportItem.REFRESH_EXPANDED_STATUS) + val services = requireContext().resources.getStringArray(R.array.service_list) + for (serviceName in services) { + try { + val service = NewPipe.getService(serviceName) + + val subscriptionExtractor = service.subscriptionExtractor ?: continue + + val supportedSources = subscriptionExtractor.supportedSources + if (supportedSources.isEmpty()) continue + + addMenuItem(importSubMenu, serviceName) { + onImportFromServiceSelected(service.serviceId) } + } catch (e: ExtractionException) { + throw RuntimeException( + "Services array contains an entry that it's not a valid service name ($serviceName)", + e + ) } } - LocalBroadcastManager.getInstance(activity).registerReceiver(subscriptionBroadcastReceiver!!, filters) + // -- Export -- + + val exportSubMenu = menu.addSubMenu(R.string.export_to) + + addMenuItem(exportSubMenu, R.string.file) { onExportSelected() } + } + + private fun addMenuItem( + subMenu: SubMenu, + @StringRes title: Int, + onClick: Runnable + ): MenuItem { + return addMenuItem({ subMenu.add(title) }, onClick) + } + + private fun addMenuItem( + subMenu: SubMenu, + title: String, + onClick: Runnable + ): MenuItem { + return addMenuItem({ subMenu.add(title) }, onClick) + } + + private fun addMenuItem( + menuItemSupplier: Supplier, + onClick: Runnable + ): MenuItem { + val item = menuItemSupplier.get() + + item.setOnMenuItemClickListener { _ -> + onClick.run() + true + } + + return item } private fun onImportFromServiceSelected(serviceId: Int) { @@ -263,13 +294,14 @@ class SubscriptionFragment : BaseStateFragment() { subscriptionsSection.setPlaceholder(EmptyPlaceholderItem()) subscriptionsSection.setHideWhenEmpty(true) - importExportItem = FeedImportExportItem( - { onImportPreviousSelected() }, - { onImportFromServiceSelected(it) }, - { onExportSelected() }, - importExportItemExpandedState ?: false + groupAdapter.add( + Section( + HeaderWithMenuItem( + getString(R.string.tab_subscriptions) + ), + listOf(subscriptionsSection) + ) ) - groupAdapter.add(Section(importExportItem, listOf(subscriptionsSection))) } override fun initViews(rootView: View, savedInstanceState: Bundle?) { @@ -371,13 +403,6 @@ class SubscriptionFragment : BaseStateFragment() { subscriptionsSection.update(result.subscriptions) subscriptionsSection.setHideWhenEmpty(false) - if (result.subscriptions.isEmpty() && importExportItemExpandedState == null) { - binding.itemsList.post { - importExportItem.isExpanded = true - importExportItem.notifyChanged(FeedImportExportItem.REFRESH_EXPANDED_STATUS) - } - } - if (itemsListState != null) { binding.itemsList.layoutManager?.onRestoreInstanceState(itemsListState) itemsListState = null diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/item/FeedImportExportItem.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/item/FeedImportExportItem.kt deleted file mode 100644 index aacfc77ad..000000000 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/item/FeedImportExportItem.kt +++ /dev/null @@ -1,122 +0,0 @@ -package org.schabi.newpipe.local.subscription.item - -import android.graphics.Color -import android.graphics.PorterDuff -import android.view.View -import android.view.ViewGroup -import android.widget.ImageView -import android.widget.TextView -import androidx.annotation.DrawableRes -import com.xwray.groupie.viewbinding.BindableItem -import com.xwray.groupie.viewbinding.GroupieViewHolder -import org.schabi.newpipe.R -import org.schabi.newpipe.databinding.FeedImportExportGroupBinding -import org.schabi.newpipe.extractor.NewPipe -import org.schabi.newpipe.extractor.exceptions.ExtractionException -import org.schabi.newpipe.ktx.animateRotation -import org.schabi.newpipe.util.ServiceHelper -import org.schabi.newpipe.util.ThemeHelper -import org.schabi.newpipe.views.CollapsibleView - -class FeedImportExportItem( - val onImportPreviousSelected: () -> Unit, - val onImportFromServiceSelected: (Int) -> Unit, - val onExportSelected: () -> Unit, - var isExpanded: Boolean = false -) : BindableItem() { - companion object { - const val REFRESH_EXPANDED_STATUS = 123 - } - - override fun bind(viewBinding: FeedImportExportGroupBinding, position: Int, payloads: MutableList) { - if (payloads.contains(REFRESH_EXPANDED_STATUS)) { - viewBinding.importExportOptions.apply { if (isExpanded) expand() else collapse() } - return - } - - super.bind(viewBinding, position, payloads) - } - - override fun getLayout(): Int = R.layout.feed_import_export_group - - override fun bind(viewBinding: FeedImportExportGroupBinding, position: Int) { - if (viewBinding.importFromOptions.childCount == 0) setupImportFromItems(viewBinding.importFromOptions) - if (viewBinding.exportToOptions.childCount == 0) setupExportToItems(viewBinding.exportToOptions) - - expandIconListener?.let { viewBinding.importExportOptions.removeListener(it) } - expandIconListener = CollapsibleView.StateListener { newState -> - viewBinding.importExportExpandIcon.animateRotation( - 250, if (newState == CollapsibleView.COLLAPSED) 0 else 180 - ) - } - - viewBinding.importExportOptions.currentState = if (isExpanded) CollapsibleView.EXPANDED else CollapsibleView.COLLAPSED - viewBinding.importExportExpandIcon.rotation = if (isExpanded) 180F else 0F - viewBinding.importExportOptions.ready() - - viewBinding.importExportOptions.addListener(expandIconListener) - viewBinding.importExport.setOnClickListener { - viewBinding.importExportOptions.switchState() - isExpanded = viewBinding.importExportOptions.currentState == CollapsibleView.EXPANDED - } - } - - override fun unbind(viewHolder: GroupieViewHolder) { - super.unbind(viewHolder) - expandIconListener?.let { viewHolder.binding.importExportOptions.removeListener(it) } - expandIconListener = null - } - - override fun initializeViewBinding(view: View) = FeedImportExportGroupBinding.bind(view) - - private var expandIconListener: CollapsibleView.StateListener? = null - - private fun addItemView(title: String, @DrawableRes icon: Int, container: ViewGroup): View { - val itemRoot = View.inflate(container.context, R.layout.subscription_import_export_item, null) - val titleView = itemRoot.findViewById(android.R.id.text1) - val iconView = itemRoot.findViewById(android.R.id.icon1) - - titleView.text = title - iconView.setImageResource(icon) - - container.addView(itemRoot) - return itemRoot - } - - private fun setupImportFromItems(listHolder: ViewGroup) { - val previousBackupItem = addItemView( - listHolder.context.getString(R.string.previous_export), - R.drawable.ic_backup, listHolder - ) - previousBackupItem.setOnClickListener { onImportPreviousSelected() } - - val iconColor = if (ThemeHelper.isLightThemeSelected(listHolder.context)) Color.BLACK else Color.WHITE - val services = listHolder.context.resources.getStringArray(R.array.service_list) - for (serviceName in services) { - try { - val service = NewPipe.getService(serviceName) - - val subscriptionExtractor = service.subscriptionExtractor ?: continue - - val supportedSources = subscriptionExtractor.supportedSources - if (supportedSources.isEmpty()) continue - - val itemView = addItemView(serviceName, ServiceHelper.getIcon(service.serviceId), listHolder) - val iconView = itemView.findViewById(android.R.id.icon1) - iconView.setColorFilter(iconColor, PorterDuff.Mode.SRC_IN) - - itemView.setOnClickListener { onImportFromServiceSelected(service.serviceId) } - } catch (e: ExtractionException) { - throw RuntimeException("Services array contains an entry that it's not a valid service name ($serviceName)", e) - } - } - } - - private fun setupExportToItems(listHolder: ViewGroup) { - val previousBackupItem = addItemView( - listHolder.context.getString(R.string.file), - R.drawable.ic_save, listHolder - ) - previousBackupItem.setOnClickListener { onExportSelected() } - } -} diff --git a/app/src/main/res/layout/feed_import_export_group.xml b/app/src/main/res/layout/feed_import_export_group.xml deleted file mode 100644 index e8232acc4..000000000 --- a/app/src/main/res/layout/feed_import_export_group.xml +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/subscription_import_export_item.xml b/app/src/main/res/layout/subscription_import_export_item.xml deleted file mode 100644 index 8aadf5d8c..000000000 --- a/app/src/main/res/layout/subscription_import_export_item.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - From 5c69568c7f1c98762afb9aa19621503e21cc0baf Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Sun, 20 Mar 2022 15:13:24 +0100 Subject: [PATCH 02/12] Remove unused ``dimens`` --- app/src/main/res/values/dimens.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 022dc5179..419b3ca43 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -58,11 +58,6 @@ 150dp 9dp - 32dp - 42dp - 24dp - - 9dp 15sp From f9ccc19df56d60444644fb306357db5d6bfdc73c Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Sun, 20 Mar 2022 15:52:19 +0100 Subject: [PATCH 03/12] Fix popup-menu expand icon color --- app/src/main/res/values/styles_misc.xml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values/styles_misc.xml b/app/src/main/res/values/styles_misc.xml index f539eb5a1..ca285ae15 100644 --- a/app/src/main/res/values/styles_misc.xml +++ b/app/src/main/res/values/styles_misc.xml @@ -69,14 +69,17 @@ +