finish history pagination & other features
This commit is contained in:
parent
f6c9b011f2
commit
448c4bc66d
16 changed files with 199 additions and 170 deletions
|
|
@ -8,31 +8,12 @@ import androidx.room.Query
|
|||
import androidx.room.Update
|
||||
import com.deniscerri.ytdl.database.models.DownloadItem
|
||||
import com.deniscerri.ytdl.database.models.HistoryItem
|
||||
import com.deniscerri.ytdl.database.repository.HistoryRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface HistoryDao {
|
||||
|
||||
@Query("SELECT * FROM history WHERE (title LIKE '%'||:query||'%' OR author LIKE '%'||:query||'%') AND type LIKE '%'||:type||'%' AND website LIKE '%'||:site||'%' ORDER BY " +
|
||||
"CASE WHEN :sort = 'ASC' THEN id END ASC," +
|
||||
"CASE WHEN :sort = 'DESC' THEN id END DESC," +
|
||||
"CASE WHEN :sort = '' THEN id END DESC ")
|
||||
fun getHistorySortedByID(query : String, type : String, site : String, sort : String) : List<HistoryItem>
|
||||
|
||||
@Query("SELECT * FROM history WHERE (title LIKE '%'||:query||'%' OR author LIKE '%'||:query||'%') AND type LIKE '%'||:type||'%' AND website LIKE '%'||:site||'%' ORDER BY " +
|
||||
"CASE WHEN :sort = 'ASC' THEN title END ASC," +
|
||||
"CASE WHEN :sort = 'DESC' THEN title END DESC," +
|
||||
"CASE WHEN :sort = '' THEN title END DESC ")
|
||||
fun getHistorySortedByTitle(query : String, type : String, site : String, sort : String) : List<HistoryItem>
|
||||
|
||||
@Query("SELECT * FROM history WHERE (title LIKE '%'||:query||'%' OR author LIKE '%'||:query||'%') AND type LIKE '%'||:type||'%' AND website LIKE '%'||:site||'%' ORDER BY " +
|
||||
"CASE WHEN :sort = 'ASC' THEN author END ASC," +
|
||||
"CASE WHEN :sort = 'DESC' THEN author END DESC," +
|
||||
"CASE WHEN :sort = '' THEN author END DESC ")
|
||||
fun getHistorySortedByAuthor(query : String, type : String, site : String, sort : String) : List<HistoryItem>
|
||||
|
||||
|
||||
|
||||
@Query("SELECT * FROM history WHERE (title LIKE '%'||:query||'%' OR author LIKE '%'||:query||'%') AND type LIKE '%'||:type||'%' AND website LIKE '%'||:site||'%' ORDER BY " +
|
||||
"CASE WHEN :sort = 'ASC' THEN id END ASC," +
|
||||
"CASE WHEN :sort = 'DESC' THEN id END DESC," +
|
||||
|
|
@ -57,6 +38,29 @@ interface HistoryDao {
|
|||
"CASE WHEN :sort = '' THEN filesize END DESC ")
|
||||
fun getHistorySortedByFilesizePaginated(query : String, type : String, site : String, sort : String) : PagingSource<Int, HistoryItem>
|
||||
|
||||
@Query("SELECT id, downloadPath FROM history WHERE (title LIKE '%'||:query||'%' OR author LIKE '%'||:query||'%') AND type LIKE '%'||:type||'%' AND website LIKE '%'||:site||'%' ORDER BY " +
|
||||
"CASE WHEN :sort = 'ASC' THEN id END ASC," +
|
||||
"CASE WHEN :sort = 'DESC' THEN id END DESC," +
|
||||
"CASE WHEN :sort = '' THEN id END DESC ")
|
||||
fun getHistoryIDsSortedByID(query : String, type : String, site : String, sort : String) : List<HistoryRepository.HistoryIDsAndPaths>
|
||||
|
||||
@Query("SELECT id, downloadPath FROM history WHERE (title LIKE '%'||:query||'%' OR author LIKE '%'||:query||'%') AND type LIKE '%'||:type||'%' AND website LIKE '%'||:site||'%' ORDER BY " +
|
||||
"CASE WHEN :sort = 'ASC' THEN title END ASC," +
|
||||
"CASE WHEN :sort = 'DESC' THEN title END DESC," +
|
||||
"CASE WHEN :sort = '' THEN title END DESC ")
|
||||
fun getHistoryIDsSortedByTitle(query : String, type : String, site : String, sort : String) : List<HistoryRepository.HistoryIDsAndPaths>
|
||||
|
||||
@Query("SELECT id, downloadPath FROM history WHERE (title LIKE '%'||:query||'%' OR author LIKE '%'||:query||'%') AND type LIKE '%'||:type||'%' AND website LIKE '%'||:site||'%' ORDER BY " +
|
||||
"CASE WHEN :sort = 'ASC' THEN author END ASC," +
|
||||
"CASE WHEN :sort = 'DESC' THEN author END DESC," +
|
||||
"CASE WHEN :sort = '' THEN author END DESC ")
|
||||
fun getHistoryIDsSortedByAuthor(query : String, type : String, site : String, sort : String) : List<HistoryRepository.HistoryIDsAndPaths>
|
||||
|
||||
@Query("SELECT id, downloadPath FROM history WHERE (title LIKE '%'||:query||'%' OR author LIKE '%'||:query||'%') AND type LIKE '%'||:type||'%' AND website LIKE '%'||:site||'%' ORDER BY " +
|
||||
"CASE WHEN :sort = 'ASC' THEN filesize END ASC," +
|
||||
"CASE WHEN :sort = 'DESC' THEN filesize END DESC," +
|
||||
"CASE WHEN :sort = '' THEN filesize END DESC ")
|
||||
fun getHistoryIDsSortedByFilesize(query : String, type : String, site : String, sort : String) : List<HistoryRepository.HistoryIDsAndPaths>
|
||||
|
||||
@Query("SELECT * FROM history")
|
||||
fun getAllHistory() : Flow<List<HistoryItem>>
|
||||
|
|
@ -79,6 +83,12 @@ interface HistoryDao {
|
|||
@Query("SELECT * FROM history WHERE url=:url")
|
||||
fun getAllHistoryByURL(url: String) : List<HistoryItem>
|
||||
|
||||
@Query("SELECT * FROM history WHERE id in (:ids)")
|
||||
fun getAllHistoryByIDs(ids: List<Long>) : List<HistoryItem>
|
||||
|
||||
@Query("SELECT downloadPath FROM history WHERE id in (:ids)")
|
||||
fun getDownloadPathsFromIDs(ids: List<Long>) : List<HistoryRepository.HistoryItemDownloadPaths>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insert(item: HistoryItem)
|
||||
|
||||
|
|
@ -88,6 +98,9 @@ interface HistoryDao {
|
|||
@Query("DELETE FROM history")
|
||||
suspend fun deleteAll()
|
||||
|
||||
@Query("DELETE FROM history where id in (:ids)")
|
||||
suspend fun deleteAllByIDs(ids: List<Long>)
|
||||
|
||||
@Query("DELETE FROM history WHERE id > (SELECT MIN(h.id) FROM history h WHERE h.url = history.url AND h.type = history.type)")
|
||||
suspend fun deleteDuplicates()
|
||||
|
||||
|
|
|
|||
|
|
@ -35,19 +35,17 @@ class HistoryRepository(private val historyDao: HistoryDao) {
|
|||
return historyDao.getAllHistoryByURL(url)
|
||||
}
|
||||
|
||||
data class HistoryIDsAndPaths(
|
||||
val id: Long,
|
||||
val downloadPath: List<String>
|
||||
)
|
||||
|
||||
fun getFiltered(query : String, type : String, site : String, sortType: HistorySortType, sort: SORTING, statusFilter: HistoryViewModel.HistoryStatus) : List<HistoryItem> {
|
||||
fun getFilteredIDs (query : String, type : String, site : String, sortType: HistorySortType, sort: SORTING, statusFilter: HistoryViewModel.HistoryStatus) : List<Long> {
|
||||
var filtered = when(sortType){
|
||||
HistorySortType.DATE -> historyDao.getHistorySortedByID(query, type, site, sort.toString())
|
||||
HistorySortType.TITLE -> historyDao.getHistorySortedByTitle(query, type, site, sort.toString())
|
||||
HistorySortType.AUTHOR -> historyDao.getHistorySortedByAuthor(query, type, site, sort.toString())
|
||||
HistorySortType.FILESIZE -> {
|
||||
val items = historyDao.getHistorySortedByID(query, type, site, sort.toString())
|
||||
when(sort){
|
||||
SORTING.DESC -> items.sortedByDescending { it.format.filesize }
|
||||
SORTING.ASC -> items.sortedBy { it.format.filesize }
|
||||
}
|
||||
}
|
||||
HistorySortType.DATE -> historyDao.getHistoryIDsSortedByID(query, type, site, sort.toString())
|
||||
HistorySortType.TITLE -> historyDao.getHistoryIDsSortedByTitle(query, type, site, sort.toString())
|
||||
HistorySortType.AUTHOR -> historyDao.getHistoryIDsSortedByAuthor(query, type, site, sort.toString())
|
||||
HistorySortType.FILESIZE -> historyDao.getHistoryIDsSortedByFilesize(query, type, site, sort.toString())
|
||||
}
|
||||
|
||||
when(statusFilter) {
|
||||
|
|
@ -59,8 +57,7 @@ class HistoryRepository(private val historyDao: HistoryDao) {
|
|||
}
|
||||
else -> {}
|
||||
}
|
||||
|
||||
return filtered
|
||||
return filtered.map { it.id }
|
||||
}
|
||||
|
||||
fun getPaginatedSource(query : String, type : String, site : String, sortType: HistorySortType, sort: SORTING) : PagingSource<Int, HistoryItem> {
|
||||
|
|
@ -101,6 +98,26 @@ class HistoryRepository(private val historyDao: HistoryDao) {
|
|||
historyDao.deleteAll()
|
||||
}
|
||||
|
||||
suspend fun deleteAllWithIDs(ids: List<Long>, deleteFile: Boolean = false){
|
||||
if (deleteFile){
|
||||
historyDao.getAllHistoryByIDs(ids).forEach { item ->
|
||||
item.downloadPath.forEach {
|
||||
FileUtil.deleteFile(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
historyDao.deleteAllByIDs(ids)
|
||||
}
|
||||
|
||||
data class HistoryItemDownloadPaths(
|
||||
val downloadPath: List<String>
|
||||
)
|
||||
|
||||
fun getDownloadPathsFromIDs(ids: List<Long>) : List<List<String>> {
|
||||
val res = historyDao.getDownloadPathsFromIDs(ids)
|
||||
return res.map { it.downloadPath }
|
||||
}
|
||||
|
||||
suspend fun deleteDuplicates(){
|
||||
historyDao.deleteDuplicates()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,7 +118,6 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
|
|||
if (resetResults) deleteAll()
|
||||
|
||||
//throw YoutubeDLException("Youtube Watch Videos is not yet supported in data fetching. You can download it directly by clicking Continue Anyway or by Quick Downloading it!")
|
||||
//TODO use below code after youtubedl-android has fixed issue #295 in their repo
|
||||
val items = mutableListOf<ResultItem>()
|
||||
val ytExtractorResult = newPipeUtil?.getPlaylistData(inputQuery) {
|
||||
if (addToResults){
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import kotlinx.coroutines.flow.flowOf
|
|||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class HistoryViewModel(application: Application) : AndroidViewModel(application) {
|
||||
|
|
@ -52,7 +53,7 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
|
|||
|
||||
var paginatedItems : Flow<PagingData<HistoryItem>>
|
||||
var websites : Flow<List<String>>
|
||||
var totalCount : Flow<Int>
|
||||
var totalCount = MutableStateFlow(0)
|
||||
|
||||
data class HistoryFilters(
|
||||
var type: String = "",
|
||||
|
|
@ -67,7 +68,6 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
|
|||
val dao = DBManager.getInstance(application).historyDao
|
||||
repository = HistoryRepository(dao)
|
||||
websites = repository.websites
|
||||
totalCount = repository.count
|
||||
|
||||
val filters = listOf(dao.getAllHistory(), sortOrder, sortType, websiteFilter, statusFilter, queryFilter, typeFilter)
|
||||
paginatedItems = combine(filters) { f ->
|
||||
|
|
@ -107,6 +107,10 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
|
|||
else -> {}
|
||||
}
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
totalCount.value = repository.getFilteredIDs(query, type, website, sortType, sortOrder, status).count()
|
||||
}
|
||||
|
||||
pager
|
||||
}.flatMapLatest { it }
|
||||
}
|
||||
|
|
@ -138,9 +142,16 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
|
|||
statusFilter.value = status
|
||||
}
|
||||
|
||||
private fun filter(query : String, format : String, site : String, sortType: HistorySortType, sort: SORTING, statusFilter: HistoryStatus) = viewModelScope.launch(Dispatchers.IO){
|
||||
|
||||
fun getIDsBetweenTwoItems(firstID: Long, secondID: Long): List<Long> {
|
||||
val ids = repository.getFilteredIDs(queryFilter.value, typeFilter.value, websiteFilter.value, sortType.value, sortOrder.value, statusFilter.value)
|
||||
val firstIndex = ids.indexOf(firstID)
|
||||
val secondIndex = ids.indexOf(secondID)
|
||||
return ids.filterIndexed {index, _ -> index in (firstIndex + 1) until secondIndex }
|
||||
}
|
||||
|
||||
fun getItemIDsNotPresentIn(not: List<Long>) : List<Long> {
|
||||
val ids = repository.getFilteredIDs(queryFilter.value, typeFilter.value, websiteFilter.value, sortType.value, sortOrder.value, statusFilter.value)
|
||||
return ids.filter { !not.contains(it) }
|
||||
}
|
||||
|
||||
fun getAll() : List<HistoryItem> {
|
||||
|
|
@ -159,6 +170,14 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
|
|||
repository.delete(item, deleteFile)
|
||||
}
|
||||
|
||||
fun deleteAllWithIDs(ids: List<Long>, deleteFile: Boolean = false) = viewModelScope.launch(Dispatchers.IO) {
|
||||
repository.deleteAllWithIDs(ids, deleteFile)
|
||||
}
|
||||
|
||||
fun getDownloadPathsFromIDs(ids: List<Long>) : List<List<String>> {
|
||||
return repository.getDownloadPathsFromIDs(ids)
|
||||
}
|
||||
|
||||
fun deleteAll(deleteFile: Boolean = false) = viewModelScope.launch(Dispatchers.IO) {
|
||||
repository.deleteAll(deleteFile)
|
||||
}
|
||||
|
|
@ -175,16 +194,4 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
|
|||
repository.clearDeletedHistory()
|
||||
}
|
||||
|
||||
fun getRecordsBetweenTwoItems(item1: Long, item2: Long) : List<HistoryItem> {
|
||||
val filtered = repository.getFiltered(queryFilter.value!!, typeFilter.value!!, websiteFilter.value!!, sortType.value!!, sortOrder.value!!, statusFilter.value!!)
|
||||
val firstIndex = filtered.indexOfFirst { it.id == item1 }
|
||||
val secondIndex = filtered.indexOfFirst { it.id == item2 }
|
||||
|
||||
return if(firstIndex > secondIndex) {
|
||||
filtered.filterIndexed { index, _ -> index in (secondIndex + 1) until firstIndex }
|
||||
}else{
|
||||
filtered.filterIndexed { index, _ -> index in (firstIndex + 1) until secondIndex }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -84,6 +84,7 @@ class HistoryPaginatedAdapter(onItemClickListener: OnItemClickListener, activity
|
|||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val item = getItem(position) ?: return
|
||||
val card = holder.cardView
|
||||
holder.itemView.tag = item.id.toString()
|
||||
card.tag = item.id.toString()
|
||||
card.popup()
|
||||
|
||||
|
|
@ -147,7 +148,7 @@ class HistoryPaginatedAdapter(onItemClickListener: OnItemClickListener, activity
|
|||
if (btn.hasOnClickListeners()) btn.setOnClickListener(null)
|
||||
btn.isClickable = filesPresent
|
||||
|
||||
if (checkedItems.contains(item.id)) {
|
||||
if ((checkedItems.contains(item.id) && !inverted) || (!checkedItems.contains(item.id) && inverted)) {
|
||||
card.isChecked = true
|
||||
card.strokeWidth = 5
|
||||
} else {
|
||||
|
|
@ -156,12 +157,12 @@ class HistoryPaginatedAdapter(onItemClickListener: OnItemClickListener, activity
|
|||
}
|
||||
val finalFilePresent = filesPresent
|
||||
card.setOnLongClickListener {
|
||||
checkCard(card, item.id)
|
||||
checkCard(card, item.id, position)
|
||||
true
|
||||
}
|
||||
card.setOnClickListener {
|
||||
if (checkedItems.size > 0) {
|
||||
checkCard(card, item.id)
|
||||
checkCard(card, item.id, position)
|
||||
} else {
|
||||
onItemClickListener.onCardClick(item.id, finalFilePresent)
|
||||
}
|
||||
|
|
@ -208,7 +209,7 @@ class HistoryPaginatedAdapter(onItemClickListener: OnItemClickListener, activity
|
|||
}
|
||||
}
|
||||
|
||||
private fun checkCard(card: MaterialCardView, itemID: Long) {
|
||||
private fun checkCard(card: MaterialCardView, itemID: Long, position: Int) {
|
||||
if (card.isChecked) {
|
||||
card.strokeWidth = 0
|
||||
if (inverted) checkedItems.add(itemID)
|
||||
|
|
@ -219,13 +220,13 @@ class HistoryPaginatedAdapter(onItemClickListener: OnItemClickListener, activity
|
|||
else checkedItems.add(itemID)
|
||||
}
|
||||
card.isChecked = !card.isChecked
|
||||
onItemClickListener.onCardSelect(itemID, card.isChecked)
|
||||
onItemClickListener.onCardSelect(card.isChecked, position)
|
||||
}
|
||||
|
||||
interface OnItemClickListener {
|
||||
fun onCardClick(itemID: Long, isPresent: Boolean)
|
||||
fun onButtonClick(itemID: Long, isPresent: Boolean)
|
||||
fun onCardSelect(itemID: Long, isChecked: Boolean)
|
||||
fun onButtonClick(itemID: Long, filePresent: Boolean)
|
||||
fun onCardClick(itemID: Long, filePresent: Boolean)
|
||||
fun onCardSelect(isChecked: Boolean, position: Int)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -309,7 +309,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
|
||||
|
||||
scheduleBtn.setOnClickListener{
|
||||
UiUtil.showDatePicker(fragmentManager) {
|
||||
UiUtil.showDatePicker(fragmentManager, sharedPreferences) {
|
||||
lifecycleScope.launch {
|
||||
resultViewModel.cancelUpdateItemData()
|
||||
resultViewModel.cancelUpdateFormatsItemData()
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
|
||||
|
||||
scheduleBtn.setOnClickListener{
|
||||
UiUtil.showDatePicker(parentFragmentManager) { cal ->
|
||||
UiUtil.showDatePicker(parentFragmentManager, preferences) { cal ->
|
||||
toggleLoading(true)
|
||||
lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO){
|
||||
|
|
|
|||
|
|
@ -315,7 +315,7 @@ class ObserveSourcesBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
isFocusable = false
|
||||
isClickable = false
|
||||
setOnClickListener{
|
||||
UiUtil.showTimePicker(fragmentManager){
|
||||
UiUtil.showTimePicker(fragmentManager, sharedPreferences){
|
||||
everyTime.editText?.setText(
|
||||
SimpleDateFormat(DateFormat.getBestDateTimePattern(Locale.getDefault(), "HHmm"), Locale.getDefault()).format(it.timeInMillis)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
|
|||
}
|
||||
|
||||
override fun onItemClick(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
|
||||
TODO("Not yet implemented")
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
|||
import com.deniscerri.ytdl.database.viewmodel.HistoryViewModel
|
||||
import com.deniscerri.ytdl.ui.adapter.HistoryPaginatedAdapter
|
||||
import com.deniscerri.ytdl.util.Extensions.enableFastScroll
|
||||
import com.deniscerri.ytdl.util.Extensions.toListString
|
||||
import com.deniscerri.ytdl.util.FileUtil
|
||||
import com.deniscerri.ytdl.util.NavbarUtil
|
||||
import com.deniscerri.ytdl.util.UiUtil
|
||||
|
|
@ -523,14 +524,10 @@ class HistoryFragment : Fragment(), HistoryPaginatedAdapter.OnItemClickListener{
|
|||
}
|
||||
}
|
||||
|
||||
override fun onCardSelect(itemID: Long, isChecked: Boolean) {
|
||||
override fun onCardSelect(isChecked: Boolean, position: Int) {
|
||||
lifecycleScope.launch {
|
||||
val item = withContext(Dispatchers.IO) {
|
||||
historyViewModel.getByID(itemID)
|
||||
}
|
||||
|
||||
if (actionMode == null) actionMode = (activity as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
||||
val selectedObjects = historyAdapter.getSelectedObjectsCount(totalCount)
|
||||
if (actionMode == null) actionMode = (activity as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
||||
actionMode?.apply {
|
||||
if (selectedObjects == 0){
|
||||
this.finish()
|
||||
|
|
@ -538,15 +535,28 @@ class HistoryFragment : Fragment(), HistoryPaginatedAdapter.OnItemClickListener{
|
|||
actionMode?.title = "$selectedObjects ${getString(R.string.selected)}"
|
||||
this.menu.findItem(R.id.select_between).isVisible = false
|
||||
if(selectedObjects == 2){
|
||||
//TODO
|
||||
// val selectedIDs = contextualActionBar.getSelectedIDs().sortedBy { it }
|
||||
// val resultsInMiddle = withContext(Dispatchers.IO){
|
||||
// historyViewModel.getRecordsBetweenTwoItems(selectedIDs.first().id, selectedIDs.last().id)
|
||||
// }.toMutableList()
|
||||
// this.menu.findItem(R.id.select_between).isVisible = resultsInMiddle.isNotEmpty()
|
||||
val selectedIDs = contextualActionBar.getSelectedIDs().sortedBy { it }
|
||||
val idsInMiddle = withContext(Dispatchers.IO){
|
||||
historyViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last())
|
||||
}
|
||||
this.menu.findItem(R.id.select_between).isVisible = idsInMiddle.isNotEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isChecked) {
|
||||
if (actionMode == null){
|
||||
actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
||||
}else{
|
||||
actionMode!!.title = "$selectedObjects ${getString(R.string.selected)}"
|
||||
}
|
||||
}
|
||||
else {
|
||||
actionMode?.title = "$selectedObjects ${getString(R.string.selected)}"
|
||||
if (selectedObjects == 0){
|
||||
actionMode?.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -573,66 +583,63 @@ class HistoryFragment : Fragment(), HistoryPaginatedAdapter.OnItemClickListener{
|
|||
): Boolean {
|
||||
return when (item!!.itemId) {
|
||||
R.id.select_between -> {
|
||||
//TODO
|
||||
// lifecycleScope.launch {
|
||||
// val selectedIDs = selectedObjects.sortedBy { it.id }
|
||||
// val resultsInMiddle = withContext(Dispatchers.IO){
|
||||
// historyViewModel.getRecordsBetweenTwoItems(selectedIDs.first().id, selectedIDs.last().id)
|
||||
// }.toMutableList()
|
||||
// if (resultsInMiddle.isNotEmpty()){
|
||||
// selectedObjects.addAll(resultsInMiddle)
|
||||
// historyAdapter.checkMultipleItems(selectedObjects.map { it.id })
|
||||
// actionMode?.title = "${selectedObjects.count()} ${getString(R.string.selected)}"
|
||||
// }
|
||||
// mode?.menu?.findItem(R.id.select_between)?.isVisible = false
|
||||
// }
|
||||
lifecycleScope.launch {
|
||||
val selectedIDs = getSelectedIDs().sortedBy { it }
|
||||
val idsInMiddle = withContext(Dispatchers.IO){
|
||||
historyViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last())
|
||||
}.toMutableList()
|
||||
idsInMiddle.addAll(selectedIDs)
|
||||
if (idsInMiddle.isNotEmpty()){
|
||||
historyAdapter.checkMultipleItems(idsInMiddle)
|
||||
actionMode?.title = "${idsInMiddle.count()} ${getString(R.string.selected)}"
|
||||
}
|
||||
mode?.menu?.findItem(R.id.select_between)?.isVisible = false
|
||||
}
|
||||
true
|
||||
}
|
||||
R.id.delete_results -> {
|
||||
//TODO
|
||||
// val deleteFile = booleanArrayOf(false)
|
||||
// val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
|
||||
// deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
||||
// deleteDialog.setMultiChoiceItems(
|
||||
// arrayOf(getString(R.string.delete_files_too)),
|
||||
// booleanArrayOf(false)
|
||||
// ) { _: DialogInterface?, _: Int, b: Boolean -> deleteFile[0] = b }
|
||||
// deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||
// deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||
// for (obj in selectedObjects){
|
||||
// historyViewModel.delete(obj, deleteFile[0])
|
||||
// }
|
||||
// clearCheckedItems()
|
||||
// actionMode?.finish()
|
||||
// }
|
||||
// deleteDialog.show()
|
||||
val deleteFile = booleanArrayOf(false)
|
||||
val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
|
||||
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
||||
deleteDialog.setMultiChoiceItems(
|
||||
arrayOf(getString(R.string.delete_files_too)),
|
||||
booleanArrayOf(false)
|
||||
) { _: DialogInterface?, _: Int, b: Boolean -> deleteFile[0] = b }
|
||||
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||
lifecycleScope.launch {
|
||||
val selectedObjects = getSelectedIDs()
|
||||
historyAdapter.clearCheckedItems()
|
||||
historyViewModel.deleteAllWithIDs(selectedObjects, deleteFile[0])
|
||||
actionMode?.finish()
|
||||
}
|
||||
}
|
||||
deleteDialog.show()
|
||||
true
|
||||
}
|
||||
R.id.share -> {
|
||||
//TODO
|
||||
// FileUtil.shareFileIntent(requireContext(), selectedObjects.map { it.downloadPath }.flatten())
|
||||
// clearCheckedItems()
|
||||
// actionMode?.finish()
|
||||
lifecycleScope.launch {
|
||||
val selectedObjects = getSelectedIDs()
|
||||
val paths = withContext(Dispatchers.IO){
|
||||
historyViewModel.getDownloadPathsFromIDs(selectedObjects)
|
||||
}
|
||||
FileUtil.shareFileIntent(requireContext(), paths.flatten())
|
||||
historyAdapter.clearCheckedItems()
|
||||
actionMode?.finish()
|
||||
}
|
||||
true
|
||||
}
|
||||
R.id.select_all -> {
|
||||
//TODO
|
||||
// historyAdapter.checkAll()
|
||||
// historyList?.forEach { selectedObjects.add(it!!) }
|
||||
// mode?.title = "(${selectedObjects.size}) ${resources.getString(R.string.all_items_selected)}"
|
||||
historyAdapter.checkAll()
|
||||
val selectedCount = historyAdapter.getSelectedObjectsCount(totalCount)
|
||||
mode?.title = "(${selectedCount}) ${resources.getString(R.string.all_items_selected)}"
|
||||
true
|
||||
}
|
||||
R.id.invert_selected -> {
|
||||
//TODO
|
||||
// historyAdapter.invertSelected()
|
||||
// val invertedList = arrayListOf<HistoryItem>()
|
||||
// historyList?.forEach {
|
||||
// if (!selectedObjects.contains(it)!!) invertedList.add(it!!)
|
||||
// }
|
||||
// selectedObjects.clear()
|
||||
// selectedObjects.addAll(invertedList)
|
||||
// actionMode!!.title = "${selectedObjects.size} ${getString(R.string.selected)}"
|
||||
// if (invertedList.isEmpty()) actionMode?.finish()
|
||||
historyAdapter.invertSelected()
|
||||
val selectedCount = historyAdapter.getSelectedObjectsCount(totalCount)
|
||||
actionMode?.title = "$selectedCount ${getString(R.string.selected)}"
|
||||
if (selectedCount == 0) actionMode?.finish()
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
|
|
@ -647,15 +654,13 @@ class HistoryFragment : Fragment(), HistoryPaginatedAdapter.OnItemClickListener{
|
|||
}
|
||||
|
||||
suspend fun getSelectedIDs() : List<Long>{
|
||||
//TODO
|
||||
// return if (historyAdapter.inverted || historyAdapter.checkedItems.isEmpty()) {
|
||||
// withContext(Dispatchers.IO){
|
||||
// historyViewModel.getItemIDsNotPresentIn(historyAdapter.checkedItems.toList())
|
||||
// }
|
||||
// }else{
|
||||
// historyAdapter.checkedItems.toList()
|
||||
// }
|
||||
return listOf()
|
||||
return if (historyAdapter.inverted || historyAdapter.checkedItems.isEmpty()){
|
||||
withContext(Dispatchers.IO){
|
||||
historyViewModel.getItemIDsNotPresentIn(historyAdapter.checkedItems.toList())
|
||||
}
|
||||
}else{
|
||||
historyAdapter.checkedItems.toList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.animation.AnimatorSet
|
|||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.DialogInterface
|
||||
import android.content.SharedPreferences
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
|
|
@ -66,6 +67,7 @@ class QueuedDownloadsFragment : Fragment(), QueuedDownloadAdapter.OnItemClickLis
|
|||
private lateinit var notificationUtil: NotificationUtil
|
||||
private lateinit var fileSize: TextView
|
||||
private lateinit var dragHandleToggle: TextView
|
||||
private lateinit var sharedPreferences: SharedPreferences
|
||||
private var totalSize: Int = 0
|
||||
private var actionMode : ActionMode? = null
|
||||
|
||||
|
|
@ -84,6 +86,7 @@ class QueuedDownloadsFragment : Fragment(), QueuedDownloadAdapter.OnItemClickLis
|
|||
@SuppressLint("SetTextI18n", "RestrictedApi")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
fileSize = view.findViewById(R.id.filesize)
|
||||
dragHandleToggle = view.findViewById(R.id.drag)
|
||||
val itemTouchHelper = ItemTouchHelper(queuedDragDropHelper)
|
||||
|
|
@ -473,7 +476,7 @@ class QueuedDownloadsFragment : Fragment(), QueuedDownloadAdapter.OnItemClickLis
|
|||
}
|
||||
},
|
||||
scheduleButtonClick = {downloadItem ->
|
||||
UiUtil.showDatePicker(parentFragmentManager) {
|
||||
UiUtil.showDatePicker(parentFragmentManager, sharedPreferences) {
|
||||
Toast.makeText(context, getString(R.string.download_rescheduled_to) + " " + it.time, Toast.LENGTH_LONG).show()
|
||||
downloadViewModel.deleteDownload(downloadItem.id)
|
||||
downloadItem.downloadStartTime = it.timeInMillis
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ class ScheduledDownloadsFragment : Fragment(), ScheduledDownloadAdapter.OnItemCl
|
|||
)
|
||||
},
|
||||
scheduleButtonClick = {downloadItem ->
|
||||
UiUtil.showDatePicker(parentFragmentManager) {
|
||||
UiUtil.showDatePicker(parentFragmentManager, preferences) {
|
||||
Toast.makeText(context, getString(R.string.download_rescheduled_to) + " " + it.time, Toast.LENGTH_LONG).show()
|
||||
downloadViewModel.deleteDownload(downloadItem.id)
|
||||
downloadItem.downloadStartTime = it.timeInMillis
|
||||
|
|
|
|||
|
|
@ -35,17 +35,6 @@ class DownloadSettingsFragment : BaseSettingsFragment() {
|
|||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.downloading_preferences, rootKey)
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
|
||||
findPreference<Preference>("prevent_duplicate_downloads")?.apply {
|
||||
//TODO transitioning code, delete after couple releases
|
||||
if (preferences.getBoolean("download_archive", false)){
|
||||
preferences.edit(commit = true){
|
||||
putBoolean("download_archive", false).apply()
|
||||
putString("prevent_duplicate_downloads", "download_archive")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val rememberDownloadType = findPreference<SwitchPreferenceCompat>("remember_download_type")
|
||||
val downloadType = findPreference<ListPreference>("preferred_download_type")
|
||||
downloadType?.isEnabled = rememberDownloadType?.isChecked == false
|
||||
|
|
@ -163,7 +152,7 @@ class DownloadSettingsFragment : BaseSettingsFragment() {
|
|||
}
|
||||
|
||||
scheduleStart?.setOnPreferenceClickListener {
|
||||
UiUtil.showTimePicker(parentFragmentManager){
|
||||
UiUtil.showTimePicker(parentFragmentManager, preferences){
|
||||
val hr = it.get(Calendar.HOUR_OF_DAY)
|
||||
val mn = it.get(Calendar.MINUTE)
|
||||
val formattedTime = String.format("%02d", hr) + ":" + String.format("%02d", mn)
|
||||
|
|
@ -176,7 +165,7 @@ class DownloadSettingsFragment : BaseSettingsFragment() {
|
|||
}
|
||||
|
||||
scheduleEnd?.setOnPreferenceClickListener {
|
||||
UiUtil.showTimePicker(parentFragmentManager){
|
||||
UiUtil.showTimePicker(parentFragmentManager, preferences){
|
||||
val hr = it.get(Calendar.HOUR_OF_DAY)
|
||||
val mn = it.get(Calendar.MINUTE)
|
||||
val formattedTime = String.format("%02d", hr) + ":" + String.format("%02d", mn)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import android.content.ClipboardManager
|
|||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.text.Editable
|
||||
|
|
@ -413,7 +414,7 @@ object UiUtil {
|
|||
datePicker.show(fragmentManager, "datepicker")
|
||||
}
|
||||
|
||||
fun showDatePicker(fragmentManager: FragmentManager , onSubmit : (chosenDate: Calendar) -> Unit ){
|
||||
fun showDatePicker(fragmentManager: FragmentManager , preferences: SharedPreferences, onSubmit : (chosenDate: Calendar) -> Unit ){
|
||||
val currentDate = Calendar.getInstance()
|
||||
currentDate.timeInMillis = (currentDate.timeInMillis - (currentDate.timeInMillis % 1800000)) + 1800000
|
||||
val date = Calendar.getInstance()
|
||||
|
|
@ -430,38 +431,35 @@ object UiUtil {
|
|||
datePicker.addOnPositiveButtonClickListener{
|
||||
date.timeInMillis = it
|
||||
|
||||
|
||||
val timepicker = MaterialTimePicker.Builder()
|
||||
.setTimeFormat(TimeFormat.CLOCK_24H)
|
||||
.setHour(currentDate.get(Calendar.HOUR_OF_DAY))
|
||||
.setMinute(currentDate.get(Calendar.MINUTE))
|
||||
.build()
|
||||
|
||||
timepicker.addOnPositiveButtonClickListener{
|
||||
date[Calendar.HOUR_OF_DAY] = timepicker.hour
|
||||
date[Calendar.MINUTE] = timepicker.minute
|
||||
showTimePicker(fragmentManager, preferences) { chosenTime ->
|
||||
date[Calendar.HOUR_OF_DAY] = chosenTime[Calendar.HOUR_OF_DAY]
|
||||
date[Calendar.MINUTE] = chosenTime[Calendar.MINUTE]
|
||||
onSubmit(date)
|
||||
}
|
||||
timepicker.show(fragmentManager, "timepicker")
|
||||
|
||||
}
|
||||
datePicker.show(fragmentManager, "datepicker")
|
||||
}
|
||||
|
||||
fun showTimePicker(fragmentManager: FragmentManager , onSubmit : (chosenTime: Calendar) -> Unit ){
|
||||
fun showTimePicker(fragmentManager: FragmentManager , preferences: SharedPreferences, onSubmit : (chosenTime: Calendar) -> Unit ){
|
||||
val currentDate = Calendar.getInstance()
|
||||
currentDate.timeInMillis = (currentDate.timeInMillis - (currentDate.timeInMillis % 1800000)) + 1800000
|
||||
val date = Calendar.getInstance()
|
||||
|
||||
val lastTime = preferences.getString("latest_timepicker_date", "${currentDate.get(Calendar.HOUR_OF_DAY)}:${currentDate.get(Calendar.MINUTE)}")!!
|
||||
val hour = lastTime.split(":")[0].toInt()
|
||||
val minute = lastTime.split(":")[1].toInt()
|
||||
|
||||
val timepicker = MaterialTimePicker.Builder()
|
||||
.setTimeFormat(TimeFormat.CLOCK_24H)
|
||||
.setHour(currentDate.get(Calendar.HOUR_OF_DAY))
|
||||
.setMinute(currentDate.get(Calendar.MINUTE))
|
||||
.setHour(hour)
|
||||
.setMinute(minute)
|
||||
.build()
|
||||
|
||||
timepicker.addOnPositiveButtonClickListener{
|
||||
date[Calendar.HOUR_OF_DAY] = timepicker.hour
|
||||
date[Calendar.MINUTE] = timepicker.minute
|
||||
preferences.edit().putString("latest_timepicker_date", "${timepicker.hour}:${timepicker.minute}").apply()
|
||||
onSubmit(date)
|
||||
}
|
||||
timepicker.show(fragmentManager, "timepicker")
|
||||
|
|
|
|||
|
|
@ -151,8 +151,7 @@ class NewPipeUtil(context: Context) {
|
|||
|
||||
fun getChannelData(url: String, progress: (pagedResults: MutableList<ResultItem>) -> Unit) : Result<List<ResultItem>> {
|
||||
try {
|
||||
//TODO BROKEN FOR NOW
|
||||
return Result.failure(Throwable())
|
||||
//return Result.failure(Throwable())
|
||||
val req = ChannelInfo.getInfo(ServiceList.YouTube, url)
|
||||
println(Gson().toJson(req))
|
||||
val items = mutableListOf<ResultItem>()
|
||||
|
|
|
|||
|
|
@ -49,17 +49,15 @@
|
|||
</shortcut>
|
||||
|
||||
<shortcut
|
||||
android:shortcutId="newTemplate"
|
||||
android:shortcutId="terminal"
|
||||
android:enabled="true"
|
||||
android:icon="@drawable/ic_terminal_red"
|
||||
android:shortcutShortLabel="@string/new_template"
|
||||
android:shortcutDisabledMessage="@string/new_template">
|
||||
android:shortcutShortLabel="@string/terminal"
|
||||
android:shortcutDisabledMessage="@string/terminal">
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
android:action="android.intent.category.DEFAULT"
|
||||
android:targetPackage="com.deniscerri.ytdl"
|
||||
android:targetClass="com.deniscerri.ytdl.receiver.TransparentActivity">
|
||||
|
||||
<extra android:name="action" android:value="NEW_TEMPLATE" />
|
||||
android:targetClass="com.deniscerri.ytdl.ui.more.terminal.TerminalActivity">
|
||||
|
||||
</intent>
|
||||
</shortcut>
|
||||
|
|
|
|||
Loading…
Reference in a new issue