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> </value>
</option> </option>
</component> </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" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

View file

@ -2,7 +2,7 @@
"formatVersion": 1, "formatVersion": 1,
"database": { "database": {
"version": 1, "version": 1,
"identityHash": "5939be553c41327f4f64e1d62d008968", "identityHash": "c8effa77edcd8cf5e46a79fd1a791535",
"entities": [ "entities": [
{ {
"tableName": "results", "tableName": "results",
@ -198,7 +198,7 @@
}, },
{ {
"tableName": "downloads", "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": [ "fields": [
{ {
"fieldPath": "url", "fieldPath": "url",
@ -236,24 +236,6 @@
"affinity": "TEXT", "affinity": "TEXT",
"notNull": true "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", "fieldPath": "audioFormat",
"columnName": "audioFormat", "columnName": "audioFormat",
@ -266,6 +248,24 @@
"affinity": "TEXT", "affinity": "TEXT",
"notNull": true "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", "fieldPath": "formatDesc",
"columnName": "formatDesc", "columnName": "formatDesc",
@ -321,6 +321,18 @@
"affinity": "INTEGER", "affinity": "INTEGER",
"notNull": true "notNull": true
}, },
{
"fieldPath": "ext",
"columnName": "ext",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "filesize",
"columnName": "filesize",
"affinity": "TEXT",
"notNull": true
},
{ {
"fieldPath": "status", "fieldPath": "status",
"columnName": "status", "columnName": "status",
@ -386,7 +398,7 @@
"views": [], "views": [],
"setupQueries": [ "setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", "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"?> <?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.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
@ -77,6 +78,17 @@
android:name="android.support.FILE_PROVIDER_PATHS" android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" /> android:resource="@xml/provider_paths" />
</provider> </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> </application>
</manifest> </manifest>

View file

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

View file

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

View file

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

View file

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

View file

@ -3,13 +3,15 @@ package com.deniscerri.ytdlnis.database.repository
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import com.deniscerri.ytdlnis.database.dao.CommandTemplateDao
import com.deniscerri.ytdlnis.database.dao.FormatDao import com.deniscerri.ytdlnis.database.dao.FormatDao
import com.deniscerri.ytdlnis.database.dao.ResultDao 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.Format
import com.deniscerri.ytdlnis.database.models.ResultItem import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.util.InfoUtil 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" private val tag: String = "ResultRepository"
val allResults : LiveData<List<ResultItem>> = resultDao.getResults() val allResults : LiveData<List<ResultItem>> = resultDao.getResults()
@ -119,5 +121,12 @@ class ResultRepository(private val resultDao: ResultDao, private val formatDao:
resultDao.update(item) 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 package com.deniscerri.ytdlnis.database.viewmodel
import android.app.Application import android.app.Application
import android.content.Context
import android.util.Log import android.util.Log
import androidx.lifecycle.* import androidx.lifecycle.*
import com.deniscerri.ytdlnis.App
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.DBManager 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.models.ResultItem
import com.deniscerri.ytdlnis.database.repository.ResultRepository import com.deniscerri.ytdlnis.database.repository.ResultRepository
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.regex.Pattern import java.util.regex.Pattern
import kotlin.coroutines.coroutineContext
class ResultViewModel(application: Application) : AndroidViewModel(application) { class ResultViewModel(application: Application) : AndroidViewModel(application) {
private val tag: String = "ResultViewModel" private val tag: String = "ResultViewModel"
@ -19,7 +25,8 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
init { init {
val dao = DBManager.getInstance(application).resultDao val dao = DBManager.getInstance(application).resultDao
val formatDao = DBManager.getInstance(application).formatDao 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 items = repository.allResults
loadingItems.postValue(false) loadingItems.postValue(false)
} }
@ -86,4 +93,31 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
fun update(item: ResultItem) = viewModelScope.launch(Dispatchers.IO){ fun update(item: ResultItem) = viewModelScope.launch(Dispatchers.IO){
repository.update(item); 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 =
HistoryAdapter( HistoryAdapter(
this, this,
activity requireActivity()
) )
recyclerView = view.findViewById(R.id.recyclerviewdownloadss) recyclerView = view.findViewById(R.id.recyclerviewdownloadss)
recyclerView?.layoutManager = LinearLayoutManager(context) recyclerView?.layoutManager = LinearLayoutManager(context)

View file

@ -7,13 +7,17 @@ import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.text.Editable
import android.text.InputType import android.text.InputType
import android.text.TextWatcher
import android.util.Log
import android.view.* import android.view.*
import android.view.View.GONE import android.view.View.GONE
import android.view.View.VISIBLE import android.view.View.VISIBLE
import android.widget.* import android.widget.*
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.text.trimmedLength
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
@ -22,6 +26,7 @@ import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.adapter.HomeAdapter import com.deniscerri.ytdlnis.adapter.HomeAdapter
import com.deniscerri.ytdlnis.database.DatabaseManager 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.models.ResultItem
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.databinding.FragmentDownloadsBinding 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.floatingactionbutton.FloatingActionButton
import com.google.android.material.progressindicator.LinearProgressIndicator import com.google.android.material.progressindicator.LinearProgressIndicator
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import kotlinx.coroutines.runBlocking
import java.text.DecimalFormat
import java.util.* import java.util.*
import kotlin.math.log10
import kotlin.math.pow
class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickListener { class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickListener {
private var progressBar: LinearProgressIndicator? = null private var progressBar: LinearProgressIndicator? = null
@ -121,7 +130,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
homeAdapter = homeAdapter =
HomeAdapter( HomeAdapter(
this, this,
activity requireActivity()
) )
recyclerView = view.findViewById(R.id.recyclerViewHome) recyclerView = view.findViewById(R.id.recyclerViewHome)
recyclerView?.layoutManager = LinearLayoutManager(context) recyclerView?.layoutManager = LinearLayoutManager(context)
@ -336,11 +345,10 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
selectedObjects = ArrayList() selectedObjects = ArrayList()
downloadAllFabCoordinator!!.visibility = GONE downloadAllFabCoordinator!!.visibility = GONE
downloadFabs!!.visibility = GONE downloadFabs!!.visibility = GONE
} } else if (itemId == R.id.cancel_download) {
// } else if (itemId == R.id.cancel_download) { try {
// try { mainActivity!!.cancelAllDownloads()
// mainActivity!!.cancelDownloadService() topAppBar!!.menu.findItem(itemId).isVisible = false
// topAppBar!!.menu.findItem(itemId).isVisible = false
// for (i in downloadInfo!!.downloadQueue.indices) { // for (i in downloadInfo!!.downloadQueue.indices) {
// val vid = downloadInfo!!.downloadQueue[i] // val vid = downloadInfo!!.downloadQueue[i]
// val type = vid.downloadedType // val type = vid.downloadedType
@ -348,9 +356,9 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
// } // }
// downloadQueue = ArrayList() // downloadQueue = ArrayList()
// downloading = false // downloading = false
// } catch (ignored: Exception) { } catch (ignored: Exception) {
// } }
// } }
true true
} }
} }
@ -380,11 +388,11 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
} }
@SuppressLint("ResourceType") @SuppressLint("ResourceType")
override fun onButtonClick(position: Int, type: String) { override fun onButtonClick(videoURL: String, type: String?) {
// Log.e(TAG, type) Log.e(TAG, type!! + " " + videoURL)
// val vid = resultsList!![position] val item = resultsList!!.find { it?.url == videoURL }
// vid!!.downloadedType = type Log.e(TAG, resultsList!![0].toString() + " " + videoURL)
// val btn = recyclerView!!.findViewWithTag<MaterialButton>(vid.videoId + "##" + type) val btn = recyclerView!!.findViewWithTag<MaterialButton>("""${item?.url}##$type""")
// if (downloading) { // if (downloading) {
// try { // try {
// if (btn.getTag(R.id.cancelDownload) == "true") { // if (btn.getTag(R.id.cancelDownload) == "true") {
@ -394,20 +402,20 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
// } catch (ignored: Exception) { // } catch (ignored: Exception) {
// } // }
// } // }
// val sharedPreferences = val sharedPreferences =
// context!!.getSharedPreferences("root_preferences", Activity.MODE_PRIVATE) requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
// if (sharedPreferences.getBoolean("download_card", true)) { if (sharedPreferences.getBoolean("download_card", true)) {
// selectedObjects!!.clear() // selectedObjects!!.clear()
// selectedObjects!!.add(resultsList!![position]) // selectedObjects!!.add(item!!)
// showConfigureDownloadCard(type) showConfigureSingleDownloadCard(item!!, type)
// } else { } else {
// downloadQueue!!.add(vid) // downloadQueue!!.add(vid)
// updateDownloadingStatusOnResult(vid, type, true) // updateDownloadingStatusOnResult(vid, type, true)
// if (isStoragePermissionGranted) { // if (isStoragePermissionGranted) {
// mainActivity!!.startDownloadService(downloadQueue, listener) // mainActivity!!.startDownloadService(downloadQueue, listener)
// downloadQueue!!.clear() // downloadQueue!!.clear()
// } // }
// } }
} }
@ -436,8 +444,8 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
// } // }
} }
override fun onCardClick(position: Int, add: Boolean) { override fun onCardClick(videoURL: String, add: Boolean) {
val item = resultsList!![position] val item = resultsList?.find { it -> it?.url == videoURL }
if (add) selectedObjects!!.add(item!!) else selectedObjects!!.remove(item) if (add) selectedObjects!!.add(item!!) else selectedObjects!!.remove(item)
if (selectedObjects!!.size > 1) { if (selectedObjects!!.size > 1) {
downloadAllFabCoordinator!!.visibility = GONE downloadAllFabCoordinator!!.visibility = GONE
@ -456,7 +464,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
} catch (e: Exception) { } catch (e: Exception) {
"" ""
} }
if (!viewIdName.isEmpty()) { if (viewIdName.isNotEmpty()) {
if (viewIdName.contains("audio") || viewIdName.contains("video")) { if (viewIdName.contains("audio") || viewIdName.contains("video")) {
val buttonData = val buttonData =
viewIdName.split("##".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() viewIdName.split("##".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
@ -465,39 +473,39 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
} }
} }
if (viewIdName == "downloadAll") { if (viewIdName == "downloadAll") {
//remove previously selected // //remove previously selected
for (i in selectedObjects!!.indices) { // for (i in selectedObjects!!.indices) {
val vid = findVideo( // val vid = findVideo(
selectedObjects!![i].url // selectedObjects!![i].url
) // )
homeAdapter!!.notifyItemChanged(resultsList!!.indexOf(vid)) // homeAdapter!!.notifyItemChanged(resultsList!!.indexOf(vid))
} // }
selectedObjects = ArrayList() // selectedObjects = ArrayList()
downloadFabs!!.visibility = GONE // downloadFabs!!.visibility = GONE
bottomSheet = BottomSheetDialog(fragmentContext!!) // bottomSheet = BottomSheetDialog(fragmentContext!!)
bottomSheet!!.requestWindowFeature(Window.FEATURE_NO_TITLE) // bottomSheet!!.requestWindowFeature(Window.FEATURE_NO_TITLE)
bottomSheet!!.setContentView(R.layout.home_download_all_bottom_sheet) // bottomSheet!!.setContentView(R.layout.home_download_all_bottom_sheet)
val first = bottomSheet!!.findViewById<TextInputLayout>(R.id.first_textinput) // val first = bottomSheet!!.findViewById<TextInputLayout>(R.id.first_textinput)
first!!.editText!!.setText(1.toString()) // first!!.editText!!.setText(1.toString())
val last = bottomSheet!!.findViewById<TextInputLayout>(R.id.last_textinput) // val last = bottomSheet!!.findViewById<TextInputLayout>(R.id.last_textinput)
last!!.editText!!.setText(resultsList!!.size.toString()) // last!!.editText!!.setText(resultsList!!.size.toString())
val audio = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_audio_button) // val audio = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_audio_button)
audio!!.setOnClickListener { // audio!!.setOnClickListener {
val start = first.editText!!.text.toString().toInt() // val start = first.editText!!.text.toString().toInt()
val end = last.editText!!.text.toString().toInt() // val end = last.editText!!.text.toString().toInt()
initDownloadAll(bottomSheet!!, start, end, "audio") // initDownloadAll(bottomSheet!!, start, end, "audio")
} // }
val video = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_video_button) // val video = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_video_button)
video!!.setOnClickListener { // video!!.setOnClickListener {
val start = first.editText!!.text.toString().toInt() // val start = first.editText!!.text.toString().toInt()
val end = last.editText!!.text.toString().toInt() // val end = last.editText!!.text.toString().toInt()
initDownloadAll(bottomSheet!!, start, end, "video") // initDownloadAll(bottomSheet!!, start, end, "video")
} // }
bottomSheet!!.show() // bottomSheet!!.show()
bottomSheet!!.window!!.setLayout( // bottomSheet!!.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT, // ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT // ViewGroup.LayoutParams.MATCH_PARENT
) // )
} }
} }
} }
@ -569,75 +577,122 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
// } // }
} }
private fun showConfigureDownloadCard(type: String) { private fun showConfigureSingleDownloadCard(item: ResultItem, type: String) {
// val sharedPreferences = val sharedPreferences =
// context!!.getSharedPreferences("root_preferences", Activity.MODE_PRIVATE) requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
// val editor = sharedPreferences.edit() val embedSubs = sharedPreferences.getBoolean("embed_subtitles", false)
// try { val addChapters = sharedPreferences.getBoolean("add_chapters", false)
// bottomSheet = BottomSheetDialog(fragmentContext!!) val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false)
// bottomSheet!!.requestWindowFeature(Window.FEATURE_NO_TITLE)
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") { // 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 audioFormats = context!!.resources.getStringArray(R.array.music_formats)
// val audioFormat = bottomSheet!!.findViewById<TextInputLayout>(R.id.audio_format) // val audioFormat = bottomSheet!!.findViewById<TextInputLayout>(R.id.audio_format)
// val autoCompleteTextView = // val autoCompleteTextView =
@ -687,8 +742,8 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
// } // }
// }) // })
// } // }
// val videoFormats = context!!.resources.getStringArray(R.array.video_formats) // val videoFormats = context!!.resources.getStringArray(R.array.video_containers)
// val videoQualities = context!!.resources.getStringArray(R.array.video_quality) // val videoQualities = context!!.resources.getStringArray(R.array.video_formats)
// val videoFormat = bottomSheet!!.findViewById<TextInputLayout>(R.id.video_format) // val videoFormat = bottomSheet!!.findViewById<TextInputLayout>(R.id.video_format)
// var autoCompleteTextView = // var autoCompleteTextView =
// bottomSheet!!.findViewById<AutoCompleteTextView>(R.id.video_format_textview) // bottomSheet!!.findViewById<AutoCompleteTextView>(R.id.video_format_textview)
@ -773,14 +828,21 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
// } // }
// bottomSheet!!.cancel() // bottomSheet!!.cancel()
// } // }
// bottomSheet!!.show() bottomSheet!!.show()
// bottomSheet!!.window!!.setLayout( bottomSheet!!.window!!.setLayout(
// ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
// ViewGroup.LayoutParams.MATCH_PARENT ViewGroup.LayoutParams.MATCH_PARENT
// ) )
// } catch (e: Exception) { } catch (e: Exception) {
// Toast.makeText(fragmentContext, e.message, Toast.LENGTH_LONG).show() 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 { companion object {

View file

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

View file

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

View file

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

View file

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

View file

@ -160,4 +160,9 @@
<string name="schedule">Schedule</string> <string name="schedule">Schedule</string>
<string name="concurrent_downloads">Concurrent Downloads</string> <string name="concurrent_downloads">Concurrent Downloads</string>
<string name="concurrent_downloads_summary">Number of downloads executing at the same time</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> </resources>

View file

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