diff --git a/app/schemas/org.schabi.newpipe.database.AppDatabase/3.json b/app/schemas/org.schabi.newpipe.database.AppDatabase/3.json index dcedb5a7fe9..313c3e27cfd 100644 --- a/app/schemas/org.schabi.newpipe.database.AppDatabase/3.json +++ b/app/schemas/org.schabi.newpipe.database.AppDatabase/3.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 3, - "identityHash": "83d5d68663102d5fa28d63caaffb396d", + "identityHash": "9f825b1ee281480bedd38b971feac327", "entities": [ { "tableName": "subscriptions", @@ -555,7 +555,7 @@ }, { "tableName": "feed_group", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `icon_id` INTEGER NOT NULL)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `icon_id` INTEGER NOT NULL, `sort_order` INTEGER NOT NULL)", "fields": [ { "fieldPath": "uid", @@ -574,6 +574,12 @@ "columnName": "icon_id", "affinity": "INTEGER", "notNull": true + }, + { + "fieldPath": "sortOrder", + "columnName": "sort_order", + "affinity": "INTEGER", + "notNull": true } ], "primaryKey": { @@ -582,7 +588,16 @@ ], "autoGenerate": true }, - "indices": [], + "indices": [ + { + "name": "index_feed_group_sort_order", + "unique": false, + "columnNames": [ + "sort_order" + ], + "createSql": "CREATE INDEX `index_feed_group_sort_order` ON `${TABLE_NAME}` (`sort_order`)" + } + ], "foreignKeys": [] }, { @@ -686,7 +701,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '83d5d68663102d5fa28d63caaffb396d')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '9f825b1ee281480bedd38b971feac327')" ] } } \ No newline at end of file diff --git a/app/src/main/java/org/schabi/newpipe/database/Migrations.java b/app/src/main/java/org/schabi/newpipe/database/Migrations.java index b489900a0ac..ccb097a7bef 100644 --- a/app/src/main/java/org/schabi/newpipe/database/Migrations.java +++ b/app/src/main/java/org/schabi/newpipe/database/Migrations.java @@ -91,7 +91,8 @@ public void migrate(@NonNull SupportSQLiteDatabase database) { // Tables for feed feature database.execSQL("CREATE TABLE IF NOT EXISTS feed (stream_id INTEGER NOT NULL, subscription_id INTEGER NOT NULL, PRIMARY KEY(stream_id, subscription_id), FOREIGN KEY(stream_id) REFERENCES streams(uid) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY(subscription_id) REFERENCES subscriptions(uid) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)"); database.execSQL("CREATE INDEX index_feed_subscription_id ON feed (subscription_id)"); - database.execSQL("CREATE TABLE IF NOT EXISTS feed_group (uid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT NOT NULL, icon_id INTEGER NOT NULL)"); + database.execSQL("CREATE TABLE IF NOT EXISTS feed_group (uid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT NOT NULL, icon_id INTEGER NOT NULL, sort_order INTEGER NOT NULL)"); + database.execSQL("CREATE INDEX index_feed_group_sort_order ON feed_group (sort_order)"); database.execSQL("CREATE TABLE IF NOT EXISTS feed_group_subscription_join (group_id INTEGER NOT NULL, subscription_id INTEGER NOT NULL, PRIMARY KEY(group_id, subscription_id), FOREIGN KEY(group_id) REFERENCES feed_group(uid) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY(subscription_id) REFERENCES subscriptions(uid) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)"); database.execSQL("CREATE INDEX index_feed_group_subscription_join_subscription_id ON feed_group_subscription_join (subscription_id)"); database.execSQL("CREATE TABLE IF NOT EXISTS feed_last_updated (subscription_id INTEGER NOT NULL, last_updated INTEGER, PRIMARY KEY(subscription_id), FOREIGN KEY(subscription_id) REFERENCES subscriptions(uid) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)"); diff --git a/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedGroupDAO.kt b/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedGroupDAO.kt index bf2b12df07e..d2616f7d6b2 100644 --- a/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedGroupDAO.kt +++ b/app/src/main/java/org/schabi/newpipe/database/feed/dao/FeedGroupDAO.kt @@ -9,14 +9,18 @@ import org.schabi.newpipe.database.feed.model.FeedGroupSubscriptionEntity @Dao abstract class FeedGroupDAO { - @Query("SELECT * FROM feed_group") + @Query("SELECT * FROM feed_group ORDER BY sort_order ASC") abstract fun getAll(): Flowable> @Query("SELECT * FROM feed_group WHERE uid = :groupId") abstract fun getGroup(groupId: Long): Maybe - @Insert(onConflict = OnConflictStrategy.ABORT) - abstract fun insert(feedEntity: FeedGroupEntity): Long + @Transaction + open fun insert(feedGroupEntity: FeedGroupEntity): Long { + val nextSortOrder = nextSortOrder() + feedGroupEntity.sortOrder = nextSortOrder + return insertInternal(feedGroupEntity) + } @Update(onConflict = OnConflictStrategy.IGNORE) abstract fun update(feedGroupEntity: FeedGroupEntity): Int @@ -41,4 +45,18 @@ abstract class FeedGroupDAO { deleteSubscriptionsFromGroup(groupId) insertSubscriptionsToGroup(subscriptionIds.map { FeedGroupSubscriptionEntity(groupId, it) }) } + + @Transaction + open fun updateOrder(orderMap: Map) { + orderMap.forEach { (groupId, sortOrder) -> updateOrder(groupId, sortOrder) } + } + + @Query("UPDATE feed_group SET sort_order = :sortOrder WHERE uid = :groupId") + abstract fun updateOrder(groupId: Long, sortOrder: Long): Int + + @Query("SELECT IFNULL(MAX(sort_order) + 1, 0) FROM feed_group") + protected abstract fun nextSortOrder(): Long + + @Insert(onConflict = OnConflictStrategy.ABORT) + protected abstract fun insertInternal(feedGroupEntity: FeedGroupEntity): Long } diff --git a/app/src/main/java/org/schabi/newpipe/database/feed/model/FeedGroupEntity.kt b/app/src/main/java/org/schabi/newpipe/database/feed/model/FeedGroupEntity.kt index edb91406a96..a84568dd67b 100644 --- a/app/src/main/java/org/schabi/newpipe/database/feed/model/FeedGroupEntity.kt +++ b/app/src/main/java/org/schabi/newpipe/database/feed/model/FeedGroupEntity.kt @@ -2,11 +2,16 @@ package org.schabi.newpipe.database.feed.model import androidx.room.ColumnInfo import androidx.room.Entity +import androidx.room.Index import androidx.room.PrimaryKey import org.schabi.newpipe.database.feed.model.FeedGroupEntity.Companion.FEED_GROUP_TABLE +import org.schabi.newpipe.database.feed.model.FeedGroupEntity.Companion.SORT_ORDER import org.schabi.newpipe.local.subscription.FeedGroupIcon -@Entity(tableName = FEED_GROUP_TABLE) +@Entity( + tableName = FEED_GROUP_TABLE, + indices = [Index(SORT_ORDER)] +) data class FeedGroupEntity( @PrimaryKey(autoGenerate = true) @ColumnInfo(name = ID) @@ -16,7 +21,10 @@ data class FeedGroupEntity( var name: String, @ColumnInfo(name = ICON) - var icon: FeedGroupIcon + var icon: FeedGroupIcon, + + @ColumnInfo(name = SORT_ORDER) + var sortOrder: Long = -1 ) { companion object { const val FEED_GROUP_TABLE = "feed_group" @@ -24,6 +32,7 @@ data class FeedGroupEntity( const val ID = "uid" const val NAME = "name" const val ICON = "icon_id" + const val SORT_ORDER = "sort_order" const val GROUP_ALL_ID = -1L } diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt index 08e6615801b..5231e16c65b 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedDatabaseManager.kt @@ -147,6 +147,15 @@ class FeedDatabaseManager(context: Context) { .observeOn(AndroidSchedulers.mainThread()) } + fun updateGroupsOrder(groupIdList: List): Completable { + var index = 0L + val orderMap = groupIdList.associateBy({ it }, { index++ }) + + return Completable.fromCallable { feedGroupTable.updateOrder(orderMap) } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + } + fun oldestSubscriptionUpdate(groupId: Long): Flowable> { return when (groupId) { FeedGroupEntity.GROUP_ALL_ID -> feedTable.oldestSubscriptionUpdateFromAll() 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 58175d5cdfa..04c79717051 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 @@ -25,8 +25,9 @@ import org.schabi.newpipe.R import org.schabi.newpipe.database.feed.model.FeedGroupEntity import org.schabi.newpipe.extractor.channel.ChannelInfoItem import org.schabi.newpipe.fragments.BaseStateFragment -import org.schabi.newpipe.local.subscription.SubscriptionViewModel.* +import org.schabi.newpipe.local.subscription.SubscriptionViewModel.SubscriptionState import org.schabi.newpipe.local.subscription.dialog.FeedGroupDialog +import org.schabi.newpipe.local.subscription.dialog.FeedGroupReorderDialog import org.schabi.newpipe.local.subscription.item.* import org.schabi.newpipe.local.subscription.services.SubscriptionsExportService import org.schabi.newpipe.local.subscription.services.SubscriptionsExportService.EXPORT_COMPLETE_ACTION @@ -34,11 +35,8 @@ import org.schabi.newpipe.local.subscription.services.SubscriptionsExportService import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.* import org.schabi.newpipe.report.UserAction +import org.schabi.newpipe.util.* import org.schabi.newpipe.util.AnimationUtils.animateView -import org.schabi.newpipe.util.FilePickerActivityHelper -import org.schabi.newpipe.util.NavigationHelper -import org.schabi.newpipe.util.OnClickGesture -import org.schabi.newpipe.util.ShareUtils import java.io.File import java.text.SimpleDateFormat import java.util.* @@ -54,6 +52,7 @@ class SubscriptionFragment : BaseStateFragment() { private val feedGroupsSection = Section() private var feedGroupsCarousel: FeedGroupCarouselItem? = null private lateinit var importExportItem: FeedImportExportItem + private lateinit var feedGroupsSortMenuItem: HeaderWithMenuItem private val subscriptionsSection = Section() @State @JvmField var itemsListState: Parcelable? = null @@ -164,6 +163,10 @@ class SubscriptionFragment : BaseStateFragment() { startActivityForResult(FilePickerActivityHelper.chooseFileToSave(activity, exportFile.absolutePath), REQUEST_EXPORT_CODE) } + private fun openReorderDialog() { + FeedGroupReorderDialog().show(requireFragmentManager(), null) + } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (data != null && data.data != null && resultCode == Activity.RESULT_OK) { @@ -210,7 +213,12 @@ class SubscriptionFragment : BaseStateFragment() { } feedGroupsCarousel = FeedGroupCarouselItem(requireContext(), carouselAdapter) - add(Section(HeaderItem(getString(R.string.feed_groups_header_title)), listOf(feedGroupsCarousel))) + feedGroupsSortMenuItem = HeaderWithMenuItem( + getString(R.string.feed_groups_header_title), + ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_sort), + menuItemOnClickListener = ::openReorderDialog + ) + add(Section(feedGroupsSortMenuItem, listOf(feedGroupsCarousel))) groupAdapter.add(this) } @@ -333,6 +341,12 @@ class SubscriptionFragment : BaseStateFragment() { feedGroupsCarousel?.onRestoreInstanceState(feedGroupsListState) feedGroupsListState = null } + + if (groups.size < 2) { + items_list.post { feedGroupsSortMenuItem.notifyChanged(HeaderWithMenuItem.PAYLOAD_HIDE_MENU_ITEM) } + } else { + items_list.post { feedGroupsSortMenuItem.notifyChanged(HeaderWithMenuItem.PAYLOAD_SHOW_MENU_ITEM) } + } } /////////////////////////////////////////////////////////////////////////// diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialog.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialog.kt index c199e72ca1c..b5f97bf12c3 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialog.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialog.kt @@ -39,6 +39,7 @@ class FeedGroupDialog : DialogFragment() { private lateinit var viewModel: FeedGroupDialogViewModel private var groupId: Long = NO_GROUP_SELECTED private var groupIcon: FeedGroupIcon? = null + private var groupSortOrder: Long = -1 sealed class ScreenState : Serializable { object InitialScreen : ScreenState() @@ -145,7 +146,7 @@ class FeedGroupDialog : DialogFragment() { when (groupId) { NO_GROUP_SELECTED -> viewModel.createGroup(name, icon, selectedSubscriptions) - else -> viewModel.updateGroup(name, icon, selectedSubscriptions) + else -> viewModel.updateGroup(name, icon, selectedSubscriptions, groupSortOrder) } } else { showInitialScreen() @@ -167,6 +168,7 @@ class FeedGroupDialog : DialogFragment() { val icon = feedGroupEntity?.icon ?: FeedGroupIcon.ALL val name = feedGroupEntity?.name ?: "" groupIcon = feedGroupEntity?.icon + groupSortOrder = feedGroupEntity?.sortOrder ?: -1 icon_preview.setImageResource((if (selectedIcon == null) icon else selectedIcon!!).getDrawableRes(requireContext())) diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt index 575c71f45c3..40ed38bd44f 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupDialogViewModel.kt @@ -58,9 +58,9 @@ class FeedGroupDialogViewModel(applicationContext: Context, val groupId: Long = .subscribe { successLiveData.postValue(FeedDialogEvent.SuccessEvent) }) } - fun updateGroup(name: String, selectedIcon: FeedGroupIcon, selectedSubscriptions: Set) { + fun updateGroup(name: String, selectedIcon: FeedGroupIcon, selectedSubscriptions: Set, sortOrder: Long) { disposables.add(feedDatabaseManager.updateSubscriptionsForGroup(groupId, selectedSubscriptions.toList()) - .andThen(feedDatabaseManager.updateGroup(FeedGroupEntity(groupId, name, selectedIcon))) + .andThen(feedDatabaseManager.updateGroup(FeedGroupEntity(groupId, name, selectedIcon, sortOrder))) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe { successLiveData.postValue(FeedDialogEvent.SuccessEvent) }) diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupReorderDialog.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupReorderDialog.kt new file mode 100644 index 00000000000..6313b3c1004 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupReorderDialog.kt @@ -0,0 +1,103 @@ +package org.schabi.newpipe.local.subscription.dialog + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.ItemTouchHelper.SimpleCallback +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.xwray.groupie.GroupAdapter +import com.xwray.groupie.TouchCallback +import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder +import icepick.Icepick +import icepick.State +import kotlinx.android.synthetic.main.dialog_feed_group_reorder.* +import org.schabi.newpipe.R +import org.schabi.newpipe.database.feed.model.FeedGroupEntity +import org.schabi.newpipe.local.subscription.dialog.FeedGroupReorderDialogViewModel.DialogEvent.SuccessEvent +import org.schabi.newpipe.local.subscription.item.FeedGroupReorderItem +import org.schabi.newpipe.util.ThemeHelper +import java.util.* +import kotlin.collections.ArrayList + +class FeedGroupReorderDialog : DialogFragment() { + private lateinit var viewModel: FeedGroupReorderDialogViewModel + + @State @JvmField var groupOrderedIdList = ArrayList() + private val groupAdapter = GroupAdapter() + private val itemTouchHelper = ItemTouchHelper(getItemTouchCallback()) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + Icepick.restoreInstanceState(this, savedInstanceState) + + setStyle(STYLE_NO_TITLE, ThemeHelper.getMinWidthDialogTheme(requireContext())) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.dialog_feed_group_reorder, container) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + viewModel = ViewModelProviders.of(this).get(FeedGroupReorderDialogViewModel::class.java) + viewModel.groupsLiveData.observe(viewLifecycleOwner, Observer(::handleGroups)) + viewModel.dialogEventLiveData.observe(viewLifecycleOwner, Observer { + when (it) { + is SuccessEvent -> dismiss() + } + }) + + feed_groups_list.layoutManager = LinearLayoutManager(requireContext()) + feed_groups_list.adapter = groupAdapter + itemTouchHelper.attachToRecyclerView(feed_groups_list) + + confirm_button.setOnClickListener { + viewModel.updateOrder(groupOrderedIdList) + } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + Icepick.saveInstanceState(this, outState) + } + + private fun handleGroups(list: List) { + val groupList: List + + if (groupOrderedIdList.isEmpty()) { + groupList = list + groupOrderedIdList.addAll(groupList.map { it.uid }) + } else { + groupList = list.sortedBy { groupOrderedIdList.indexOf(it.uid) } + } + + groupAdapter.update(groupList.map { FeedGroupReorderItem(it, itemTouchHelper) }) + } + + private fun getItemTouchCallback(): SimpleCallback { + return object : TouchCallback() { + + override fun onMove(recyclerView: RecyclerView, source: RecyclerView.ViewHolder, + target: RecyclerView.ViewHolder): Boolean { + val sourceIndex = source.adapterPosition + val targetIndex = target.adapterPosition + + groupAdapter.notifyItemMoved(sourceIndex, targetIndex) + Collections.swap(groupOrderedIdList, sourceIndex, targetIndex) + + return true + } + + override fun isLongPressDragEnabled(): Boolean = false + override fun isItemViewSwipeEnabled(): Boolean = false + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, swipeDir: Int) {} + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupReorderDialogViewModel.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupReorderDialogViewModel.kt new file mode 100644 index 00000000000..21828ee0414 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/dialog/FeedGroupReorderDialogViewModel.kt @@ -0,0 +1,41 @@ +package org.schabi.newpipe.local.subscription.dialog + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.MutableLiveData +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.schedulers.Schedulers +import org.schabi.newpipe.database.feed.model.FeedGroupEntity +import org.schabi.newpipe.local.feed.FeedDatabaseManager + +class FeedGroupReorderDialogViewModel(application: Application) : AndroidViewModel(application) { + private var feedDatabaseManager: FeedDatabaseManager = FeedDatabaseManager(application) + + val groupsLiveData = MutableLiveData>() + val dialogEventLiveData = MutableLiveData() + + private val disposables = CompositeDisposable() + + private var groupsDisposable = feedDatabaseManager.groups() + .limit(1) + .subscribeOn(Schedulers.io()) + .subscribe(groupsLiveData::postValue) + + override fun onCleared() { + super.onCleared() + groupsDisposable.dispose() + disposables.dispose() + } + + fun updateOrder(groupIdList: List) { + disposables.add(feedDatabaseManager.updateGroupsOrder(groupIdList) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { dialogEventLiveData.postValue(DialogEvent.SuccessEvent) }) + } + + sealed class DialogEvent { + object SuccessEvent : DialogEvent() + } +} \ No newline at end of file diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/item/FeedGroupReorderItem.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/item/FeedGroupReorderItem.kt new file mode 100644 index 00000000000..cf010af7f0b --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/item/FeedGroupReorderItem.kt @@ -0,0 +1,48 @@ +package org.schabi.newpipe.local.subscription.item + +import android.view.MotionEvent +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.ItemTouchHelper.DOWN +import androidx.recyclerview.widget.ItemTouchHelper.UP +import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder +import com.xwray.groupie.kotlinandroidextensions.Item +import kotlinx.android.synthetic.main.feed_group_reorder_item.* +import org.schabi.newpipe.R +import org.schabi.newpipe.database.feed.model.FeedGroupEntity +import org.schabi.newpipe.local.subscription.FeedGroupIcon + +data class FeedGroupReorderItem( + val groupId: Long = FeedGroupEntity.GROUP_ALL_ID, + val name: String, + val icon: FeedGroupIcon, + val dragCallback: ItemTouchHelper +) : Item() { + constructor (feedGroupEntity: FeedGroupEntity, dragCallback: ItemTouchHelper) + : this(feedGroupEntity.uid, feedGroupEntity.name, feedGroupEntity.icon, dragCallback) + + override fun getId(): Long { + return when (groupId) { + FeedGroupEntity.GROUP_ALL_ID -> super.getId() + else -> groupId + } + } + + override fun getLayout(): Int = R.layout.feed_group_reorder_item + + override fun bind(viewHolder: GroupieViewHolder, position: Int) { + viewHolder.group_name.text = name + viewHolder.group_icon.setImageResource(icon.getDrawableRes(viewHolder.containerView.context)) + viewHolder.handle.setOnTouchListener { _, event -> + if (event.actionMasked == MotionEvent.ACTION_DOWN) { + dragCallback.startDrag(viewHolder) + return@setOnTouchListener true + } + + false + } + } + + override fun getDragDirs(): Int { + return UP or DOWN + } +} \ No newline at end of file diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/item/HeaderWithMenuItem.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/item/HeaderWithMenuItem.kt new file mode 100644 index 00000000000..5ffdfe7c14b --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/item/HeaderWithMenuItem.kt @@ -0,0 +1,48 @@ +package org.schabi.newpipe.local.subscription.item + +import android.view.View.* +import androidx.annotation.DrawableRes +import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder +import com.xwray.groupie.kotlinandroidextensions.Item +import kotlinx.android.synthetic.main.header_with_menu_item.* +import org.schabi.newpipe.R + +class HeaderWithMenuItem( + val title: String, + @DrawableRes val itemIcon: Int = 0, + private val onClickListener: (() -> Unit)? = null, + private val menuItemOnClickListener: (() -> Unit)? = null +) : Item() { + companion object { + const val PAYLOAD_SHOW_MENU_ITEM = 1 + const val PAYLOAD_HIDE_MENU_ITEM = 2 + } + + override fun getLayout(): Int = R.layout.header_with_menu_item + + + override fun bind(viewHolder: GroupieViewHolder, position: Int, payloads: MutableList) { + if (payloads.contains(PAYLOAD_SHOW_MENU_ITEM)) { + viewHolder.header_menu_item.visibility = VISIBLE + return + } else if (payloads.contains(PAYLOAD_HIDE_MENU_ITEM)) { + viewHolder.header_menu_item.visibility = GONE + return + } + + super.bind(viewHolder, position, payloads) + } + + override fun bind(viewHolder: GroupieViewHolder, position: Int) { + viewHolder.header_title.text = title + viewHolder.header_menu_item.setImageResource(itemIcon) + + val listener: OnClickListener? = + onClickListener?.let { OnClickListener { onClickListener.invoke() } } + viewHolder.root.setOnClickListener(listener) + + val menuItemListener: OnClickListener? = + menuItemOnClickListener?.let { OnClickListener { menuItemOnClickListener.invoke() } } + viewHolder.header_menu_item.setOnClickListener(menuItemListener) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_sort_black_24dp.xml b/app/src/main/res/drawable/ic_sort_black_24dp.xml new file mode 100644 index 00000000000..fd4c56f0e09 --- /dev/null +++ b/app/src/main/res/drawable/ic_sort_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_sort_white_24dp.xml b/app/src/main/res/drawable/ic_sort_white_24dp.xml new file mode 100644 index 00000000000..a0c153ad014 --- /dev/null +++ b/app/src/main/res/drawable/ic_sort_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/dialog_feed_group_reorder.xml b/app/src/main/res/layout/dialog_feed_group_reorder.xml new file mode 100644 index 00000000000..82a9b1591d6 --- /dev/null +++ b/app/src/main/res/layout/dialog_feed_group_reorder.xml @@ -0,0 +1,31 @@ + + + + + + + +