fixed some things in download card

This commit is contained in:
Denis Çerri 2023-01-22 18:16:22 +01:00
parent 2371ab734d
commit 19eab5e054
No known key found for this signature in database
GPG key ID: 95C43D517D830350
22 changed files with 842 additions and 715 deletions

View file

@ -167,7 +167,7 @@
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View file

@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "5939be553c41327f4f64e1d62d008968",
"identityHash": "c8effa77edcd8cf5e46a79fd1a791535",
"entities": [
{
"tableName": "results",
@ -198,7 +198,7 @@
},
{
"tableName": "downloads",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `thumb` TEXT NOT NULL, `duration` TEXT NOT NULL, `type` TEXT NOT NULL, `audioQualityId` TEXT NOT NULL, `videoQualityId` TEXT NOT NULL, `customTemplateId` INTEGER NOT NULL, `audioFormat` TEXT NOT NULL, `videoFormat` TEXT NOT NULL, `formatDesc` TEXT NOT NULL, `removeAudio` INTEGER NOT NULL DEFAULT 0, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `downloadSize` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `embedSubs` INTEGER NOT NULL, `addChapters` INTEGER NOT NULL, `SaveThumb` INTEGER NOT NULL, `status` TEXT NOT NULL DEFAULT 'Queued', `workID` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `thumb` TEXT NOT NULL, `duration` TEXT NOT NULL, `type` TEXT NOT NULL, `audioFormat` TEXT NOT NULL, `videoFormat` TEXT NOT NULL, `audioFormatId` TEXT NOT NULL, `videoFormatId` TEXT NOT NULL, `customTemplateId` INTEGER NOT NULL, `formatDesc` TEXT NOT NULL, `removeAudio` INTEGER NOT NULL DEFAULT 0, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `downloadSize` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `embedSubs` INTEGER NOT NULL, `addChapters` INTEGER NOT NULL, `SaveThumb` INTEGER NOT NULL, `ext` TEXT NOT NULL, `filesize` TEXT NOT NULL, `status` TEXT NOT NULL DEFAULT 'Queued', `workID` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)",
"fields": [
{
"fieldPath": "url",
@ -236,24 +236,6 @@
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "audioQualityId",
"columnName": "audioQualityId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "videoQualityId",
"columnName": "videoQualityId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "customTemplateId",
"columnName": "customTemplateId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "audioFormat",
"columnName": "audioFormat",
@ -266,6 +248,24 @@
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "audioFormatId",
"columnName": "audioFormatId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "videoFormatId",
"columnName": "videoFormatId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "customTemplateId",
"columnName": "customTemplateId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "formatDesc",
"columnName": "formatDesc",
@ -321,6 +321,18 @@
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ext",
"columnName": "ext",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "filesize",
"columnName": "filesize",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "status",
"columnName": "status",
@ -386,7 +398,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, '5939be553c41327f4f64e1d62d008968')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c8effa77edcd8cf5e46a79fd1a791535')"
]
}
}

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
@ -77,6 +78,17 @@
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<!-- If you are using androidx.startup to initialize other components -->
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>
</application>
</manifest>

View file

@ -17,6 +17,7 @@ import androidx.core.app.ActivityCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.work.WorkManager
import com.deniscerri.ytdlnis.DownloaderService.LocalBinder
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.databinding.ActivityMainBinding
@ -38,17 +39,14 @@ import java.util.*
import kotlin.system.exitProcess
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
lateinit var context: Context
private lateinit var homeFragment: HomeFragment
private lateinit var downloadsFragment: DownloadsFragment
private lateinit var moreFragment: MoreFragment
private lateinit var lastFragment: Fragment
private lateinit var fm: FragmentManager
private var isDownloadServiceRunning = false
var downloaderService: DownloaderService? = null
private lateinit var listeners: ArrayList<IDownloaderListener>
private var iDownloaderService: IDownloaderService? = null
private lateinit var workManager: WorkManager
private val serviceConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
@ -83,6 +81,8 @@ class MainActivity : AppCompatActivity() {
reconnectDownloadService()
checkUpdate()
fm = supportFragmentManager
workManager = WorkManager.getInstance(context)
homeFragment = HomeFragment()
downloadsFragment = DownloadsFragment()
moreFragment = MoreFragment()
@ -256,6 +256,10 @@ class MainActivity : AppCompatActivity() {
stopDownloadService()
}
fun cancelAllDownloads() {
workManager.cancelAllWork();
}
fun removeItemFromDownloadQueue(video: ResultItem?, type: String?) {
iDownloaderService!!.removeItemFromDownloadQueue(video, type)
}
@ -368,5 +372,11 @@ class MainActivity : AppCompatActivity() {
companion object {
private const val TAG = "MainActivity"
private lateinit var moreFragment: MoreFragment
private lateinit var lastFragment: Fragment
private lateinit var fm: FragmentManager
private var isDownloadServiceRunning = false
private lateinit var listeners: ArrayList<IDownloaderListener>
private var iDownloaderService: IDownloaderService? = null
}
}

View file

@ -1,214 +0,0 @@
package com.deniscerri.ytdlnis.adapter;
import android.app.Activity;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import com.deniscerri.ytdlnis.R;
import com.deniscerri.ytdlnis.database.models.HistoryItem;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.card.MaterialCardView;
import com.squareup.picasso.Picasso;
import java.io.File;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
public class HistoryAdapter extends ListAdapter<HistoryItem, HistoryAdapter.ViewHolder> {
private ArrayList<Integer> checkedItems;
private final OnItemClickListener onItemClickListener;
private Activity activity;
public HistoryAdapter(OnItemClickListener onItemClickListener, Activity activity) {
super(DIFF_CALLBACK);
this.checkedItems = new ArrayList<>();
this.onItemClickListener = onItemClickListener;
this.activity = activity;
}
private static final DiffUtil.ItemCallback<HistoryItem> DIFF_CALLBACK = new DiffUtil.ItemCallback<HistoryItem>() {
@Override
public boolean areItemsTheSame(@NonNull HistoryItem oldItem, @NonNull HistoryItem newItem) {
return oldItem.getId() == newItem.getId();
}
@Override
public boolean areContentsTheSame(@NonNull HistoryItem oldItem, @NonNull HistoryItem newItem) {
return oldItem.getTime() == newItem.getTime();
}
};
@Override
public int getItemViewType(int position) {
return super.getItemViewType(position);
}
public static class ViewHolder extends RecyclerView.ViewHolder {
private MaterialCardView cardView;
public ViewHolder(@NonNull View itemView, OnItemClickListener onItemClickListener) {
super(itemView);
cardView = itemView.findViewById(R.id.downloads_card_view);
}
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View cardView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.downloads_card, parent, false);
return new HistoryAdapter.ViewHolder(cardView, onItemClickListener);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
HistoryItem video = getItem(position);
MaterialCardView card = holder.cardView;
// THUMBNAIL ----------------------------------
ImageView thumbnail = card.findViewById(R.id.downloads_image_view);
String imageURL= video.getThumb();
Handler uiHandler = new Handler(Looper.getMainLooper());
if (!imageURL.isEmpty()){
uiHandler.post(() -> Picasso.get().load(imageURL).into(thumbnail));
}else {
uiHandler.post(() -> Picasso.get().load(R.color.black).into(thumbnail));
}
thumbnail.setColorFilter(Color.argb(95, 0, 0, 0));
// TITLE ----------------------------------
TextView videoTitle = card.findViewById(R.id.downloads_title);
String title = video.getTitle();
if(title.length() > 100){
title = title.substring(0, 40) + "...";
}
videoTitle.setText(title);
// Bottom Info ----------------------------------
TextView bottomInfo = card.findViewById(R.id.downloads_info_bottom);
String info = video.getAuthor();
if (!video.getDuration().isEmpty()){
if (!video.getAuthor().isEmpty()) info += "";
info += video.getDuration();
}
bottomInfo.setText(info);
// TIME DOWNLOADED ----------------------------------
TextView datetime = card.findViewById(R.id.downloads_info_time);
long time = video.getTime();
String downloadedTime;
if (time == 0){
downloadedTime = activity.getString(R.string.currently_downloading) + " " + video.getType();
}else{
Calendar cal = Calendar.getInstance();
Date date = new Date(time*1000L);
cal.setTime(date);
int day = cal.get(Calendar.DAY_OF_MONTH);
String month = cal.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault());
int year = cal.get(Calendar.YEAR);
DateFormat formatter = new SimpleDateFormat("HH:mm", Locale.getDefault());
String timeString = formatter.format(date);
downloadedTime = day + " " + month + " " + year + " - " + timeString;
}
datetime.setText(downloadedTime);
// BUTTON ----------------------------------
LinearLayout buttonLayout = card.findViewById(R.id.downloads_download_button_layout);
MaterialButton btn = buttonLayout.findViewById(R.id.downloads_download_button_type);
boolean filePresent = true;
//IS IN THE FILE SYSTEM?
String path = video.getDownloadPath();
File file = new File(path);
if(!file.exists() && !path.isEmpty()){
filePresent = false;
thumbnail.setColorFilter(new ColorMatrixColorFilter(new ColorMatrix(){{setSaturation(0f);}}));
thumbnail.setAlpha(0.7f);
}
if(!video.getType().isEmpty()){
if(video.getType().equals("audio")){
if (filePresent) btn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_music_downloaded));
else btn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_music));
}else{
if (filePresent) btn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_video_downloaded));
else btn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_video));
}
}
if (btn.hasOnClickListeners()) btn.setOnClickListener(null);
if(checkedItems.contains(position)){
card.setChecked(true);
card.setStrokeWidth(5);
}else{
card.setChecked(false);
card.setStrokeWidth(0);
}
boolean finalFilePresent = filePresent;
card.setOnLongClickListener(view -> {
checkCard(card, position);
return true;
});
card.setOnClickListener(view -> {
if(checkedItems.size() > 0){
checkCard(card, video.getId());
}else{
onItemClickListener.onCardClick(video.getId(), finalFilePresent);
}
});
}
private void checkCard(MaterialCardView card, int videoID){
if(card.isChecked()){
card.setStrokeWidth(0);
checkedItems.remove(videoID);
}else{
card.setStrokeWidth(5);
checkedItems.add(videoID);
}
card.setChecked(!card.isChecked());
onItemClickListener.onCardSelect(videoID, card.isChecked());
}
public interface OnItemClickListener {
void onCardClick(int position, boolean isPresent);
void onCardSelect(int position, boolean isChecked);
void onButtonClick(int position);
}
public void clearCheckedVideos(){
int size = checkedItems.size();
for (int i = 0; i < size; i++){
int position = checkedItems.get(i);
notifyItemChanged(position);
}
checkedItems.clear();
}
}

View file

@ -0,0 +1,189 @@
package com.deniscerri.ytdlnis.adapter
import android.app.Activity
import android.graphics.Color
import android.graphics.ColorMatrix
import android.graphics.ColorMatrixColorFilter
import android.os.Handler
import android.os.Looper
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.HistoryItem
import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView
import com.squareup.picasso.Picasso
import java.io.File
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.*
class HistoryAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter<HistoryItem?, HistoryAdapter.ViewHolder>(AsyncDifferConfig.Builder(DIFF_CALLBACK).build()) {
private val checkedItems: ArrayList<Int>
private val onItemClickListener: OnItemClickListener
private val activity: Activity
init {
checkedItems = ArrayList()
this.onItemClickListener = onItemClickListener
this.activity = activity
}
class ViewHolder(itemView: View, onItemClickListener: OnItemClickListener?) : RecyclerView.ViewHolder(itemView) {
val cardView: MaterialCardView
init {
cardView = itemView.findViewById(R.id.downloads_card_view)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val cardView = LayoutInflater.from(parent.context)
.inflate(R.layout.downloads_card, parent, false)
return ViewHolder(cardView, onItemClickListener)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val video = getItem(position)
val card = holder.cardView
// THUMBNAIL ----------------------------------
val thumbnail = card.findViewById<ImageView>(R.id.downloads_image_view)
val imageURL = video!!.thumb
val uiHandler = Handler(Looper.getMainLooper())
if (imageURL.isNotEmpty()) {
uiHandler.post { Picasso.get().load(imageURL).into(thumbnail) }
} else {
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
}
thumbnail.setColorFilter(Color.argb(95, 0, 0, 0))
// TITLE ----------------------------------
val videoTitle = card.findViewById<TextView>(R.id.downloads_title)
var title = video.title
if (title.length > 100) {
title = title.substring(0, 40) + "..."
}
videoTitle.text = title
// Bottom Info ----------------------------------
val bottomInfo = card.findViewById<TextView>(R.id.downloads_info_bottom)
var info = video.author
if (video.duration.isNotEmpty()) {
if (video.author.isNotEmpty()) info += ""
info += video.duration
}
bottomInfo.text = info
// TIME DOWNLOADED ----------------------------------
val datetime = card.findViewById<TextView>(R.id.downloads_info_time)
val time = video.time
val downloadedTime: String
if (time == 0L) {
downloadedTime = activity.getString(R.string.currently_downloading) + " " + video.type
} else {
val cal = Calendar.getInstance()
val date = Date(time * 1000L)
cal.time = date
val day = cal[Calendar.DAY_OF_MONTH]
val month = cal.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault())
val year = cal[Calendar.YEAR]
val formatter: DateFormat = SimpleDateFormat("HH:mm", Locale.getDefault())
val timeString = formatter.format(date)
downloadedTime = "$day $month $year - $timeString"
}
datetime.text = downloadedTime
// BUTTON ----------------------------------
val buttonLayout = card.findViewById<LinearLayout>(R.id.downloads_download_button_layout)
val btn = buttonLayout.findViewById<MaterialButton>(R.id.downloads_download_button_type)
var filePresent = true
//IS IN THE FILE SYSTEM?
val path = video.downloadPath
val file = File(path)
if (!file.exists() && path.isNotEmpty()) {
filePresent = false
thumbnail.colorFilter = ColorMatrixColorFilter(object : ColorMatrix() {
init {
setSaturation(0f)
}
})
thumbnail.alpha = 0.7f
}
if (video.type.isNotEmpty()) {
if (video.type == "audio") {
if (filePresent) btn.icon = ContextCompat.getDrawable(activity, R.drawable.ic_music_downloaded) else btn.icon = ContextCompat.getDrawable(activity, R.drawable.ic_music)
} else {
if (filePresent) btn.icon = ContextCompat.getDrawable(activity, R.drawable.ic_video_downloaded) else btn.icon = ContextCompat.getDrawable(activity, R.drawable.ic_video)
}
}
if (btn.hasOnClickListeners()) btn.setOnClickListener(null)
if (checkedItems.contains(position)) {
card.isChecked = true
card.strokeWidth = 5
} else {
card.isChecked = false
card.strokeWidth = 0
}
val finalFilePresent = filePresent
card.setOnLongClickListener {
checkCard(card, position)
true
}
card.setOnClickListener {
if (checkedItems.size > 0) {
checkCard(card, video.id!!)
} else {
onItemClickListener.onCardClick(video.id!!, finalFilePresent)
}
}
}
private fun checkCard(card: MaterialCardView, videoID: Int) {
if (card.isChecked) {
card.strokeWidth = 0
checkedItems.removeAt(videoID)
} else {
card.strokeWidth = 5
checkedItems.add(videoID)
}
card.isChecked = !card.isChecked
onItemClickListener.onCardSelect(videoID, card.isChecked)
}
interface OnItemClickListener {
fun onCardClick(position: Int, isPresent: Boolean)
fun onCardSelect(position: Int, isChecked: Boolean)
fun onButtonClick(position: Int)
}
fun clearCheckedVideos() {
val size = checkedItems.size
for (i in 0 until size) {
val position = checkedItems[i]
notifyItemChanged(position)
}
checkedItems.clear()
}
companion object {
private val DIFF_CALLBACK: DiffUtil.ItemCallback<HistoryItem> = object : DiffUtil.ItemCallback<HistoryItem>() {
override fun areItemsTheSame(oldItem: HistoryItem, newItem: HistoryItem): Boolean {
return oldItem.id === newItem.id
}
override fun areContentsTheSame(oldItem: HistoryItem, newItem: HistoryItem): Boolean {
return oldItem.time == newItem.time
}
}
}
}

View file

@ -1,212 +0,0 @@
package com.deniscerri.ytdlnis.adapter;
import android.app.Activity;
import android.graphics.Color;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import com.deniscerri.ytdlnis.R;
import com.deniscerri.ytdlnis.database.models.ResultItem;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.card.MaterialCardView;
import com.google.android.material.progressindicator.LinearProgressIndicator;
import com.squareup.picasso.Picasso;
import java.util.ArrayList;
public class HomeAdapter extends ListAdapter<ResultItem, HomeAdapter.ViewHolder> {
private final ArrayList<Integer> checkedVideos;
private final OnItemClickListener onItemClickListener;
private Activity activity;
public HomeAdapter(HomeAdapter.OnItemClickListener onItemClickListener, Activity activity){
super(DIFF_CALLBACK);
this.checkedVideos = new ArrayList<>();
this.onItemClickListener = onItemClickListener;
this.activity = activity;
}
private static final DiffUtil.ItemCallback<ResultItem> DIFF_CALLBACK = new DiffUtil.ItemCallback<ResultItem>() {
@Override
public boolean areItemsTheSame(@NonNull ResultItem oldItem, @NonNull ResultItem newItem) {
return oldItem.getId() == newItem.getId();
}
@Override
public boolean areContentsTheSame(@NonNull ResultItem oldItem, @NonNull ResultItem newItem) {
return oldItem.getUrl().equals(newItem.getUrl());
}
};
@Override
public int getItemViewType(int position) {
return super.getItemViewType(position);
}
public static class ViewHolder extends RecyclerView.ViewHolder {
private MaterialCardView cardView;
public ViewHolder(@NonNull View itemView, OnItemClickListener onItemClickListener) {
super(itemView);
cardView = itemView.findViewById(R.id.result_card_view);
}
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View cardView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.result_card, parent, false);
return new HomeAdapter.ViewHolder(cardView, onItemClickListener);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
ResultItem video = getItem(position);
MaterialCardView card = holder.cardView;
// THUMBNAIL ----------------------------------
ImageView thumbnail = card.findViewById(R.id.result_image_view);
String imageURL= video.getThumb();
if (!imageURL.isEmpty()){
Handler uiHandler = new Handler(Looper.getMainLooper());
uiHandler.post(() -> Picasso.get().load(imageURL).into(thumbnail));
thumbnail.setColorFilter(Color.argb(70, 0, 0, 0));
}else {
Handler uiHandler = new Handler(Looper.getMainLooper());
uiHandler.post(() -> Picasso.get().load(R.color.black).into(thumbnail));
thumbnail.setColorFilter(Color.argb(70, 0, 0, 0));
}
// TITLE ----------------------------------
TextView videoTitle = card.findViewById(R.id.result_title);
String title = video.getTitle();
if(title.length() > 100){
title = title.substring(0, 40) + "...";
}
videoTitle.setText(title);
// Bottom Info ----------------------------------
TextView author = card.findViewById(R.id.author);
author.setText(video.getAuthor());
TextView duration = card.findViewById(R.id.duration);
if (!video.getDuration().isEmpty()){
duration.setText(video.getDuration());
}
// BUTTONS ----------------------------------
int videoID = video.getId();
LinearLayout buttonLayout = card.findViewById(R.id.download_button_layout);
MaterialButton musicBtn = buttonLayout.findViewById(R.id.download_music);
musicBtn.setTag(videoID + "##audio");
musicBtn.setTag(R.id.cancelDownload, "false");
musicBtn.setOnClickListener(view -> onItemClickListener.onButtonClick(position, "audio"));
MaterialButton videoBtn = buttonLayout.findViewById(R.id.download_video);
videoBtn.setTag(videoID + "##video");
videoBtn.setTag(R.id.cancelDownload, "false");
videoBtn.setOnClickListener(view -> onItemClickListener.onButtonClick(position, "video"));
// PROGRESS BAR ----------------------------------------------------
LinearProgressIndicator progressBar = card.findViewById(R.id.download_progress);
progressBar.setTag(videoID + "##progress");
progressBar.setProgress(0);
progressBar.setIndeterminate(true);
progressBar.setVisibility(View.GONE);
// if (video.isDownloading()){
// progressBar.setVisibility(View.VISIBLE);
// }else {
// progressBar.setProgress(0);
// progressBar.setIndeterminate(true);
// progressBar.setVisibility(View.GONE);
// }
//
// if (video.isDownloadingAudio()) {
// musicBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_cancel));
// musicBtn.setTag(R.id.cancelDownload, "true");
// }else{
// if(video.isAudioDownloaded() == 1){
// musicBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_music_downloaded));
// }else{
// musicBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_music));
// }
// }
//
// if (video.isDownloadingVideo()){
// videoBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_cancel));
// videoBtn.setTag(R.id.cancelDownload, "true");
// }else{
// if(video.isVideoDownloaded() == 1){
// videoBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_video_downloaded));
// }else{
// videoBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_video));
// }
// }
if(checkedVideos.contains(videoID)){
card.setChecked(true);
card.setStrokeWidth(5);
}else{
card.setChecked(false);
card.setStrokeWidth(0);
}
card.setTag(videoID + "##card");
card.setOnLongClickListener(view -> {
checkCard(card, videoID);
return true;
});
card.setOnClickListener(view -> {
if(checkedVideos.size() > 0){
checkCard(card, videoID);
}
});
}
private void checkCard(MaterialCardView card, int videoID){
if(card.isChecked()){
card.setStrokeWidth(0);
checkedVideos.remove(Integer.valueOf(videoID));
}else{
card.setStrokeWidth(5);
checkedVideos.add(videoID);
}
card.setChecked(!card.isChecked());
onItemClickListener.onCardClick(videoID, card.isChecked());
}
public interface OnItemClickListener {
void onButtonClick(int videoID, String type);
void onCardClick(int videoID, boolean add);
}
public void clearCheckedVideos(){
int size = checkedVideos.size();
for (int i = 0; i < size; i++){
int position = checkedVideos.get(i);
notifyItemChanged(position);
}
checkedVideos.clear();
}
}

View file

@ -0,0 +1,186 @@
package com.deniscerri.ytdlnis.adapter
import android.app.Activity
import android.graphics.Color
import android.os.Handler
import android.os.Looper
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView
import com.google.android.material.progressindicator.LinearProgressIndicator
import com.squareup.picasso.Picasso
class HomeAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter<ResultItem?, HomeAdapter.ViewHolder>(AsyncDifferConfig.Builder(DIFF_CALLBACK).build()) {
private val checkedVideos: ArrayList<String>
private val onItemClickListener: OnItemClickListener
private val activity: Activity
init {
checkedVideos = ArrayList()
this.onItemClickListener = onItemClickListener
this.activity = activity
}
class ViewHolder(itemView: View, onItemClickListener: OnItemClickListener?) : RecyclerView.ViewHolder(itemView) {
val cardView: MaterialCardView
init {
cardView = itemView.findViewById(R.id.result_card_view)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val cardView = LayoutInflater.from(parent.context)
.inflate(R.layout.result_card, parent, false)
return ViewHolder(cardView, onItemClickListener)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val video = getItem(position)
val card = holder.cardView
// THUMBNAIL ----------------------------------
val thumbnail = card.findViewById<ImageView>(R.id.result_image_view)
val imageURL = video!!.thumb
if (imageURL.isNotEmpty()) {
val uiHandler = Handler(Looper.getMainLooper())
uiHandler.post { Picasso.get().load(imageURL).into(thumbnail) }
thumbnail.setColorFilter(Color.argb(70, 0, 0, 0))
} else {
val uiHandler = Handler(Looper.getMainLooper())
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
thumbnail.setColorFilter(Color.argb(70, 0, 0, 0))
}
// TITLE ----------------------------------
val videoTitle = card.findViewById<TextView>(R.id.result_title)
var title = video.title
if (title.length > 100) {
title = title.substring(0, 40) + "..."
}
videoTitle.text = title
// Bottom Info ----------------------------------
val author = card.findViewById<TextView>(R.id.author)
author.text = video.author
val duration = card.findViewById<TextView>(R.id.duration)
if (video.duration.isNotEmpty()) {
duration.text = video.duration
}
// BUTTONS ----------------------------------
val videoURL = video.url
val buttonLayout = card.findViewById<LinearLayout>(R.id.download_button_layout)
val musicBtn = buttonLayout.findViewById<MaterialButton>(R.id.download_music)
musicBtn.tag = "$videoURL##audio"
musicBtn.setTag(R.id.cancelDownload, "false")
musicBtn.setOnClickListener { onItemClickListener.onButtonClick(videoURL, "audio") }
val videoBtn = buttonLayout.findViewById<MaterialButton>(R.id.download_video)
videoBtn.tag = "$videoURL##video"
videoBtn.setTag(R.id.cancelDownload, "false")
videoBtn.setOnClickListener { onItemClickListener.onButtonClick(videoURL, "video") }
// PROGRESS BAR ----------------------------------------------------
val progressBar = card.findViewById<LinearProgressIndicator>(R.id.download_progress)
progressBar.tag = "$videoURL##progress"
progressBar.progress = 0
progressBar.isIndeterminate = true
progressBar.visibility = View.GONE
// if (video.isDownloading()){
// progressBar.setVisibility(View.VISIBLE);
// }else {
// progressBar.setProgress(0);
// progressBar.setIndeterminate(true);
// progressBar.setVisibility(View.GONE);
// }
//
// if (video.isDownloadingAudio()) {
// musicBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_cancel));
// musicBtn.setTag(R.id.cancelDownload, "true");
// }else{
// if(video.isAudioDownloaded() == 1){
// musicBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_music_downloaded));
// }else{
// musicBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_music));
// }
// }
//
// if (video.isDownloadingVideo()){
// videoBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_cancel));
// videoBtn.setTag(R.id.cancelDownload, "true");
// }else{
// if(video.isVideoDownloaded() == 1){
// videoBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_video_downloaded));
// }else{
// videoBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_video));
// }
// }
if (checkedVideos.contains(videoURL)) {
card.isChecked = true
card.strokeWidth = 5
} else {
card.isChecked = false
card.strokeWidth = 0
}
card.tag = "$videoURL##card"
card.setOnLongClickListener {
checkCard(card, videoURL)
true
}
card.setOnClickListener {
if (checkedVideos.size > 0) {
checkCard(card, videoURL)
}
}
}
private fun checkCard(card: MaterialCardView, videoURL: String) {
if (card.isChecked) {
card.strokeWidth = 0
checkedVideos.remove(videoURL)
} else {
card.strokeWidth = 5
checkedVideos.add(videoURL)
}
card.isChecked = !card.isChecked
onItemClickListener.onCardClick(videoURL, card.isChecked)
}
interface OnItemClickListener {
fun onButtonClick(videoURL: String, type: String?)
fun onCardClick(videoURL: String, add: Boolean)
}
fun clearCheckedVideos() {
// val size = checkedVideos.size
// for (i in 0 until size) {
// val position = checkedVideos[i]
// notifyItemChanged(position)
// }
// checkedVideos.clear()
}
companion object {
private val DIFF_CALLBACK: DiffUtil.ItemCallback<ResultItem> = object : DiffUtil.ItemCallback<ResultItem>() {
override fun areItemsTheSame(oldItem: ResultItem, newItem: ResultItem): Boolean {
return oldItem.id === newItem.id
}
override fun areContentsTheSame(oldItem: ResultItem, newItem: ResultItem): Boolean {
return oldItem.url == newItem.url
}
}
}
}

View file

@ -30,7 +30,9 @@ abstract class DBManager : RoomDatabase(){
context.applicationContext,
DBManager::class.java,
"YTDLnisDatabase"
).build()
)
.allowMainThreadQueries()
.build()
instance = dbInstance
dbInstance
}

View file

@ -7,8 +7,8 @@ import com.deniscerri.ytdlnis.database.models.ResultItem
@Dao
interface FormatDao {
@Query("SELECT * FROM formats")
fun getFormatsByItemId() : LiveData<List<Format>>
@Query("SELECT * FROM formats WHERE itemId=:itemId")
fun getFormatsByItemId(itemId: Int) : List<Format>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(item: Format)

View file

@ -6,32 +6,32 @@ import androidx.room.PrimaryKey
@Entity(tableName = "downloads")
data class DownloadItem(
val url: String,
val title: String,
val author: String,
val thumb: String,
val duration: String,
val type: String,
val audioQualityId: String,
val videoQualityId: String,
val customTemplateId: Int,
val audioFormat: String,
val videoFormat: String,
val formatDesc: String,
@ColumnInfo(defaultValue = "0")
val url: String,
var title: String,
var author: String,
val thumb: String,
val duration: String,
val type: String,
var audioFormat: String,
var videoFormat: String,
var audioFormatId: String,
var videoFormatId: String,
var customTemplateId: Int,
val formatDesc: String,
@ColumnInfo(defaultValue = "0")
val removeAudio: Boolean,
val downloadPath: String,
val website: String,
val downloadSize: String,
val playlistTitle: String,
val embedSubs: Boolean,
val addChapters: Boolean,
val SaveThumb: Boolean,
var ext: String,
var filesize: String,
@ColumnInfo(defaultValue = "Queued")
val downloadPath: String,
val website: String,
val downloadSize: String,
val playlistTitle: String,
val embedSubs: Boolean,
val addChapters: Boolean,
val SaveThumb: Boolean,
var ext: String,
var filesize: String,
@ColumnInfo(defaultValue = "Queued")
var status: String,
val workID: Int
val workID: Int
){
@PrimaryKey(autoGenerate = true)
var id: Int? = null

View file

@ -5,13 +5,13 @@ import androidx.room.PrimaryKey
@Entity(tableName = "results")
data class ResultItem(
val url: String,
val title: String,
val author: String,
val duration: String,
val thumb: String,
val website: String,
var playlistTitle: String
val url: String,
var title: String,
var author: String,
val duration: String,
val thumb: String,
val website: String,
var playlistTitle: String
){
@PrimaryKey(autoGenerate = true)
var id: Int? = null

View file

@ -3,13 +3,15 @@ package com.deniscerri.ytdlnis.database.repository
import android.content.Context
import android.util.Log
import androidx.lifecycle.LiveData
import com.deniscerri.ytdlnis.database.dao.CommandTemplateDao
import com.deniscerri.ytdlnis.database.dao.FormatDao
import com.deniscerri.ytdlnis.database.dao.ResultDao
import com.deniscerri.ytdlnis.database.models.CommandTemplate
import com.deniscerri.ytdlnis.database.models.Format
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.util.InfoUtil
class ResultRepository(private val resultDao: ResultDao, private val formatDao: FormatDao, private val context: Context) {
class ResultRepository(private val resultDao: ResultDao, private val formatDao: FormatDao, private val commandTemplateDao: CommandTemplateDao, private val context: Context) {
private val tag: String = "ResultRepository"
val allResults : LiveData<List<ResultItem>> = resultDao.getResults()
@ -119,5 +121,12 @@ class ResultRepository(private val resultDao: ResultDao, private val formatDao:
resultDao.update(item)
}
fun getFormats(item: ResultItem): List<Format> {
return formatDao.getFormatsByItemId(item.id!!)
}
fun getTemplates() : List<CommandTemplate> {
return commandTemplateDao.getAllTemplates()
}
}

View file

@ -1,14 +1,20 @@
package com.deniscerri.ytdlnis.database.viewmodel
import android.app.Application
import android.content.Context
import android.util.Log
import androidx.lifecycle.*
import com.deniscerri.ytdlnis.App
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.DBManager
import com.deniscerri.ytdlnis.database.dao.FormatDao
import com.deniscerri.ytdlnis.database.models.Format
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.repository.ResultRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.util.regex.Pattern
import kotlin.coroutines.coroutineContext
class ResultViewModel(application: Application) : AndroidViewModel(application) {
private val tag: String = "ResultViewModel"
@ -19,7 +25,8 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
init {
val dao = DBManager.getInstance(application).resultDao
val formatDao = DBManager.getInstance(application).formatDao
repository = ResultRepository(dao, formatDao, getApplication<Application>().applicationContext)
val commandDao = DBManager.getInstance(application).commandTemplateDao
repository = ResultRepository(dao, formatDao, commandDao, getApplication<Application>().applicationContext)
items = repository.allResults
loadingItems.postValue(false)
}
@ -86,4 +93,31 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
fun update(item: ResultItem) = viewModelScope.launch(Dispatchers.IO){
repository.update(item);
}
fun getFormats(item: ResultItem, type: String) : List<Format> {
val list = repository.getFormats(item)
val formats = mutableListOf<Format>()
if (list.isEmpty()){
when(type){
"audio" -> {
val audioFormats = getApplication<App>().resources.getStringArray(R.array.audio_formats)
audioFormats.forEach { formats.add(Format(item.id!!, it, "", "", 0, it)) }
}
"video" -> {
val videoFormats = getApplication<App>().resources.getStringArray(R.array.video_formats)
videoFormats.forEach { formats.add(Format(item.id!!, it, "", "", 0, it)) }
}
}
return formats
}
when(type){
"audio" -> return list.filter { it.format.contains("audio", ignoreCase = true) }
"video" -> return list.filter { !it.format.contains("audio", ignoreCase = true) }
}
val templates = repository.getTemplates()
templates.forEach {
formats.add(Format(item.id!!, it.title, it.id.toString(), "", 0, it.content))
}
return formats
}
}

View file

@ -105,7 +105,7 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
historyAdapter =
HistoryAdapter(
this,
activity
requireActivity()
)
recyclerView = view.findViewById(R.id.recyclerviewdownloadss)
recyclerView?.layoutManager = LinearLayoutManager(context)

View file

@ -7,13 +7,17 @@ import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.text.Editable
import android.text.InputType
import android.text.TextWatcher
import android.util.Log
import android.view.*
import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.*
import androidx.appcompat.widget.SearchView
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.text.trimmedLength
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
@ -22,6 +26,7 @@ import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.adapter.HomeAdapter
import com.deniscerri.ytdlnis.database.DatabaseManager
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.databinding.FragmentDownloadsBinding
@ -38,7 +43,11 @@ import com.google.android.material.floatingactionbutton.ExtendedFloatingActionBu
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.progressindicator.LinearProgressIndicator
import com.google.android.material.textfield.TextInputLayout
import kotlinx.coroutines.runBlocking
import java.text.DecimalFormat
import java.util.*
import kotlin.math.log10
import kotlin.math.pow
class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickListener {
private var progressBar: LinearProgressIndicator? = null
@ -121,7 +130,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
homeAdapter =
HomeAdapter(
this,
activity
requireActivity()
)
recyclerView = view.findViewById(R.id.recyclerViewHome)
recyclerView?.layoutManager = LinearLayoutManager(context)
@ -336,11 +345,10 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
selectedObjects = ArrayList()
downloadAllFabCoordinator!!.visibility = GONE
downloadFabs!!.visibility = GONE
}
// } else if (itemId == R.id.cancel_download) {
// try {
// mainActivity!!.cancelDownloadService()
// topAppBar!!.menu.findItem(itemId).isVisible = false
} else if (itemId == R.id.cancel_download) {
try {
mainActivity!!.cancelAllDownloads()
topAppBar!!.menu.findItem(itemId).isVisible = false
// for (i in downloadInfo!!.downloadQueue.indices) {
// val vid = downloadInfo!!.downloadQueue[i]
// val type = vid.downloadedType
@ -348,9 +356,9 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
// }
// downloadQueue = ArrayList()
// downloading = false
// } catch (ignored: Exception) {
// }
// }
} catch (ignored: Exception) {
}
}
true
}
}
@ -380,11 +388,11 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
}
@SuppressLint("ResourceType")
override fun onButtonClick(position: Int, type: String) {
// Log.e(TAG, type)
// val vid = resultsList!![position]
// vid!!.downloadedType = type
// val btn = recyclerView!!.findViewWithTag<MaterialButton>(vid.videoId + "##" + type)
override fun onButtonClick(videoURL: String, type: String?) {
Log.e(TAG, type!! + " " + videoURL)
val item = resultsList!!.find { it?.url == videoURL }
Log.e(TAG, resultsList!![0].toString() + " " + videoURL)
val btn = recyclerView!!.findViewWithTag<MaterialButton>("""${item?.url}##$type""")
// if (downloading) {
// try {
// if (btn.getTag(R.id.cancelDownload) == "true") {
@ -394,20 +402,20 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
// } catch (ignored: Exception) {
// }
// }
// val sharedPreferences =
// context!!.getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
// if (sharedPreferences.getBoolean("download_card", true)) {
val sharedPreferences =
requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
if (sharedPreferences.getBoolean("download_card", true)) {
// selectedObjects!!.clear()
// selectedObjects!!.add(resultsList!![position])
// showConfigureDownloadCard(type)
// } else {
// selectedObjects!!.add(item!!)
showConfigureSingleDownloadCard(item!!, type)
} else {
// downloadQueue!!.add(vid)
// updateDownloadingStatusOnResult(vid, type, true)
// if (isStoragePermissionGranted) {
// mainActivity!!.startDownloadService(downloadQueue, listener)
// downloadQueue!!.clear()
// }
// }
}
}
@ -436,8 +444,8 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
// }
}
override fun onCardClick(position: Int, add: Boolean) {
val item = resultsList!![position]
override fun onCardClick(videoURL: String, add: Boolean) {
val item = resultsList?.find { it -> it?.url == videoURL }
if (add) selectedObjects!!.add(item!!) else selectedObjects!!.remove(item)
if (selectedObjects!!.size > 1) {
downloadAllFabCoordinator!!.visibility = GONE
@ -456,7 +464,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
} catch (e: Exception) {
""
}
if (!viewIdName.isEmpty()) {
if (viewIdName.isNotEmpty()) {
if (viewIdName.contains("audio") || viewIdName.contains("video")) {
val buttonData =
viewIdName.split("##".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
@ -465,39 +473,39 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
}
}
if (viewIdName == "downloadAll") {
//remove previously selected
for (i in selectedObjects!!.indices) {
val vid = findVideo(
selectedObjects!![i].url
)
homeAdapter!!.notifyItemChanged(resultsList!!.indexOf(vid))
}
selectedObjects = ArrayList()
downloadFabs!!.visibility = GONE
bottomSheet = BottomSheetDialog(fragmentContext!!)
bottomSheet!!.requestWindowFeature(Window.FEATURE_NO_TITLE)
bottomSheet!!.setContentView(R.layout.home_download_all_bottom_sheet)
val first = bottomSheet!!.findViewById<TextInputLayout>(R.id.first_textinput)
first!!.editText!!.setText(1.toString())
val last = bottomSheet!!.findViewById<TextInputLayout>(R.id.last_textinput)
last!!.editText!!.setText(resultsList!!.size.toString())
val audio = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_audio_button)
audio!!.setOnClickListener {
val start = first.editText!!.text.toString().toInt()
val end = last.editText!!.text.toString().toInt()
initDownloadAll(bottomSheet!!, start, end, "audio")
}
val video = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_video_button)
video!!.setOnClickListener {
val start = first.editText!!.text.toString().toInt()
val end = last.editText!!.text.toString().toInt()
initDownloadAll(bottomSheet!!, start, end, "video")
}
bottomSheet!!.show()
bottomSheet!!.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
// //remove previously selected
// for (i in selectedObjects!!.indices) {
// val vid = findVideo(
// selectedObjects!![i].url
// )
// homeAdapter!!.notifyItemChanged(resultsList!!.indexOf(vid))
// }
// selectedObjects = ArrayList()
// downloadFabs!!.visibility = GONE
// bottomSheet = BottomSheetDialog(fragmentContext!!)
// bottomSheet!!.requestWindowFeature(Window.FEATURE_NO_TITLE)
// bottomSheet!!.setContentView(R.layout.home_download_all_bottom_sheet)
// val first = bottomSheet!!.findViewById<TextInputLayout>(R.id.first_textinput)
// first!!.editText!!.setText(1.toString())
// val last = bottomSheet!!.findViewById<TextInputLayout>(R.id.last_textinput)
// last!!.editText!!.setText(resultsList!!.size.toString())
// val audio = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_audio_button)
// audio!!.setOnClickListener {
// val start = first.editText!!.text.toString().toInt()
// val end = last.editText!!.text.toString().toInt()
// initDownloadAll(bottomSheet!!, start, end, "audio")
// }
// val video = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_video_button)
// video!!.setOnClickListener {
// val start = first.editText!!.text.toString().toInt()
// val end = last.editText!!.text.toString().toInt()
// initDownloadAll(bottomSheet!!, start, end, "video")
// }
// bottomSheet!!.show()
// bottomSheet!!.window!!.setLayout(
// ViewGroup.LayoutParams.MATCH_PARENT,
// ViewGroup.LayoutParams.MATCH_PARENT
// )
}
}
}
@ -569,75 +577,122 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
// }
}
private fun showConfigureDownloadCard(type: String) {
// val sharedPreferences =
// context!!.getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
// val editor = sharedPreferences.edit()
// try {
// bottomSheet = BottomSheetDialog(fragmentContext!!)
// bottomSheet!!.requestWindowFeature(Window.FEATURE_NO_TITLE)
private fun showConfigureSingleDownloadCard(item: ResultItem, type: String) {
val sharedPreferences =
requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
val embedSubs = sharedPreferences.getBoolean("embed_subtitles", false)
val addChapters = sharedPreferences.getBoolean("add_chapters", false)
val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false)
var downloadItem = DownloadItem(
item.url,
item.title,
item.author,
item.thumb,
item.duration,
type,
"", "", "", "", 0, "", false,
"", item.website, "", item.playlistTitle, embedSubs, addChapters, saveThumb, "",
"", "", 0
)
val editor = sharedPreferences.edit()
try {
bottomSheet = BottomSheetDialog(fragmentContext!!)
bottomSheet!!.requestWindowFeature(Window.FEATURE_NO_TITLE)
bottomSheet!!.setContentView(R.layout.home_download_single_bottom_sheet)
val title = bottomSheet!!.findViewById<TextInputLayout>(R.id.title_textinput)
title!!.editText!!.setText(item.title)
title.editText!!.addTextChangedListener(object: TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun afterTextChanged(p0: Editable?) {
downloadItem.title = p0.toString()
item.title = p0.toString()
resultViewModel.update(item)
}
})
val author = bottomSheet!!.findViewById<TextInputLayout>(R.id.author_textinput)
author!!.editText!!.setText(item.author)
author.editText!!.addTextChangedListener(object: TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun afterTextChanged(p0: Editable?) {
downloadItem.author = p0.toString()
item.author = p0.toString()
resultViewModel.update(item)
}
})
var formats = resultViewModel.getFormats(item, type)
Log.e(TAG, formats.toString())
val formatTitles = formats.map {
if (it.format_note.contains("AUDIO_QUALITY_"))
it.format_note.replace("AUDIO_QUALITY_", "") +
if (it.filesize == 0L) "" else " / " + convertFileSize(it.filesize)
else it.format_note +
if (it.filesize == 0L) "" else " / " + convertFileSize(it.filesize)}
val format = bottomSheet!!.findViewById<TextInputLayout>(R.id.format)
val autoCompleteTextView = bottomSheet!!.findViewById<AutoCompleteTextView>(R.id.format_textview)
autoCompleteTextView?.setAdapter(ArrayAdapter(requireContext(), android.R.layout.simple_dropdown_item_1line, formatTitles))
autoCompleteTextView!!.setText(formatTitles[formats.lastIndex], false)
(format!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
when(type){
"audio" -> {
downloadItem.audioFormat = formats[index].format
downloadItem.audioFormatId = formats[index].format_id
}
"video" -> {
downloadItem.videoFormat = formats[index].format
downloadItem.videoFormatId = formats[index].format_id
}
"command" -> {
downloadItem.customTemplateId = formats[index].format_id.toInt()
}
}
}
lateinit var selectedContainer : String
val containers = when (type){
"audio" -> requireContext().resources.getStringArray(R.array.audio_containers)
"video" -> requireContext().resources.getStringArray(R.array.video_containers)
else -> null
}
val container = bottomSheet!!.findViewById<TextInputLayout>(R.id.downloadContainer)
val containerAutoCompleteTextView = bottomSheet!!.findViewById<AutoCompleteTextView>(R.id.container_textview)
if (containers == null){
containerAutoCompleteTextView!!.setText(getString(R.string.custom_command), false)
containerAutoCompleteTextView.isClickable = false
containerAutoCompleteTextView.isLongClickable = false
}else{
// val containerTitles = containers.map {
// if (it.format_note.contains("AUDIO_QUALITY_"))
// it.format_note.replace("AUDIO_QUALITY_", "") + " / " + convertFileSize(it.filesize)
// else it.format_note + " / " + convertFileSize(it.filesize) }
selectedContainer = when(type){
"audio" -> formats.find { downloadItem.audioFormat == it.format }?.ext ?: sharedPreferences.getString("audio_format", "mp3")!!
else -> formats.find { downloadItem.videoFormat == it.format }?.ext ?: sharedPreferences.getString("video_format", "DEFAULT")!!
}
containerAutoCompleteTextView!!.setText(selectedContainer, false)
(container!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
downloadItem.ext = containers[index]
}
}
// when(type) {
// "audio" -> {
//
// }
// }
// if (type == "audio") {
// bottomSheet!!.setContentView(R.layout.home_download_audio_bottom_sheet)
// val title = bottomSheet!!.findViewById<TextInputLayout>(R.id.title_textinput)
// if (selectedObjects!!.size > 1) {
// title!!.editText!!.setText(getString(R.string.mutliple_titles))
// title.editText!!.isClickable = false
// title.editText!!.isLongClickable = false
// } else {
// title!!.editText!!.setText(selectedObjects!![0]!!.title)
// title.editText!!.addTextChangedListener(object : TextWatcher {
// override fun beforeTextChanged(
// charSequence: CharSequence,
// i: Int,
// i1: Int,
// i2: Int
// ) {
// }
//
// override fun onTextChanged(
// charSequence: CharSequence,
// i: Int,
// i1: Int,
// i2: Int
// ) {
// }
//
// override fun afterTextChanged(editable: Editable) {
// val index = resultsList!!.indexOf(selectedObjects!![0])
// resultsList!![index]!!.title = editable.toString()
// }
// })
// }
// val author = bottomSheet!!.findViewById<TextInputLayout>(R.id.author_textinput)
// if (selectedObjects!!.size > 1) {
// author!!.editText!!.setText(getString(R.string.mutliple_authors))
// author.editText!!.isClickable = false
// author.editText!!.isLongClickable = false
// } else {
// author!!.editText!!.setText(selectedObjects!![0]!!.author)
// author.editText!!.addTextChangedListener(object : TextWatcher {
// override fun beforeTextChanged(
// charSequence: CharSequence,
// i: Int,
// i1: Int,
// i2: Int
// ) {
// }
//
// override fun onTextChanged(
// charSequence: CharSequence,
// i: Int,
// i1: Int,
// i2: Int
// ) {
// }
//
// override fun afterTextChanged(editable: Editable) {
// val index = resultsList!!.indexOf(selectedObjects!![0])
// resultsList!![index]!!.author = editable.toString()
// }
// })
// }
// val audioFormats = context!!.resources.getStringArray(R.array.music_formats)
// val audioFormat = bottomSheet!!.findViewById<TextInputLayout>(R.id.audio_format)
// val autoCompleteTextView =
@ -687,8 +742,8 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
// }
// })
// }
// val videoFormats = context!!.resources.getStringArray(R.array.video_formats)
// val videoQualities = context!!.resources.getStringArray(R.array.video_quality)
// val videoFormats = context!!.resources.getStringArray(R.array.video_containers)
// val videoQualities = context!!.resources.getStringArray(R.array.video_formats)
// val videoFormat = bottomSheet!!.findViewById<TextInputLayout>(R.id.video_format)
// var autoCompleteTextView =
// bottomSheet!!.findViewById<AutoCompleteTextView>(R.id.video_format_textview)
@ -773,14 +828,21 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
// }
// bottomSheet!!.cancel()
// }
// bottomSheet!!.show()
// bottomSheet!!.window!!.setLayout(
// ViewGroup.LayoutParams.MATCH_PARENT,
// ViewGroup.LayoutParams.MATCH_PARENT
// )
// } catch (e: Exception) {
// Toast.makeText(fragmentContext, e.message, Toast.LENGTH_LONG).show()
// }
bottomSheet!!.show()
bottomSheet!!.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
} catch (e: Exception) {
Log.e(TAG, e.printStackTrace().toString());
}
}
private fun convertFileSize(s: Long): String{
if (s <= 0) return "0"
val units = arrayOf("B", "kB", "MB", "GB", "TB")
val digitGroups = (log10(s.toDouble()) / log10(1024.0)).toInt()
return DecimalFormat("#,##0.#").format(s / 1024.0.pow(digitGroups.toDouble())) + " " + units[digitGroups]
}
companion object {

View file

@ -59,8 +59,8 @@ class DownloadWorker(
val tempFolder = StringBuilder(context.cacheDir.absolutePath + """/${downloadItem.title}##${downloadItem.type}""")
when(type){
"audio" -> tempFolder.append("##${downloadItem.audioQualityId}")
"video" -> tempFolder.append("##${downloadItem.videoQualityId}")
"audio" -> tempFolder.append("##${downloadItem.audioFormatId}")
"video" -> tempFolder.append("##${downloadItem.videoFormatId}")
"command" -> tempFolder.append("##${downloadItem.customTemplateId}")
}
var tempFileDir = File(tempFolder.toString())
@ -95,7 +95,7 @@ class DownloadWorker(
when(type){
"audio" -> {
request.addOption("-x")
var audioQualityId : String = downloadItem.audioQualityId
var audioQualityId : String = downloadItem.audioFormatId
if (audioQualityId == "0") audioQualityId = "ba"
var format = downloadItem.audioFormat
if (format.isEmpty()){
@ -110,7 +110,7 @@ class DownloadWorker(
request.addOption("--embed-thumbnail")
request.addOption("--convert-thumbnails", "png")
try {
val config = File(context.cacheDir, "config" + downloadItem.title + "##" + downloadItem.audioQualityId + ".txt")
val config = File(context.cacheDir, "config" + downloadItem.title + "##" + downloadItem.audioFormatId + ".txt")
val configData =
"--ppa \"ffmpeg: -c:v png -vf crop=\\\"'if(gt(ih,iw),iw,ih)':'if(gt(iw,ih),ih,iw)'\\\"\""
config.writeText(configData)
@ -142,8 +142,8 @@ class DownloadWorker(
if (embedSubs) {
request.addOption("--embed-subs", "")
}
var videoQualityId = downloadItem.videoQualityId
val audioQualityId = downloadItem.audioQualityId
var videoQualityId = downloadItem.videoFormatId
val audioQualityId = downloadItem.audioFormatId
if (videoQualityId.isEmpty()) videoQualityId = "bestvideo"
val formatArgument = StringBuilder(videoQualityId)
if (videoQualityId != "worst"){

View file

@ -9,76 +9,86 @@
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/title_textinput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
app:errorEnabled="true"
android:hint="@string/title"
style="@style/Widget.Material3.TextInputLayout.FilledBox">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:inputType="text"
android:layout_height="wrap_content"
/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/author_textinput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:errorEnabled="true"
android:hint="@string/author"
style="@style/Widget.Material3.TextInputLayout.FilledBox">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:inputType="text"
android:layout_height="wrap_content"
/>
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
android:paddingBottom="15dp"
android:orientation="horizontal">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/title_textinput"
android:layout_width="match_parent"
android:id="@+id/downloadContainer"
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_weight="30"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
app:errorEnabled="true"
android:hint="@string/title"
style="@style/Widget.Material3.TextInputLayout.FilledBox">
android:hint="@string/container">
<com.google.android.material.textfield.TextInputEditText
<AutoCompleteTextView
android:id="@+id/container_textview"
android:layout_width="match_parent"
android:inputType="text"
android:layout_height="wrap_content"
android:inputType="none"
app:simpleItems="@array/audio_containers"
/>
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/format"
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:paddingStart="10dp"
android:layout_weight="65"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:hint="@string/format">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/author_textinput"
android:layout_width="0dp"
android:layout_weight="45"
<AutoCompleteTextView
android:id="@+id/format_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:errorEnabled="true"
android:hint="@string/author"
style="@style/Widget.Material3.TextInputLayout.FilledBox">
android:inputType="none"
app:simpleItems="@array/audio_formats"
/>
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:inputType="text"
android:layout_height="wrap_content"
/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/audio_format"
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:paddingStart="10dp"
android:layout_weight="45"
android:layout_height="wrap_content"
android:hint="@string/audio_format">
<AutoCompleteTextView
android:id="@+id/audio_format_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"
app:simpleItems="@array/music_formats"
/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/outputPath_textinput"
android:layout_width="match_parent"

View file

@ -31,34 +31,51 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:gravity="end"
android:paddingTop="10dp"
android:orientation="horizontal">
<Button
style="?attr/materialIconButtonOutlinedStyle"
android:id="@+id/bottomsheet_cancel_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
app:icon="@drawable/ic_cancel" />
<Button
style="@style/Widget.Material3.Button.ElevatedButton.Icon"
android:textSize="11sp"
android:id="@+id/bottomsheet_schedule_button"
android:padding="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
app:icon="@drawable/ic_clock"
android:text="@string/schedule" />
<Button
style="@style/Widget.Material3.Button.ElevatedButton.Icon"
android:id="@+id/bottomsheet_download_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/download"
app:icon="@drawable/ic_down"
android:autoLink="all"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:gravity="end"
android:orientation="horizontal">
<Button
style="@style/Widget.Material3.Button.OutlinedButton.Icon"
android:textSize="11sp"
android:padding="10dp"
android:id="@+id/bottomsheet_cancel_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cancel"
android:layout_marginEnd="10dp"
app:icon="@drawable/ic_cancel" />
<Button
style="@style/Widget.Material3.Button.ElevatedButton.Icon"
android:textSize="11sp"
android:padding="10dp"
android:id="@+id/bottomsheet_download_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/download"
app:icon="@drawable/ic_down"
android:autoLink="all"/>
</LinearLayout>
</LinearLayout>

View file

@ -1,5 +1,5 @@
<resources>
<string-array name="music_formats">
<string-array name="audio_containers">
<item>mp3</item>
<item>m4a</item>
<item>aac</item>
@ -9,22 +9,27 @@
<item>wav</item>
</string-array>
<string-array name="video_formats">
<item>DEFAULT</item>
<string-array name="audio_formats">
<item>@string/worst</item>
<item>@string/best</item>
</string-array>
<string-array name="video_containers">
<item>@string/defaultValue</item>
<item>mp4</item>
<item>mkv</item>
<item>webm</item>
</string-array>
<string-array name="video_quality">
<item>Best Quality</item>
<item>2160p</item>
<item>1440p</item>
<item>1080p</item>
<item>720p</item>
<item>480p</item>
<item>360p</item>
<string-array name="video_formats">
<item>Worst Quality</item>
<item>360p</item>
<item>480p</item>
<item>720p</item>
<item>1080p</item>
<item>1440p</item>
<item>2160p</item>
<item>Best Quality</item>
</string-array>
<string-array name="sponsorblock_settings_entries">

View file

@ -160,4 +160,9 @@
<string name="schedule">Schedule</string>
<string name="concurrent_downloads">Concurrent Downloads</string>
<string name="concurrent_downloads_summary">Number of downloads executing at the same time</string>
<string name="best">Best</string>
<string name="worst">Worst</string>
<string name="defaultValue">Default</string>
<string name="format">Format</string>
<string name="container">Container</string>
</resources>

View file

@ -125,8 +125,8 @@
<ListPreference
android:defaultValue="mp3"
android:entries="@array/music_formats"
android:entryValues="@array/music_formats"
android:entries="@array/audio_containers"
android:entryValues="@array/audio_containers"
android:icon="@drawable/ic_code"
app:key="audio_format"
app:summary="mp3"
@ -134,8 +134,8 @@
<ListPreference
android:defaultValue="mkv"
android:entries="@array/video_formats"
android:entryValues="@array/video_formats"
android:entries="@array/video_containers"
android:entryValues="@array/video_containers"
android:icon="@drawable/ic_code"
app:key="video_format"
app:summary="DEFAULT"
@ -154,8 +154,8 @@
<ListPreference
android:defaultValue="Best Quality"
android:entries="@array/video_quality"
android:entryValues="@array/video_quality"
android:entries="@array/video_formats"
android:entryValues="@array/video_formats"
android:icon="@drawable/ic_video"
app:key="video_quality"
app:summary="Best Quality"