implemented cookies import & invert selected in contextual app bar & download preference without metered networks
This commit is contained in:
parent
f6e5f771f6
commit
55b59d75b1
38 changed files with 521 additions and 305 deletions
|
|
@ -13,6 +13,8 @@
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
<uses-permission android:name="android.permission.ACTION_OPEN_DOCUMENT" />
|
<uses-permission android:name="android.permission.ACTION_OPEN_DOCUMENT" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".App"
|
android:name=".App"
|
||||||
|
|
|
||||||
|
|
@ -101,11 +101,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cookieViewModel.updateCookiesFile()
|
||||||
cookieViewModel.getCookiesFromDB().getOrNull()?.let{
|
|
||||||
File(cacheDir, "cookies.txt").apply { writeText(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
val intent = intent
|
val intent = intent
|
||||||
handleIntents(intent)
|
handleIntents(intent)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ class CookieAdapter(onItemClickListener: OnItemClickListener, activity: Activity
|
||||||
title.text = item?.url
|
title.text = item?.url
|
||||||
|
|
||||||
val content = card.findViewById<TextView>(R.id.content)
|
val content = card.findViewById<TextView>(R.id.content)
|
||||||
content.visibility = View.GONE
|
content.text = item?.content
|
||||||
|
|
||||||
card.setOnClickListener {
|
card.setOnClickListener {
|
||||||
onItemClickListener.onItemClick(item!!)
|
onItemClickListener.onItemClick(item!!)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import androidx.recyclerview.widget.ListAdapter
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.models.HistoryItem
|
import com.deniscerri.ytdlnis.database.models.HistoryItem
|
||||||
|
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
import com.google.android.material.card.MaterialCardView
|
import com.google.android.material.card.MaterialCardView
|
||||||
|
|
@ -172,6 +173,16 @@ class HistoryAdapter(onItemClickListener: OnItemClickListener, activity: Activit
|
||||||
checkedItems.clear()
|
checkedItems.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun invertSelected(items: List<HistoryItem?>?){
|
||||||
|
val invertedList = mutableListOf<Long>()
|
||||||
|
items?.forEach {
|
||||||
|
if (!checkedItems.contains(it!!.id)) invertedList.add(it.id)
|
||||||
|
}
|
||||||
|
checkedItems.clear()
|
||||||
|
checkedItems.addAll(invertedList)
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val DIFF_CALLBACK: DiffUtil.ItemCallback<HistoryItem> = object : DiffUtil.ItemCallback<HistoryItem>() {
|
private val DIFF_CALLBACK: DiffUtil.ItemCallback<HistoryItem> = object : DiffUtil.ItemCallback<HistoryItem>() {
|
||||||
override fun areItemsTheSame(oldItem: HistoryItem, newItem: HistoryItem): Boolean {
|
override fun areItemsTheSame(oldItem: HistoryItem, newItem: HistoryItem): Boolean {
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,16 @@ class HomeAdapter(onItemClickListener: OnItemClickListener, activity: Activity)
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun invertSelected(items: List<ResultItem?>?){
|
||||||
|
val invertedList = mutableListOf<String>()
|
||||||
|
items?.forEach {
|
||||||
|
if (!checkedVideos.contains(it!!.url)) invertedList.add(it.url)
|
||||||
|
}
|
||||||
|
checkedVideos.clear()
|
||||||
|
checkedVideos.addAll(invertedList)
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
fun clearCheckedItems(){
|
fun clearCheckedItems(){
|
||||||
checkedVideos.clear()
|
checkedVideos.clear()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ class TemplatesAdapter(onItemClickListener: OnItemClickListener, activity: Activ
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun areContentsTheSame(oldItem: CommandTemplate, newItem: CommandTemplate): Boolean {
|
override fun areContentsTheSame(oldItem: CommandTemplate, newItem: CommandTemplate): Boolean {
|
||||||
return oldItem.content == newItem.content
|
return oldItem.title == newItem.title && oldItem.content == newItem.content
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,11 @@ interface CookieDao {
|
||||||
@Query("SELECT * FROM cookies ORDER BY id DESC")
|
@Query("SELECT * FROM cookies ORDER BY id DESC")
|
||||||
fun getAllCookiesLiveData() : LiveData<List<CookieItem>>
|
fun getAllCookiesLiveData() : LiveData<List<CookieItem>>
|
||||||
|
|
||||||
@Query("SELECT * FROM cookies WHERE url=:url")
|
@Query("SELECT EXISTS(SELECT * FROM cookies WHERE url=:url LIMIT 1)")
|
||||||
fun checkIfExistsWithSameURL(url: String) : CookieItem
|
fun checkIfExistsWithSameURL(url: String) : Boolean
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
suspend fun insert(item: CookieItem)
|
suspend fun insert(item: CookieItem) : Long
|
||||||
|
|
||||||
@Query("DELETE FROM cookies")
|
@Query("DELETE FROM cookies")
|
||||||
suspend fun deleteAll()
|
suspend fun deleteAll()
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,11 @@ class CookieRepository(private val cookieDao: CookieDao) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
suspend fun insert(item: CookieItem){
|
suspend fun insert(item: CookieItem) : Long{
|
||||||
try{
|
if (! cookieDao.checkIfExistsWithSameURL(item.url)){
|
||||||
cookieDao.checkIfExistsWithSameURL(item.url)
|
return cookieDao.insert(item)
|
||||||
}catch(e: Exception){
|
|
||||||
cookieDao.insert(item)
|
|
||||||
}
|
}
|
||||||
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun delete(item: CookieItem){
|
suspend fun delete(item: CookieItem){
|
||||||
|
|
|
||||||
|
|
@ -6,26 +6,27 @@ import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.sqlite.SQLiteDatabase
|
import android.database.sqlite.SQLiteDatabase
|
||||||
import android.database.sqlite.SQLiteDatabase.OPEN_READONLY
|
import android.database.sqlite.SQLiteDatabase.OPEN_READONLY
|
||||||
|
import android.util.Log
|
||||||
import android.webkit.CookieManager
|
import android.webkit.CookieManager
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.deniscerri.ytdlnis.database.DBManager
|
import com.deniscerri.ytdlnis.database.DBManager
|
||||||
import com.deniscerri.ytdlnis.database.models.CommandTemplateExport
|
|
||||||
import com.deniscerri.ytdlnis.database.models.CookieItem
|
import com.deniscerri.ytdlnis.database.models.CookieItem
|
||||||
import com.deniscerri.ytdlnis.database.repository.CookieRepository
|
import com.deniscerri.ytdlnis.database.repository.CookieRepository
|
||||||
import com.deniscerri.ytdlnis.ui.more.WebViewActivity
|
import com.deniscerri.ytdlnis.ui.more.WebViewActivity
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import kotlinx.serialization.encodeToString
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class CookieViewModel(private val application: Application) : AndroidViewModel(application) {
|
class CookieViewModel(private val application: Application) : AndroidViewModel(application) {
|
||||||
private val repository: CookieRepository
|
private val repository: CookieRepository
|
||||||
val items: LiveData<List<CookieItem>>
|
val items: LiveData<List<CookieItem>>
|
||||||
private val jsonFormat = Json { prettyPrint = true }
|
private val jsonFormat = Json { prettyPrint = true }
|
||||||
|
|
||||||
|
private val cookieHeader = "# Netscape HTTP Cookie File\n" +
|
||||||
|
"# WebView Generated by the YTDLnis app\n"
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val dao = DBManager.getInstance(application).cookieDao
|
val dao = DBManager.getInstance(application).cookieDao
|
||||||
|
|
@ -37,12 +38,13 @@ class CookieViewModel(private val application: Application) : AndroidViewModel(a
|
||||||
return repository.getAll()
|
return repository.getAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun insert(item: CookieItem) = viewModelScope.launch(Dispatchers.IO) {
|
suspend fun insert(item: CookieItem) : Long {
|
||||||
repository.insert(item)
|
return repository.insert(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun delete(item: CookieItem) = viewModelScope.launch(Dispatchers.IO) {
|
fun delete(item: CookieItem) = viewModelScope.launch(Dispatchers.IO) {
|
||||||
repository.delete(item)
|
repository.delete(item)
|
||||||
|
updateCookiesFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteAll() = viewModelScope.launch(Dispatchers.IO) {
|
fun deleteAll() = viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
|
@ -106,20 +108,53 @@ class CookieViewModel(private val application: Application) : AndroidViewModel(a
|
||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
close()
|
close()
|
||||||
val header = "# Netscape HTTP Cookie File\n" +
|
cookieList.fold(StringBuilder("")) { acc, cookie ->
|
||||||
"# WebView Generated by the YTDLnis app\n"
|
|
||||||
cookieList.fold(StringBuilder(header)) { acc, cookie ->
|
|
||||||
acc.append(cookie.toNetscapeFormat()).append("\n")
|
acc.append(cookie.toNetscapeFormat()).append("\n")
|
||||||
}.toString()
|
}.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateCookiesFile() = viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
val cookies = repository.getAll()
|
||||||
|
val cookieTXT = StringBuilder(cookieHeader)
|
||||||
|
val cookieFile = File(application.cacheDir, "cookies.txt")
|
||||||
|
if (cookies.isEmpty()) cookieFile.apply { writeText("") }
|
||||||
|
cookies.forEach {
|
||||||
|
it.content.lines().forEach {line ->
|
||||||
|
if (! cookieTXT.contains(line)) cookieTXT.append(it.content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cookieFile.apply { writeText(cookieTXT.toString()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun importFromClipboard() {
|
||||||
|
try{
|
||||||
|
val clipboard: ClipboardManager =
|
||||||
|
application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
|
var clip = clipboard.primaryClip!!.getItemAt(0).text
|
||||||
|
Log.e("Aaa", clip.toString())
|
||||||
|
if (clip.startsWith(cookieHeader)){
|
||||||
|
clip = clip.removePrefix(cookieHeader)
|
||||||
|
val cookie = CookieItem(
|
||||||
|
0,
|
||||||
|
"Cookie Import [${System.currentTimeMillis()}]",
|
||||||
|
clip.toString()
|
||||||
|
)
|
||||||
|
insert(cookie)
|
||||||
|
updateCookiesFile()
|
||||||
|
}
|
||||||
|
}catch (e: Exception){
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun exportToClipboard() = viewModelScope.launch {
|
fun exportToClipboard() = viewModelScope.launch {
|
||||||
try{
|
try{
|
||||||
val clipboard: ClipboardManager =
|
val clipboard: ClipboardManager =
|
||||||
application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
|
val cookieFile = File(application.cacheDir, "cookies.txt")
|
||||||
getCookiesFromDB().getOrNull()?.let {
|
if (! cookieFile.exists()) updateCookiesFile()
|
||||||
|
cookieFile.readText().let {
|
||||||
clipboard.setText(it)
|
clipboard.setText(it)
|
||||||
}
|
}
|
||||||
}catch (e: Exception){
|
}catch (e: Exception){
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,10 @@ package com.deniscerri.ytdlnis.database.viewmodel
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import android.net.ConnectivityManager
|
||||||
|
import android.net.NetworkCapabilities
|
||||||
import android.text.format.DateFormat
|
import android.text.format.DateFormat
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
|
@ -10,10 +13,7 @@ import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.work.Data
|
import androidx.work.*
|
||||||
import androidx.work.ExistingWorkPolicy
|
|
||||||
import androidx.work.OneTimeWorkRequestBuilder
|
|
||||||
import androidx.work.WorkManager
|
|
||||||
import com.deniscerri.ytdlnis.App
|
import com.deniscerri.ytdlnis.App
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.DBManager
|
import com.deniscerri.ytdlnis.database.DBManager
|
||||||
|
|
@ -287,6 +287,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
|
|
||||||
suspend fun queueDownloads(items: List<DownloadItem>) {
|
suspend fun queueDownloads(items: List<DownloadItem>) {
|
||||||
val context = getApplication<App>().applicationContext
|
val context = getApplication<App>().applicationContext
|
||||||
|
val allowMeteredNetworks = sharedPreferences.getBoolean("metered_networks", true)
|
||||||
items.forEach { it.status = DownloadRepository.Status.Queued.toString() }
|
items.forEach { it.status = DownloadRepository.Status.Queued.toString() }
|
||||||
val ids = repository.insertAll(items)
|
val ids = repository.insertAll(items)
|
||||||
for (i in ids.indices){
|
for (i in ids.indices){
|
||||||
|
|
@ -301,6 +302,11 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
val workRequest = OneTimeWorkRequestBuilder<DownloadWorker>()
|
val workRequest = OneTimeWorkRequestBuilder<DownloadWorker>()
|
||||||
.setInputData(Data.Builder().putLong("id", it.id).build())
|
.setInputData(Data.Builder().putLong("id", it.id).build())
|
||||||
.addTag("download")
|
.addTag("download")
|
||||||
|
.setConstraints(
|
||||||
|
Constraints.Builder()
|
||||||
|
.setRequiredNetworkType(if (allowMeteredNetworks) NetworkType.CONNECTED else NetworkType.UNMETERED)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
.setInitialDelay(delay, TimeUnit.MILLISECONDS)
|
.setInitialDelay(delay, TimeUnit.MILLISECONDS)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
|
@ -318,6 +324,10 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
repository.delete(it)
|
repository.delete(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val isCurrentNetworkMetered = (context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).isActiveNetworkMetered
|
||||||
|
if (!allowMeteredNetworks && isCurrentNetworkMetered){
|
||||||
|
Toast.makeText(context, context.getString(R.string.metered_network_download_start_info), Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -78,10 +78,7 @@ class ShareActivity : AppCompatActivity() {
|
||||||
cookieViewModel = ViewModelProvider(this)[CookieViewModel::class.java]
|
cookieViewModel = ViewModelProvider(this)[CookieViewModel::class.java]
|
||||||
sharedPreferences = getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
|
sharedPreferences = getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
|
||||||
|
|
||||||
cookieViewModel.getCookiesFromDB().getOrNull()?.let{
|
cookieViewModel.updateCookiesFile()
|
||||||
File(cacheDir, "cookies.txt").apply { writeText(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
val intent = intent
|
val intent = intent
|
||||||
handleIntents(intent)
|
handleIntents(intent)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
|
|
||||||
class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickListener {
|
class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickListener {
|
||||||
|
|
@ -595,6 +596,17 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
|
||||||
mode?.title = getString(R.string.all_items_selected)
|
mode?.title = getString(R.string.all_items_selected)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
R.id.invert_selected -> {
|
||||||
|
homeAdapter?.invertSelected(resultsList)
|
||||||
|
val invertedList = arrayListOf<ResultItem>()
|
||||||
|
resultsList?.forEach {
|
||||||
|
if (!selectedObjects?.contains(it)!!) invertedList.add(it!!)
|
||||||
|
}
|
||||||
|
selectedObjects?.clear()
|
||||||
|
selectedObjects?.addAll(invertedList)
|
||||||
|
actionMode!!.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
||||||
|
true
|
||||||
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.deniscerri.ytdlnis.ui.downloads
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
|
import android.content.Intent
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
|
|
@ -32,6 +33,7 @@ import com.deniscerri.ytdlnis.MainActivity
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.adapter.HistoryAdapter
|
import com.deniscerri.ytdlnis.adapter.HistoryAdapter
|
||||||
import com.deniscerri.ytdlnis.database.models.HistoryItem
|
import com.deniscerri.ytdlnis.database.models.HistoryItem
|
||||||
|
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||||
import com.deniscerri.ytdlnis.database.repository.HistoryRepository
|
import com.deniscerri.ytdlnis.database.repository.HistoryRepository
|
||||||
import com.deniscerri.ytdlnis.database.repository.HistoryRepository.HistorySort
|
import com.deniscerri.ytdlnis.database.repository.HistoryRepository.HistorySort
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||||
|
|
@ -213,6 +215,10 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
||||||
deleteDialog.show()
|
deleteDialog.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
R.id.download_queue -> {
|
||||||
|
val intent = Intent(context, DownloadQueueActivity::class.java)
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
R.id.remove_deleted_history -> {
|
R.id.remove_deleted_history -> {
|
||||||
if(allhistoryList!!.isEmpty()){
|
if(allhistoryList!!.isEmpty()){
|
||||||
Toast.makeText(context, R.string.history_is_empty, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, R.string.history_is_empty, Toast.LENGTH_SHORT).show()
|
||||||
|
|
@ -565,6 +571,17 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
||||||
actionMode?.finish()
|
actionMode?.finish()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
R.id.invert_selected -> {
|
||||||
|
historyAdapter?.invertSelected(historyList)
|
||||||
|
val invertedList = arrayListOf<HistoryItem>()
|
||||||
|
historyList?.forEach {
|
||||||
|
if (!selectedObjects?.contains(it)!!) invertedList.add(it!!)
|
||||||
|
}
|
||||||
|
selectedObjects?.clear()
|
||||||
|
selectedObjects?.addAll(invertedList)
|
||||||
|
actionMode!!.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
||||||
|
true
|
||||||
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,16 @@ import android.content.Context
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.widget.doOnTextChanged
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
|
|
@ -22,6 +25,8 @@ import com.deniscerri.ytdlnis.util.UiUtil
|
||||||
import com.google.android.material.appbar.MaterialToolbar
|
import com.google.android.material.appbar.MaterialToolbar
|
||||||
import com.google.android.material.chip.Chip
|
import com.google.android.material.chip.Chip
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.CookieManager
|
import java.net.CookieManager
|
||||||
|
|
||||||
|
|
@ -70,13 +75,18 @@ class CookiesActivity : AppCompatActivity(), CookieAdapter.OnItemClickListener {
|
||||||
deleteDialog.setMessage(getString(R.string.confirm_delete_cookies_desc))
|
deleteDialog.setMessage(getString(R.string.confirm_delete_cookies_desc))
|
||||||
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||||
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||||
android.webkit.CookieManager.getInstance().removeAllCookies(null)
|
cookiesViewModel.deleteAll()
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
File(cacheDir, "cookies.txt").apply { writeText("") }
|
File(cacheDir, "cookies.txt").apply { writeText("") }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
deleteDialog.show()
|
deleteDialog.show()
|
||||||
}
|
}
|
||||||
|
R.id.import_clipboard -> {
|
||||||
|
lifecycleScope.launch(Dispatchers.IO){
|
||||||
|
cookiesViewModel.importFromClipboard()
|
||||||
|
}
|
||||||
|
}
|
||||||
R.id.export_clipboard -> {
|
R.id.export_clipboard -> {
|
||||||
cookiesViewModel.exportToClipboard()
|
cookiesViewModel.exportToClipboard()
|
||||||
}
|
}
|
||||||
|
|
@ -97,7 +107,9 @@ class CookiesActivity : AppCompatActivity(), CookieAdapter.OnItemClickListener {
|
||||||
builder.setTitle(getString(R.string.cookies))
|
builder.setTitle(getString(R.string.cookies))
|
||||||
val inputLayout = layoutInflater.inflate(R.layout.textinput, null)
|
val inputLayout = layoutInflater.inflate(R.layout.textinput, null)
|
||||||
val editText = inputLayout.findViewById<EditText>(R.id.url_edittext)
|
val editText = inputLayout.findViewById<EditText>(R.id.url_edittext)
|
||||||
editText.setText(url)
|
val text = if (url.isNullOrBlank()) "https://" else url
|
||||||
|
editText.setText(text)
|
||||||
|
editText.setSelection(editText.text.length)
|
||||||
builder.setView(inputLayout)
|
builder.setView(inputLayout)
|
||||||
builder.setPositiveButton(
|
builder.setPositiveButton(
|
||||||
getString(R.string.get_cookies)
|
getString(R.string.get_cookies)
|
||||||
|
|
@ -112,9 +124,8 @@ class CookiesActivity : AppCompatActivity(), CookieAdapter.OnItemClickListener {
|
||||||
getString(R.string.cancel)
|
getString(R.string.cancel)
|
||||||
) { dialog: DialogInterface?, which: Int -> }
|
) { dialog: DialogInterface?, which: Int -> }
|
||||||
val dialog = builder.create()
|
val dialog = builder.create()
|
||||||
editText.setOnKeyListener { view, i, keyEvent ->
|
editText.doOnTextChanged { text, start, before, count ->
|
||||||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = editText.text.isNotEmpty()
|
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = editText.text.isNotEmpty()
|
||||||
false
|
|
||||||
}
|
}
|
||||||
dialog.show()
|
dialog.show()
|
||||||
val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
|
val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.deniscerri.ytdlnis.ui.more
|
package com.deniscerri.ytdlnis.ui.more
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.webkit.CookieManager
|
import android.webkit.CookieManager
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
|
|
@ -7,12 +8,16 @@ import android.webkit.WebViewClient
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.models.CookieItem
|
import com.deniscerri.ytdlnis.database.models.CookieItem
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.CookieViewModel
|
import com.deniscerri.ytdlnis.database.viewmodel.CookieViewModel
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
import com.google.android.material.appbar.MaterialToolbar
|
import com.google.android.material.appbar.MaterialToolbar
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -25,6 +30,7 @@ class WebViewActivity : AppCompatActivity() {
|
||||||
private lateinit var url: String
|
private lateinit var url: String
|
||||||
private lateinit var cookies: String
|
private lateinit var cookies: String
|
||||||
|
|
||||||
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
public override fun onCreate(savedInstanceState: Bundle?) {
|
public override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.webview_activity)
|
setContentView(R.layout.webview_activity)
|
||||||
|
|
@ -35,6 +41,7 @@ class WebViewActivity : AppCompatActivity() {
|
||||||
toolbar = appbar.findViewById(R.id.webviewToolbar)
|
toolbar = appbar.findViewById(R.id.webviewToolbar)
|
||||||
generateBtn = toolbar.findViewById(R.id.generate)
|
generateBtn = toolbar.findViewById(R.id.generate)
|
||||||
cookieManager = CookieManager.getInstance()
|
cookieManager = CookieManager.getInstance()
|
||||||
|
cookieManager.removeAllCookies(null)
|
||||||
|
|
||||||
webView = findViewById(R.id.webview)
|
webView = findViewById(R.id.webview)
|
||||||
webView.settings.javaScriptEnabled = true
|
webView.settings.javaScriptEnabled = true
|
||||||
|
|
@ -62,20 +69,24 @@ class WebViewActivity : AppCompatActivity() {
|
||||||
|
|
||||||
generateBtn.setOnClickListener {
|
generateBtn.setOnClickListener {
|
||||||
cookiesViewModel.getCookiesFromDB().getOrNull()?.let {
|
cookiesViewModel.getCookiesFromDB().getOrNull()?.let {
|
||||||
File(cacheDir , "/cookies.txt").apply { writeText(it) }
|
kotlin.runCatching {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
cookiesViewModel.insert(
|
||||||
|
CookieItem(
|
||||||
|
0,
|
||||||
|
url,
|
||||||
|
it
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
cookiesViewModel.updateCookiesFile()
|
||||||
|
}
|
||||||
|
}.onFailure {
|
||||||
|
Toast.makeText(this, "Something went wrong", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
kotlin.runCatching {
|
cookieManager.removeAllCookies(null)
|
||||||
cookiesViewModel.insert(
|
|
||||||
CookieItem(
|
|
||||||
0,
|
|
||||||
url,
|
|
||||||
""
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}.onFailure {
|
|
||||||
Toast.makeText(this, "Something went wrong", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
cookieManager.flush()
|
|
||||||
onBackPressedDispatcher.onBackPressed()
|
onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
private var incognito: SwitchPreferenceCompat? = null
|
private var incognito: SwitchPreferenceCompat? = null
|
||||||
private var preferredDownloadType : ListPreference? = null
|
private var preferredDownloadType : ListPreference? = null
|
||||||
private var downloadCard: SwitchPreferenceCompat? = null
|
private var downloadCard: SwitchPreferenceCompat? = null
|
||||||
|
private var meteredNetwork: SwitchPreferenceCompat? = null
|
||||||
private var apiKey: EditTextPreference? = null
|
private var apiKey: EditTextPreference? = null
|
||||||
private var concurrentFragments: SeekBarPreference? = null
|
private var concurrentFragments: SeekBarPreference? = null
|
||||||
private var concurrentDownloads: SeekBarPreference? = null
|
private var concurrentDownloads: SeekBarPreference? = null
|
||||||
|
|
@ -106,6 +107,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
incognito = findPreference("incognito")
|
incognito = findPreference("incognito")
|
||||||
preferredDownloadType = findPreference("preferred_download_type")
|
preferredDownloadType = findPreference("preferred_download_type")
|
||||||
downloadCard = findPreference("download_card")
|
downloadCard = findPreference("download_card")
|
||||||
|
meteredNetwork = findPreference("metered_networks")
|
||||||
apiKey = findPreference("api_key")
|
apiKey = findPreference("api_key")
|
||||||
concurrentFragments = findPreference("concurrent_fragments")
|
concurrentFragments = findPreference("concurrent_fragments")
|
||||||
concurrentDownloads = findPreference("concurrent_downloads")
|
concurrentDownloads = findPreference("concurrent_downloads")
|
||||||
|
|
@ -168,6 +170,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
editor.putBoolean("incognito", incognito!!.isChecked)
|
editor.putBoolean("incognito", incognito!!.isChecked)
|
||||||
editor.putString("preferred_download_type", preferredDownloadType!!.value)
|
editor.putString("preferred_download_type", preferredDownloadType!!.value)
|
||||||
editor.putBoolean("download_card", downloadCard!!.isChecked)
|
editor.putBoolean("download_card", downloadCard!!.isChecked)
|
||||||
|
editor.putBoolean("metered_networks", meteredNetwork!!.isChecked)
|
||||||
editor.putString("api_key", apiKey!!.text)
|
editor.putString("api_key", apiKey!!.text)
|
||||||
editor.putInt("concurrent_fragments", concurrentFragments!!.value)
|
editor.putInt("concurrent_fragments", concurrentFragments!!.value)
|
||||||
editor.putInt("concurrent_downloads", concurrentDownloads!!.value)
|
editor.putInt("concurrent_downloads", concurrentDownloads!!.value)
|
||||||
|
|
@ -282,8 +285,6 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
editor.apply()
|
editor.apply()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
downloadCard!!.onPreferenceChangeListener =
|
downloadCard!!.onPreferenceChangeListener =
|
||||||
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
|
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
|
||||||
val enable = newValue as Boolean
|
val enable = newValue as Boolean
|
||||||
|
|
@ -291,6 +292,13 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
editor.apply()
|
editor.apply()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
meteredNetwork!!.onPreferenceChangeListener =
|
||||||
|
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
|
||||||
|
val enable = newValue as Boolean
|
||||||
|
editor.putBoolean("metered_networks", enable)
|
||||||
|
editor.apply()
|
||||||
|
true
|
||||||
|
}
|
||||||
apiKey!!.onPreferenceChangeListener =
|
apiKey!!.onPreferenceChangeListener =
|
||||||
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
|
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
|
||||||
editor.putString("api_key", newValue.toString())
|
editor.putString("api_key", newValue.toString())
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@ package com.deniscerri.ytdlnis.util
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
|
import android.os.Looper
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.widget.Toast
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
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
|
||||||
|
|
@ -138,63 +140,70 @@ class InfoUtil(private val context: Context) {
|
||||||
|
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
fun getPlaylist(id: String, nextPageToken: String): PlaylistTuple {
|
fun getPlaylist(id: String, nextPageToken: String): PlaylistTuple {
|
||||||
init()
|
try{
|
||||||
items = ArrayList()
|
init()
|
||||||
if (key!!.isEmpty()) {
|
items = ArrayList()
|
||||||
return if (useInvidous) getPlaylistFromInvidous(id) else PlaylistTuple(
|
if (key!!.isEmpty()) {
|
||||||
|
return if (useInvidous) getPlaylistFromInvidous(id) else PlaylistTuple(
|
||||||
|
"",
|
||||||
|
getFromYTDL("https://www.youtube.com/playlist?list=$id")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val url = "https://youtube.googleapis.com/youtube/v3/playlistItems?part=snippet&pageToken=$nextPageToken&maxResults=50®ionCode=$countryCODE&playlistId=$id&key=$key"
|
||||||
|
//short data
|
||||||
|
val res = genericRequest(url)
|
||||||
|
if (!res.has("items")) return PlaylistTuple(
|
||||||
|
"",
|
||||||
|
getFromYTDL("https://www.youtube.com/playlist?list=$id")
|
||||||
|
)
|
||||||
|
val dataArray = res.getJSONArray("items")
|
||||||
|
|
||||||
|
//extra data
|
||||||
|
var url2 = "https://www.googleapis.com/youtube/v3/videos?id="
|
||||||
|
//getting all ids, for the extra data request
|
||||||
|
for (i in 0 until dataArray.length()) {
|
||||||
|
val element = dataArray.getJSONObject(i)
|
||||||
|
val snippet = element.getJSONObject("snippet")
|
||||||
|
val videoID = snippet.getJSONObject("resourceId").getString("videoId")
|
||||||
|
url2 = "$url2$videoID,"
|
||||||
|
snippet.put("videoID", videoID)
|
||||||
|
}
|
||||||
|
url2 = url2.substring(
|
||||||
|
0,
|
||||||
|
url2.length - 1
|
||||||
|
) + "&part=contentDetails®ionCode=" + countryCODE + "&key=" + key
|
||||||
|
val extra = genericRequest(url2)
|
||||||
|
val extraArray = extra.getJSONArray("items")
|
||||||
|
var j = 0
|
||||||
|
var i = 0
|
||||||
|
while (i < extraArray.length()) {
|
||||||
|
val element = dataArray.getJSONObject(i)
|
||||||
|
val snippet = element.getJSONObject("snippet")
|
||||||
|
var duration =
|
||||||
|
extra.getJSONArray("items").getJSONObject(j).getJSONObject("contentDetails")
|
||||||
|
.getString("duration")
|
||||||
|
duration = formatDuration(duration)
|
||||||
|
snippet.put("duration", duration)
|
||||||
|
fixThumbnail(snippet)
|
||||||
|
val v = createVideofromJSON(snippet)
|
||||||
|
if (v == null || v.thumb.isEmpty()) {
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
v.playlistTitle = "YTDLnis"
|
||||||
|
items.add(v)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
val next = res.optString("nextPageToken")
|
||||||
|
return PlaylistTuple(next, items)
|
||||||
|
}catch (e: Exception){
|
||||||
|
return PlaylistTuple(
|
||||||
"",
|
"",
|
||||||
getFromYTDL("https://www.youtube.com/playlist?list=$id")
|
getFromYTDL("https://www.youtube.com/playlist?list=$id")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val url = "https://youtube.googleapis.com/youtube/v3/playlistItems?part=snippet&pageToken=$nextPageToken&maxResults=50®ionCode=$countryCODE&playlistId=$id&key=$key"
|
|
||||||
//short data
|
|
||||||
val res = genericRequest(url)
|
|
||||||
if (!res.has("items")) return PlaylistTuple(
|
|
||||||
"",
|
|
||||||
getFromYTDL("https://www.youtube.com/playlist?list=$id")
|
|
||||||
)
|
|
||||||
val dataArray = res.getJSONArray("items")
|
|
||||||
|
|
||||||
//extra data
|
|
||||||
var url2 = "https://www.googleapis.com/youtube/v3/videos?id="
|
|
||||||
//getting all ids, for the extra data request
|
|
||||||
for (i in 0 until dataArray.length()) {
|
|
||||||
val element = dataArray.getJSONObject(i)
|
|
||||||
val snippet = element.getJSONObject("snippet")
|
|
||||||
val videoID = snippet.getJSONObject("resourceId").getString("videoId")
|
|
||||||
url2 = "$url2$videoID,"
|
|
||||||
snippet.put("videoID", videoID)
|
|
||||||
}
|
|
||||||
url2 = url2.substring(
|
|
||||||
0,
|
|
||||||
url2.length - 1
|
|
||||||
) + "&part=contentDetails®ionCode=" + countryCODE + "&key=" + key
|
|
||||||
val extra = genericRequest(url2)
|
|
||||||
val extraArray = extra.getJSONArray("items")
|
|
||||||
var j = 0
|
|
||||||
var i = 0
|
|
||||||
while (i < extraArray.length()) {
|
|
||||||
val element = dataArray.getJSONObject(i)
|
|
||||||
val snippet = element.getJSONObject("snippet")
|
|
||||||
var duration =
|
|
||||||
extra.getJSONArray("items").getJSONObject(j).getJSONObject("contentDetails")
|
|
||||||
.getString("duration")
|
|
||||||
duration = formatDuration(duration)
|
|
||||||
snippet.put("duration", duration)
|
|
||||||
fixThumbnail(snippet)
|
|
||||||
val v = createVideofromJSON(snippet)
|
|
||||||
if (v == null || v.thumb.isEmpty()) {
|
|
||||||
i++
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
v.playlistTitle = "YTDLnis"
|
|
||||||
items.add(v)
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
val next = res.optString("nextPageToken")
|
|
||||||
return PlaylistTuple(next, items)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getPlaylistFromInvidous(id: String): PlaylistTuple {
|
private fun getPlaylistFromInvidous(id: String): PlaylistTuple {
|
||||||
|
|
@ -221,30 +230,34 @@ class InfoUtil(private val context: Context) {
|
||||||
|
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
fun getVideo(id: String): ResultItem? {
|
fun getVideo(id: String): ResultItem? {
|
||||||
init()
|
try {
|
||||||
if (key!!.isEmpty()) {
|
init()
|
||||||
return if (useInvidous) {
|
if (key!!.isEmpty()) {
|
||||||
val res = genericRequest(invidousURL + "videos/" + id)
|
return if (useInvidous) {
|
||||||
if (res.length() == 0) getFromYTDL("https://www.youtube.com/watch?v=$id")[0] else createVideofromInvidiousJSON(
|
val res = genericRequest(invidousURL + "videos/" + id)
|
||||||
res
|
if (res.length() == 0) getFromYTDL("https://www.youtube.com/watch?v=$id")[0] else createVideofromInvidiousJSON(
|
||||||
)
|
res
|
||||||
} else {
|
)
|
||||||
getFromYTDL("https://www.youtube.com/watch?v=$id")[0]
|
} else {
|
||||||
|
getFromYTDL("https://www.youtube.com/watch?v=$id")[0]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//short data
|
//short data
|
||||||
var res =
|
var res =
|
||||||
genericRequest("https://youtube.googleapis.com/youtube/v3/videos?part=snippet,contentDetails&id=$id&key=$key")
|
genericRequest("https://youtube.googleapis.com/youtube/v3/videos?part=snippet,contentDetails&id=$id&key=$key")
|
||||||
if (!res.has("items")) return getFromYTDL("https://www.youtube.com/watch?v=$id")[0]
|
if (!res.has("items")) return getFromYTDL("https://www.youtube.com/watch?v=$id")[0]
|
||||||
var duration = res.getJSONArray("items").getJSONObject(0).getJSONObject("contentDetails")
|
var duration = res.getJSONArray("items").getJSONObject(0).getJSONObject("contentDetails")
|
||||||
.getString("duration")
|
.getString("duration")
|
||||||
duration = formatDuration(duration)
|
duration = formatDuration(duration)
|
||||||
res = res.getJSONArray("items").getJSONObject(0).getJSONObject("snippet")
|
res = res.getJSONArray("items").getJSONObject(0).getJSONObject("snippet")
|
||||||
res.put("videoID", id)
|
res.put("videoID", id)
|
||||||
res.put("duration", duration)
|
res.put("duration", duration)
|
||||||
fixThumbnail(res)
|
fixThumbnail(res)
|
||||||
return createVideofromJSON(res)
|
return createVideofromJSON(res)
|
||||||
|
}catch (e: Exception){
|
||||||
|
return getFromYTDL("https://www.youtube.com/watch?v=$id")[0]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createVideofromJSON(obj: JSONObject): ResultItem? {
|
private fun createVideofromJSON(obj: JSONObject): ResultItem? {
|
||||||
|
|
@ -510,7 +523,8 @@ class InfoUtil(private val context: Context) {
|
||||||
arrayOf(youtubeDLResponse.out)
|
arrayOf(youtubeDLResponse.out)
|
||||||
}
|
}
|
||||||
for (result in results) {
|
for (result in results) {
|
||||||
val jsonObject = JSONObject(result!!)
|
if (result.isNullOrBlank()) continue
|
||||||
|
val jsonObject = JSONObject(result)
|
||||||
val title = if (jsonObject.has("title")) {
|
val title = if (jsonObject.has("title")) {
|
||||||
if (jsonObject.getString("title") == "[Private video]") continue
|
if (jsonObject.getString("title") == "[Private video]") continue
|
||||||
jsonObject.getString("title")
|
jsonObject.getString("title")
|
||||||
|
|
@ -531,7 +545,9 @@ class InfoUtil(private val context: Context) {
|
||||||
thumb = jsonObject.getString("thumbnail")
|
thumb = jsonObject.getString("thumbnail")
|
||||||
} else if (jsonObject.has("thumbnails")) {
|
} else if (jsonObject.has("thumbnails")) {
|
||||||
val thumbs = jsonObject.getJSONArray("thumbnails")
|
val thumbs = jsonObject.getJSONArray("thumbnails")
|
||||||
thumb = thumbs.getJSONObject(thumbs.length() - 1).getString("url")
|
if (thumbs.length() > 0){
|
||||||
|
thumb = thumbs.getJSONObject(thumbs.length() - 1).getString("url")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val website = if (jsonObject.has("ie_key")) jsonObject.getString("ie_key") else jsonObject.getString("extractor")
|
val website = if (jsonObject.has("ie_key")) jsonObject.getString("ie_key") else jsonObject.getString("extractor")
|
||||||
var playlistTitle: String? = ""
|
var playlistTitle: String? = ""
|
||||||
|
|
@ -560,6 +576,9 @@ class InfoUtil(private val context: Context) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
Looper.prepare().run {
|
||||||
|
Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
return items
|
return items
|
||||||
|
|
|
||||||
|
|
@ -145,25 +145,27 @@ class DownloadWorker(
|
||||||
|
|
||||||
if (audioQualityId.isNotBlank()){
|
if (audioQualityId.isNotBlank()){
|
||||||
request.addOption("-f", audioQualityId)
|
request.addOption("-f", audioQualityId)
|
||||||
}
|
|
||||||
|
|
||||||
val ext = downloadItem.format.container
|
val ext = downloadItem.format.container
|
||||||
if(ext != context.getString(R.string.defaultValue) && ext != "webm"){
|
if(ext != context.getString(R.string.defaultValue) && ext != "webm"){
|
||||||
val codec = when(downloadItem.format.container){
|
val codec = when(downloadItem.format.container){
|
||||||
"m4a" -> "aac"
|
"aac" -> "aac"
|
||||||
"aac" -> "aac"
|
"mp3" -> "libmp3lame"
|
||||||
"mp3" -> "libmp3lame"
|
"flac" -> "flac"
|
||||||
"flac" -> "flac"
|
"opus" -> "libopus"
|
||||||
"opus" -> "libopus"
|
else -> ""
|
||||||
else -> ""
|
}
|
||||||
}
|
Log.e("aa", codec)
|
||||||
Log.e("aa", codec)
|
if (codec.isEmpty()){
|
||||||
if (codec.isEmpty()){
|
request.addOption("-x")
|
||||||
request.addOption("-x")
|
}else{
|
||||||
}else{
|
request.addOption("--remux-video", ext)
|
||||||
request.addOption("--remux-video", ext)
|
request.addOption("--ppa", "VideoRemuxer:-vn -c:a $codec")
|
||||||
request.addOption("--ppa", "VideoRemuxer:-vn -c:a $codec")
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}else{
|
||||||
|
request.addOption("-x")
|
||||||
}
|
}
|
||||||
|
|
||||||
request.addOption("--embed-metadata")
|
request.addOption("--embed-metadata")
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,15 @@ class TerminalDownloadWorker(
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val chunks = it.out.chunked(5000)
|
||||||
|
val dataBuilder = Data.Builder();
|
||||||
|
dataBuilder.putString("output", chunks[0])
|
||||||
|
|
||||||
|
|
||||||
|
return Result.success(
|
||||||
|
dataBuilder.build()
|
||||||
|
)
|
||||||
|
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
if (it is YoutubeDL.CanceledException) {
|
if (it is YoutubeDL.CanceledException) {
|
||||||
return Result.failure()
|
return Result.failure()
|
||||||
|
|
|
||||||
5
app/src/main/res/drawable/baseline_downloading_24.xml
Normal file
5
app/src/main/res/drawable/baseline_downloading_24.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<vector android:height="24dp"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="?android:textColorPrimary" android:pathData="M18.32,4.26C16.84,3.05 15.01,2.25 13,2.05v2.02c1.46,0.18 2.79,0.76 3.9,1.62L18.32,4.26zM19.93,11h2.02c-0.2,-2.01 -1,-3.84 -2.21,-5.32L18.31,7.1C19.17,8.21 19.75,9.54 19.93,11zM18.31,16.9l1.43,1.43c1.21,-1.48 2.01,-3.32 2.21,-5.32h-2.02C19.75,14.46 19.17,15.79 18.31,16.9zM13,19.93v2.02c2.01,-0.2 3.84,-1 5.32,-2.21l-1.43,-1.43C15.79,19.17 14.46,19.75 13,19.93zM13,12V7h-2v5H7l5,5l5,-5H13zM11,19.93v2.02c-5.05,-0.5 -9,-4.76 -9,-9.95s3.95,-9.45 9,-9.95v2.02C7.05,4.56 4,7.92 4,12S7.05,19.44 11,19.93z"/>
|
||||||
|
</vector>
|
||||||
5
app/src/main/res/drawable/metered_networks.xml
Normal file
5
app/src/main/res/drawable/metered_networks.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<vector android:height="24dp"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="?android:colorAccent" android:pathData="M18.99,11.5c0.34,0 0.67,0.03 1,0.07L20,0 0,20h11.56c-0.04,-0.33 -0.07,-0.66 -0.07,-1 0,-4.14 3.36,-7.5 7.5,-7.5zM22.7,19.49c0.02,-0.16 0.04,-0.32 0.04,-0.49 0,-0.17 -0.01,-0.33 -0.04,-0.49l1.06,-0.83c0.09,-0.08 0.12,-0.21 0.06,-0.32l-1,-1.73c-0.06,-0.11 -0.19,-0.15 -0.31,-0.11l-1.24,0.5c-0.26,-0.2 -0.54,-0.37 -0.85,-0.49l-0.19,-1.32c-0.01,-0.12 -0.12,-0.21 -0.24,-0.21h-2c-0.12,0 -0.23,0.09 -0.25,0.21l-0.19,1.32c-0.3,0.13 -0.59,0.29 -0.85,0.49l-1.24,-0.5c-0.11,-0.04 -0.24,0 -0.31,0.11l-1,1.73c-0.06,0.11 -0.04,0.24 0.06,0.32l1.06,0.83c-0.02,0.16 -0.03,0.32 -0.03,0.49 0,0.17 0.01,0.33 0.03,0.49l-1.06,0.83c-0.09,0.08 -0.12,0.21 -0.06,0.32l1,1.73c0.06,0.11 0.19,0.15 0.31,0.11l1.24,-0.5c0.26,0.2 0.54,0.37 0.85,0.49l0.19,1.32c0.02,0.12 0.12,0.21 0.25,0.21h2c0.12,0 0.23,-0.09 0.25,-0.21l0.19,-1.32c0.3,-0.13 0.59,-0.29 0.84,-0.49l1.25,0.5c0.11,0.04 0.24,0 0.31,-0.11l1,-1.73c0.06,-0.11 0.03,-0.24 -0.06,-0.32l-1.07,-0.83zM18.99,20.5c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
android:checkable="true"
|
android:checkable="true"
|
||||||
app:strokeWidth="0dp"
|
app:strokeWidth="0dp"
|
||||||
app:cardPreventCornerOverlap="true"
|
app:cardPreventCornerOverlap="true"
|
||||||
android:layout_margin="20dp">
|
android:layout_margin="10dp">
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
android:id="@+id/image_view"
|
android:id="@+id/image_view"
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_margin="10dp"
|
|
||||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -37,6 +36,7 @@
|
||||||
android:id="@+id/chips_command"
|
android:id="@+id/chips_command"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:scrollbars="none"
|
android:scrollbars="none"
|
||||||
|
android:layout_margin="10dp"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|
|
||||||
|
|
@ -29,12 +29,12 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_margin="10dp"
|
|
||||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
||||||
|
|
||||||
|
|
||||||
<com.google.android.material.chip.Chip
|
<com.google.android.material.chip.Chip
|
||||||
android:id="@+id/newCookie"
|
android:id="@+id/newCookie"
|
||||||
|
android:layout_margin="10dp"
|
||||||
style="@style/Widget.Material3.Chip.Assist"
|
style="@style/Widget.Material3.Chip.Assist"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
||||||
|
|
@ -30,47 +30,58 @@
|
||||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<ScrollView
|
<LinearLayout
|
||||||
android:layout_margin="10dp"
|
|
||||||
android:id="@+id/custom_command_scrollview"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="0dp"
|
||||||
app:layout_constraintVertical_bias="0"
|
android:orientation="horizontal"
|
||||||
android:clipToPadding="false"
|
|
||||||
android:paddingBottom="100dp"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/coordinatorLayout"
|
app:layout_constraintBottom_toTopOf="@+id/coordinatorLayout"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<LinearLayout
|
<ScrollView
|
||||||
|
android:id="@+id/custom_command_scrollview"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:focusableInTouchMode="true"
|
android:layout_margin="10dp">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:id="@+id/custom_command_output"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:fontFamily="monospace"
|
android:focusableInTouchMode="true"
|
||||||
android:gravity="bottom"
|
android:orientation="vertical">
|
||||||
android:textIsSelectable="true" />
|
|
||||||
|
|
||||||
<EditText
|
<HorizontalScrollView
|
||||||
android:id="@+id/command_edittext"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@android:color/transparent"
|
|
||||||
android:fontFamily="monospace"
|
|
||||||
android:gravity="start"
|
|
||||||
android:inputType="textMultiLine"
|
|
||||||
android:paddingBottom="100dp"
|
|
||||||
android:maxLines="100"
|
|
||||||
android:text="yt-dlp "
|
|
||||||
android:textSize="15sp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
<TextView
|
||||||
|
android:id="@+id/custom_command_output"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="monospace"
|
||||||
|
android:gravity="bottom"
|
||||||
|
android:scrollbars="horizontal|vertical"
|
||||||
|
android:scrollHorizontally="true"
|
||||||
|
android:textIsSelectable="true" />
|
||||||
|
|
||||||
</ScrollView>
|
</HorizontalScrollView>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/command_edittext"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
android:fontFamily="monospace"
|
||||||
|
android:gravity="start"
|
||||||
|
android:inputType="textMultiLine"
|
||||||
|
android:maxLines="100"
|
||||||
|
android:text="yt-dlp "
|
||||||
|
android:textSize="15sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,8 @@
|
||||||
<com.google.android.material.chip.ChipGroup
|
<com.google.android.material.chip.ChipGroup
|
||||||
android:id="@+id/shortcutsChipGroup"
|
android:id="@+id/shortcutsChipGroup"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
app:chipSpacingVertical="-10dp"
|
||||||
|
app:chipSpacing="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:singleLine="false">
|
app:singleLine="false">
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,8 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="10dp"
|
android:layout_marginTop="10dp"
|
||||||
android:padding="10dp"
|
android:paddingHorizontal="10dp"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
android:hint="@string/title"
|
android:hint="@string/title"
|
||||||
style="@style/Widget.Material3.TextInputLayout.FilledBox">
|
style="@style/Widget.Material3.TextInputLayout.FilledBox">
|
||||||
|
|
||||||
|
|
@ -34,7 +35,7 @@
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="10dp"
|
android:paddingHorizontal="10dp"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
|
@ -87,8 +88,8 @@
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/outputPath"
|
android:id="@+id/outputPath"
|
||||||
android:paddingHorizontal="10dp"
|
android:paddingHorizontal="10dp"
|
||||||
android:paddingTop="10dp"
|
|
||||||
android:paddingBottom="0dp"
|
android:paddingBottom="0dp"
|
||||||
|
android:paddingTop="10dp"
|
||||||
style="@style/Widget.Material3.TextInputLayout.FilledBox"
|
style="@style/Widget.Material3.TextInputLayout.FilledBox"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
@ -113,7 +114,7 @@
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/adjust_audio"
|
android:id="@+id/adjust_audio"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:padding="10dp"
|
android:paddingHorizontal="10dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,8 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="10dp"
|
android:layout_marginTop="10dp"
|
||||||
android:hint="@string/title"
|
android:hint="@string/title"
|
||||||
android:padding="10dp">
|
android:paddingBottom="10dp"
|
||||||
|
android:paddingHorizontal="10dp">
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
@ -32,7 +33,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:padding="10dp">
|
android:paddingHorizontal="10dp">
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/author_textinput"
|
android:id="@+id/author_textinput"
|
||||||
|
|
@ -111,7 +112,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="10dp">
|
android:paddingHorizontal="10dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|
|
||||||
|
|
@ -6,132 +6,138 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<LinearLayout
|
<ScrollView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent">
|
||||||
android:gravity="top"
|
|
||||||
android:orientation="vertical"
|
|
||||||
tools:ignore="MissingConstraints">
|
|
||||||
|
|
||||||
<ImageView
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:background="@drawable/ic_app_icon"
|
|
||||||
android:scaleType="center" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/terminal"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/terminal"
|
android:gravity="top"
|
||||||
android:textStyle="bold"
|
android:orientation="vertical"
|
||||||
android:clickable="true"
|
tools:ignore="MissingConstraints">
|
||||||
android:focusable="true"
|
|
||||||
android:background="?android:attr/selectableItemBackground"
|
|
||||||
android:paddingHorizontal="15dp"
|
|
||||||
android:paddingVertical="15dp"
|
|
||||||
android:drawablePadding="35dp"
|
|
||||||
android:textSize="16sp"
|
|
||||||
app:drawableStartCompat="@drawable/ic_terminal" />
|
|
||||||
|
|
||||||
<TextView
|
<ImageView
|
||||||
android:id="@+id/logs"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_gravity="center"
|
||||||
android:text="@string/logs"
|
android:background="@drawable/ic_app_icon"
|
||||||
android:textStyle="bold"
|
android:scaleType="center" />
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
<TextView
|
||||||
android:background="?android:attr/selectableItemBackground"
|
android:id="@+id/terminal"
|
||||||
android:paddingHorizontal="15dp"
|
android:layout_width="match_parent"
|
||||||
android:paddingVertical="15dp"
|
android:layout_height="wrap_content"
|
||||||
android:drawablePadding="35dp"
|
android:text="@string/terminal"
|
||||||
android:textSize="16sp"
|
android:textStyle="bold"
|
||||||
app:drawableStartCompat="@drawable/ic_baseline_file_open_24" />
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:paddingHorizontal="15dp"
|
||||||
|
android:paddingVertical="15dp"
|
||||||
|
android:drawablePadding="35dp"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:drawableStartCompat="@drawable/ic_terminal" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/logs"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/logs"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:paddingHorizontal="15dp"
|
||||||
|
android:paddingVertical="15dp"
|
||||||
|
android:drawablePadding="35dp"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:drawableStartCompat="@drawable/ic_baseline_file_open_24" />
|
||||||
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/command_templates"
|
android:id="@+id/command_templates"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:drawablePadding="35dp"
|
android:drawablePadding="35dp"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:paddingVertical="15dp"
|
android:paddingVertical="15dp"
|
||||||
android:text="@string/command_templates"
|
android:text="@string/command_templates"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
android:background="?android:attr/selectableItemBackground"
|
||||||
android:paddingHorizontal="15dp"
|
android:paddingHorizontal="15dp"
|
||||||
app:drawableStartCompat="@drawable/ic_baseline_keyboard_arrow_right_24" />
|
app:drawableStartCompat="@drawable/ic_baseline_keyboard_arrow_right_24" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/download_queue"
|
android:id="@+id/download_queue"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:drawablePadding="35dp"
|
android:drawablePadding="35dp"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:paddingVertical="15dp"
|
android:paddingVertical="15dp"
|
||||||
android:text="@string/download_queue"
|
android:text="@string/download_queue"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
android:background="?android:attr/selectableItemBackground"
|
||||||
android:paddingHorizontal="15dp"
|
android:paddingHorizontal="15dp"
|
||||||
app:drawableStartCompat="@drawable/ic_concurrent_downloads" />
|
app:drawableStartCompat="@drawable/ic_concurrent_downloads" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/cookies"
|
android:id="@+id/cookies"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:drawablePadding="35dp"
|
android:drawablePadding="35dp"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:paddingVertical="15dp"
|
android:paddingVertical="15dp"
|
||||||
android:text="@string/cookies"
|
android:text="@string/cookies"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
android:background="?android:attr/selectableItemBackground"
|
||||||
android:paddingHorizontal="15dp"
|
android:paddingHorizontal="15dp"
|
||||||
app:drawableStartCompat="@drawable/baseline_cookie_24" />
|
app:drawableStartCompat="@drawable/baseline_cookie_24" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/terminate"
|
android:id="@+id/terminate"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:drawablePadding="35dp"
|
android:drawablePadding="35dp"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
android:background="?android:attr/selectableItemBackground"
|
||||||
android:paddingVertical="15dp"
|
android:paddingVertical="15dp"
|
||||||
android:text="@string/kill_app"
|
android:text="@string/kill_app"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:paddingHorizontal="15dp"
|
android:paddingHorizontal="15dp"
|
||||||
app:drawableStartCompat="@drawable/ic_power" />
|
app:drawableStartCompat="@drawable/ic_power" />
|
||||||
|
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:layout_margin="0dp"
|
android:layout_margin="0dp"
|
||||||
style="@style/Divider.Horizontal" />
|
style="@style/Divider.Horizontal" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/settings"
|
android:id="@+id/settings"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:drawablePadding="35dp"
|
android:drawablePadding="35dp"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:paddingVertical="15dp"
|
android:paddingVertical="15dp"
|
||||||
android:text="@string/settings"
|
android:text="@string/settings"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
android:background="?android:attr/selectableItemBackground"
|
||||||
android:paddingHorizontal="15dp"
|
android:paddingHorizontal="15dp"
|
||||||
app:drawableStartCompat="@drawable/ic_settings" />
|
app:drawableStartCompat="@drawable/ic_settings" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,13 @@
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="10dp"
|
|
||||||
android:padding="10dp"
|
|
||||||
android:layout_marginVertical="10dp"
|
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:text="@string/sort_by"
|
android:text="@string/sort_by"
|
||||||
android:paddingBottom="20dp"
|
android:textSize="12sp"
|
||||||
|
android:layout_margin="20dp"
|
||||||
android:layout_height="wrap_content"/>
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
|
@ -27,6 +25,7 @@
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:paddingVertical="10dp"
|
android:paddingVertical="10dp"
|
||||||
|
android:paddingHorizontal="20dp"
|
||||||
android:drawablePadding="30dp"
|
android:drawablePadding="30dp"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
@ -41,6 +40,7 @@
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:paddingVertical="10dp"
|
android:paddingVertical="10dp"
|
||||||
|
android:paddingHorizontal="20dp"
|
||||||
android:drawablePadding="30dp"
|
android:drawablePadding="30dp"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
@ -55,6 +55,7 @@
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:paddingVertical="10dp"
|
android:paddingVertical="10dp"
|
||||||
|
android:paddingHorizontal="20dp"
|
||||||
android:drawablePadding="30dp"
|
android:drawablePadding="30dp"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
@ -70,6 +71,7 @@
|
||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:drawablePadding="30dp"
|
android:drawablePadding="30dp"
|
||||||
android:paddingVertical="10dp"
|
android:paddingVertical="10dp"
|
||||||
|
android:paddingHorizontal="20dp"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textSize="15sp"
|
android:textSize="15sp"
|
||||||
|
|
@ -77,7 +79,6 @@
|
||||||
app:drawableStartCompat="@drawable/ic_arrow_up" />
|
app:drawableStartCompat="@drawable/ic_arrow_up" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,8 @@
|
||||||
<com.google.android.material.chip.ChipGroup
|
<com.google.android.material.chip.ChipGroup
|
||||||
android:id="@+id/shortcutsChipGroup"
|
android:id="@+id/shortcutsChipGroup"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
app:chipSpacingVertical="0dp"
|
app:chipSpacingVertical="-10dp"
|
||||||
|
app:chipSpacing="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:singleLine="false">
|
app:singleLine="false">
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
android:padding="10dp"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,11 @@
|
||||||
android:title="@string/delete_all_cookies"
|
android:title="@string/delete_all_cookies"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/import_clipboard"
|
||||||
|
android:title="@string/import_from_clipboard"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/export_clipboard"
|
android:id="@+id/export_clipboard"
|
||||||
android:title="@string/export_from_clipboard"
|
android:title="@string/export_from_clipboard"
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,15 @@
|
||||||
android:id="@+id/search_history"
|
android:id="@+id/search_history"
|
||||||
android:title="@string/search"
|
android:title="@string/search"
|
||||||
android:icon="@drawable/ic_search"
|
android:icon="@drawable/ic_search"
|
||||||
app:showAsAction="collapseActionView|always"
|
app:showAsAction="collapseActionView|ifRoom"
|
||||||
app:actionViewClass="androidx.appcompat.widget.SearchView"/>
|
app:actionViewClass="androidx.appcompat.widget.SearchView"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/download_queue"
|
||||||
|
android:title="@string/download_queue"
|
||||||
|
android:icon="@drawable/baseline_downloading_24"
|
||||||
|
app:showAsAction="collapseActionView|ifRoom"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/remove_history"
|
android:id="@+id/remove_history"
|
||||||
android:icon="@drawable/ic_delete_all"
|
android:icon="@drawable/ic_delete_all"
|
||||||
|
|
|
||||||
|
|
@ -15,5 +15,10 @@
|
||||||
app:showAsAction="ifRoom"
|
app:showAsAction="ifRoom"
|
||||||
android:title="@string/share" />
|
android:title="@string/share" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/invert_selected"
|
||||||
|
android:title="@string/invert_selected"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,5 +20,10 @@
|
||||||
android:title="@string/select_all"
|
android:title="@string/select_all"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/invert_selected"
|
||||||
|
android:title="@string/invert_selected"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -204,4 +204,8 @@
|
||||||
<string name="get_cookies">Get Cookies</string>
|
<string name="get_cookies">Get Cookies</string>
|
||||||
<string name="delete_all_cookies">Delete All Cookies</string>
|
<string name="delete_all_cookies">Delete All Cookies</string>
|
||||||
<string name="confirm_delete_cookies_desc">Delete all the cookies, permanently</string>
|
<string name="confirm_delete_cookies_desc">Delete all the cookies, permanently</string>
|
||||||
|
<string name="download_over_metered_networks">Download over Metered Networks</string>
|
||||||
|
<string name="download_over_metered_networks_summary">Let the download worker work on any network type. If disabled, it will wait for an unmetered network to start the downloads</string>
|
||||||
|
<string name="metered_network_download_start_info">Download worker will start after you are connected to an unmetered network</string>
|
||||||
|
<string name="invert_selected">Invert Selected</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -65,6 +65,14 @@
|
||||||
app:summary="@string/download_card_summary"
|
app:summary="@string/download_card_summary"
|
||||||
app:title="@string/show_download_card" />
|
app:title="@string/show_download_card" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="true"
|
||||||
|
android:icon="@drawable/metered_networks"
|
||||||
|
android:key="metered_networks"
|
||||||
|
app:summary="@string/download_over_metered_networks_summary"
|
||||||
|
app:title="@string/download_over_metered_networks" />
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:dependency="download_card"
|
android:dependency="download_card"
|
||||||
android:defaultValue="video"
|
android:defaultValue="video"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue