From 467c0bd16e0bd9dff55a3a03062e6e0266da264f Mon Sep 17 00:00:00 2001 From: deniscerri <64997243+deniscerri@users.noreply.github.com> Date: Sat, 25 Mar 2023 21:58:48 +0100 Subject: [PATCH] added custom command and shortcuts buttons to terminal screen --- .../viewmodel/CommandTemplateViewModel.kt | 4 + .../database/viewmodel/HistoryViewModel.kt | 3 +- .../ytdlnis/ui/downloads/HistoryFragment.kt | 4 + .../ytdlnis/ui/more/MoreFragment.kt | 34 +++- .../ytdlnis/ui/more/TerminalActivity.kt | 182 +++++++++++++----- app/src/main/res/layout/activity_terminal.xml | 161 ++++++++-------- .../main/res/layout/command_template_list.xml | 66 +++++++ .../main/res/layout/dialog_terminate_app.xml | 21 ++ app/src/main/res/layout/fragment_more.xml | 18 ++ .../res/layout/template_shortcuts_list.xml | 74 +++++++ app/src/main/res/menu/terminal_menu.xml | 14 ++ app/src/main/res/values/strings.xml | 2 + 12 files changed, 452 insertions(+), 131 deletions(-) create mode 100644 app/src/main/res/layout/command_template_list.xml create mode 100644 app/src/main/res/layout/dialog_terminate_app.xml create mode 100644 app/src/main/res/layout/template_shortcuts_list.xml create mode 100644 app/src/main/res/menu/terminal_menu.xml diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/CommandTemplateViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/CommandTemplateViewModel.kt index 40f9ceff..b77d2f3c 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/CommandTemplateViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/CommandTemplateViewModel.kt @@ -41,6 +41,10 @@ class CommandTemplateViewModel(private val application: Application) : AndroidVi return repository.getAll() } + fun getAllShortcuts() : List { + return repository.getAllShortCuts() + } + fun getTotalNumber(): Int { return repository.getTotalNumber() } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/HistoryViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/HistoryViewModel.kt index 2727f4e0..41344ab6 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/HistoryViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/HistoryViewModel.kt @@ -15,7 +15,7 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application) private val repository : HistoryRepository val sortOrder = MutableLiveData(HistorySort.DESC) val sortType = MutableLiveData(HistorySortType.DATE) - private val websiteFilter = MutableLiveData("") + val websiteFilter = MutableLiveData("") private val queryFilter = MutableLiveData("") private val formatFilter = MutableLiveData("") val allItems : LiveData> @@ -48,6 +48,7 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application) return _items } + fun setSorting(sort: HistorySortType){ if (sortType.value != sort){ sortOrder.value = HistorySort.DESC diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/HistoryFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/HistoryFragment.kt index 61942676..ddc7fc6b 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/HistoryFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/HistoryFragment.kt @@ -333,6 +333,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{ private fun updateWebsiteChips(list : List) { val websites = mutableListOf() + val websiteFilter = historyViewModel.websiteFilter.value for (item in list){ if (!websites.contains(item.website)) websites.add(item.website) } @@ -353,6 +354,9 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{ tmp.isChecked = false } } + if (w == websiteFilter){ + tmp.isChecked = true + } websiteGroup!!.addView(tmp) } } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/MoreFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/MoreFragment.kt index a47d2743..51082338 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/MoreFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/MoreFragment.kt @@ -1,12 +1,14 @@ package com.deniscerri.ytdlnis.ui.more import android.app.Activity +import android.content.DialogInterface import android.content.Intent import android.content.SharedPreferences import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.CheckBox import android.widget.TextView import androidx.fragment.app.Fragment import androidx.navigation.Navigation @@ -18,10 +20,13 @@ import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding import com.deniscerri.ytdlnis.ui.downloads.DownloadQueueActivity import com.deniscerri.ytdlnis.ui.more.downloadLogs.DownloadLogListActivity import com.deniscerri.ytdlnis.ui.more.settings.SettingsActivity +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import java.util.ArrayList import kotlin.system.exitProcess class MoreFragment : Fragment() { private lateinit var mainSharedPreferences: SharedPreferences + private lateinit var mainSharedPreferencesEditor: SharedPreferences.Editor private lateinit var terminal: TextView private lateinit var logs: TextView private lateinit var commandTemplates: TextView @@ -42,6 +47,7 @@ class MoreFragment : Fragment() { super.onViewCreated(view, savedInstanceState) mainSharedPreferences = requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE) + mainSharedPreferencesEditor = mainSharedPreferences.edit() terminal = view.findViewById(R.id.terminal) logs = view.findViewById(R.id.logs) commandTemplates = view.findViewById(R.id.command_templates) @@ -79,7 +85,33 @@ class MoreFragment : Fragment() { } terminateApp.setOnClickListener { - exitProcess(0) + if (mainSharedPreferences.getBoolean("ask_terminate_app", true)){ + var doNotShowAgain = false + val terminateDialog = MaterialAlertDialogBuilder(requireContext()) + terminateDialog.setTitle(getString(R.string.confirm_delete_history)) + val dialogView = layoutInflater.inflate(R.layout.dialog_terminate_app, null) + val checkbox = dialogView.findViewById(R.id.doNotShowAgain) + terminateDialog.setView(dialogView) + + + + checkbox.setOnCheckedChangeListener { compoundButton, b -> + doNotShowAgain = compoundButton.isChecked + } + + terminateDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() } + terminateDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int -> + if (doNotShowAgain){ + mainSharedPreferencesEditor.putBoolean("ask_terminate_app", false) + mainSharedPreferencesEditor.commit() + } + exitProcess(0) + } + terminateDialog.show() + }else{ + exitProcess(0) + } + } settings.setOnClickListener { diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/TerminalActivity.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/TerminalActivity.kt index 2d14ceb9..dd9b894e 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/TerminalActivity.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/TerminalActivity.kt @@ -7,22 +7,40 @@ import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.os.Bundle +import android.os.Looper +import android.text.format.DateFormat import android.util.Log import android.view.View -import android.widget.EditText -import android.widget.ScrollView -import android.widget.TextView -import android.widget.Toast +import android.view.ViewGroup +import android.view.Window +import android.widget.* import androidx.appcompat.app.AppCompatActivity +import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.app.ActivityCompat import androidx.core.app.NotificationManagerCompat +import androidx.core.content.ContextCompat +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope +import androidx.work.Data +import androidx.work.ExistingWorkPolicy +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.WorkManager +import com.deniscerri.ytdlnis.App import com.deniscerri.ytdlnis.R +import com.deniscerri.ytdlnis.database.repository.DownloadRepository +import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel +import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.util.FileUtil import com.deniscerri.ytdlnis.util.NotificationUtil import com.deniscerri.ytdlnis.work.DownloadWorker import com.google.android.material.appbar.MaterialToolbar +import com.google.android.material.bottomappbar.BottomAppBar +import com.google.android.material.bottomsheet.BottomSheetDialog +import com.google.android.material.button.MaterialButton +import com.google.android.material.chip.Chip +import com.google.android.material.chip.ChipGroup import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton +import com.google.android.material.progressindicator.LinearProgressIndicator import com.yausername.youtubedl_android.YoutubeDL import com.yausername.youtubedl_android.YoutubeDLRequest import io.reactivex.Observable @@ -30,8 +48,14 @@ import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext import java.io.File +import java.text.SimpleDateFormat +import java.util.* +import java.util.concurrent.TimeUnit class TerminalActivity : AppCompatActivity() { @@ -40,8 +64,9 @@ class TerminalActivity : AppCompatActivity() { private var output: TextView? = null private var input: EditText? = null private var fab: ExtendedFloatingActionButton? = null - private var cancelFab: ExtendedFloatingActionButton? = null private var scrollView: ScrollView? = null + private lateinit var bottomAppBar: BottomAppBar + private lateinit var commandTemplateViewModel: CommandTemplateViewModel var context: Context? = null public override fun onCreate(savedInstanceState: Bundle?) { @@ -51,23 +76,38 @@ class TerminalActivity : AppCompatActivity() { scrollView = findViewById(R.id.custom_command_scrollview) topAppBar = findViewById(R.id.custom_command_toolbar) topAppBar!!.setNavigationOnClickListener { onBackPressedDispatcher.onBackPressed() } + + commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java] + + + bottomAppBar = findViewById(R.id.bottomAppBar) + bottomAppBar.setOnMenuItemClickListener { + when(it.itemId){ + R.id.command_templates -> showCommandTemplates() + R.id.shortcuts -> showShortcuts() + else -> {} + } + true + } + output = findViewById(R.id.custom_command_output) output!!.setTextIsSelectable(true) input = findViewById(R.id.command_edittext) input!!.requestFocus() fab = findViewById(R.id.command_fab) fab!!.setOnClickListener { - input!!.visibility = View.GONE - output!!.text = "${output!!.text}\n~ $ ${input!!.text}\n" - swapFabs() - startDownload( - input!!.text.toString() - ) - } - cancelFab = findViewById(R.id.cancel_command_fab) - cancelFab!!.setOnClickListener { - cancelDownload() - input!!.visibility = View.VISIBLE + if (fab!!.text == getString(R.string.run_command)){ + input!!.visibility = View.GONE + output!!.text = "${output!!.text}\n~ $ ${input!!.text}\n" + showCancelFab() + startDownload( + input!!.text.toString() + ) + }else { + cancelDownload() + input!!.visibility = View.VISIBLE + hideCancelFab() + } } notificationUtil = NotificationUtil(this) handleIntent(intent) @@ -90,13 +130,73 @@ class TerminalActivity : AppCompatActivity() { } - private fun swapFabs() { - val cancel = cancelFab!!.visibility - val start = fab!!.visibility - cancelFab!!.visibility = start - fab!!.visibility = cancel + private fun hideCancelFab() { + fab!!.text = getString(R.string.run_command) + fab!!.setIconResource(R.drawable.ic_baseline_keyboard_arrow_right_24) + } + private fun showCancelFab() { + fab!!.text = getString(R.string.cancel_download) + fab!!.setIconResource(R.drawable.ic_cancel) } + private fun showCommandTemplates(){ + lifecycleScope.launch { + val bottomSheet = BottomSheetDialog(this@TerminalActivity) + bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE) + bottomSheet.setContentView(R.layout.command_template_list) + + val linearLayout = bottomSheet.findViewById(R.id.command_list_linear_layout) + val list = withContext(Dispatchers.IO){ + commandTemplateViewModel.getAll() + } + + linearLayout!!.removeAllViews() + list.forEach {template -> + val item = layoutInflater.inflate(R.layout.command_template_item, linearLayout, false) as ConstraintLayout + item.findViewById(R.id.title).text = template.title + item.findViewById(R.id.content).text = template.content + item.setOnClickListener { + input!!.text.insert(input!!.selectionStart, template.content + " ") + } + linearLayout.addView(item) + } + + bottomSheet.show() + bottomSheet.window!!.setLayout( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + } + } + + private fun showShortcuts() { + lifecycleScope.launch { + val bottomSheet = BottomSheetDialog(this@TerminalActivity) + bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE) + bottomSheet.setContentView(R.layout.template_shortcuts_list) + + val chipGroup = bottomSheet.findViewById(R.id.shortcutsChipGroup) + val shortcutList = withContext(Dispatchers.IO){ + commandTemplateViewModel.getAllShortcuts() + } + + chipGroup!!.removeAllViews() + shortcutList.forEach {shortcut -> + val chip = layoutInflater.inflate(R.layout.suggestion_chip, chipGroup, false) as Chip + chip.text = shortcut.content + chip.setOnClickListener { + input!!.text.insert(input!!.selectionStart, shortcut.content + " ") + } + chipGroup.addView(chip) + } + + bottomSheet.show() + bottomSheet.window!!.setLayout( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + } + } private fun startDownload(command: String?) { val cmd = if (command!!.contains("yt-dlp")) command.replace("yt-dlp", "") @@ -137,10 +237,10 @@ class TerminalActivity : AppCompatActivity() { }.absolutePath ) - //request.addOption("-P", tempFileDir.absolutePath) - cancelFab!!.visibility = View.VISIBLE - fab!!.visibility = View.GONE + request.addOption("-P", tempFileDir.absolutePath) + + showCancelFab() val disposable: Disposable = Observable.fromCallable { try{ YoutubeDL.getInstance().execute(request, downloadID.toString()){ progress, _, line -> @@ -167,8 +267,7 @@ class TerminalActivity : AppCompatActivity() { input!!.visibility = View.VISIBLE - cancelFab!!.visibility = View.GONE - fab!!.visibility = View.VISIBLE + hideCancelFab() } } @@ -181,15 +280,7 @@ class TerminalActivity : AppCompatActivity() { input!!.setText("yt-dlp ") input!!.visibility = View.VISIBLE - cancelFab!!.visibility = View.GONE - fab!!.visibility = View.VISIBLE - - //move file from internal to set download directory - try { - moveFile(tempFileDir.absoluteFile, getString(R.string.command_path)){ } - }catch (e: Exception){ - Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show() - } + hideCancelFab() notificationUtil.cancelDownloadNotification(downloadID) }) { e -> e.printStackTrace() @@ -198,32 +289,19 @@ class TerminalActivity : AppCompatActivity() { input!!.setText("yt-dlp ") input!!.visibility = View.VISIBLE - cancelFab!!.visibility = View.GONE - fab!!.visibility = View.VISIBLE + hideCancelFab(); - tempFileDir.delete() Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show() - Log.e(DownloadWorker.TAG, context?.getString(R.string.failed_download), e) notificationUtil.cancelDownloadNotification(downloadID) } compositeDisposable.add(disposable) - - } - @Throws(Exception::class) - private fun moveFile(originDir: File, downLocation: String, progress: (progress: Int) -> Unit){ - val fileUtil = FileUtil() - fileUtil.moveFile(originDir, context!!, downLocation){ p -> - progress(p) - } } private fun cancelDownload() { - lifecycleScope.launch { - compositeDisposable.dispose() - YoutubeDL.getInstance().destroyProcessById(downloadID.toString()) - notificationUtil.cancelDownloadNotification(downloadID) - } + YoutubeDL.getInstance().destroyProcessById(downloadID.toString()) + WorkManager.getInstance(this).cancelUniqueWork(downloadID.toString()) + notificationUtil.cancelDownloadNotification(downloadID) } companion object { diff --git a/app/src/main/res/layout/activity_terminal.xml b/app/src/main/res/layout/activity_terminal.xml index dbf8d469..eb80ba96 100644 --- a/app/src/main/res/layout/activity_terminal.xml +++ b/app/src/main/res/layout/activity_terminal.xml @@ -6,64 +6,6 @@ android:layout_height="match_parent" tools:context="com.deniscerri.ytdlnis.ui.more.TerminalActivity"> - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/command_template_list.xml b/app/src/main/res/layout/command_template_list.xml new file mode 100644 index 00000000..6e54e834 --- /dev/null +++ b/app/src/main/res/layout/command_template_list.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_terminate_app.xml b/app/src/main/res/layout/dialog_terminate_app.xml new file mode 100644 index 00000000..4b23ae94 --- /dev/null +++ b/app/src/main/res/layout/dialog_terminate_app.xml @@ -0,0 +1,21 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_more.xml b/app/src/main/res/layout/fragment_more.xml index 24333c35..f03801d6 100644 --- a/app/src/main/res/layout/fragment_more.xml +++ b/app/src/main/res/layout/fragment_more.xml @@ -26,6 +26,9 @@ android:layout_height="wrap_content" android:text="@string/terminal" android:textStyle="bold" + android:clickable="true" + android:focusable="true" + android:background="?android:attr/selectableItemBackground" android:paddingHorizontal="15dp" android:paddingVertical="15dp" android:drawablePadding="35dp" @@ -38,6 +41,9 @@ 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" @@ -54,6 +60,9 @@ android:paddingVertical="15dp" android:text="@string/command_templates" android:textStyle="bold" + android:clickable="true" + android:focusable="true" + android:background="?android:attr/selectableItemBackground" android:paddingHorizontal="15dp" app:drawableLeftCompat="@drawable/ic_baseline_keyboard_arrow_right_24" /> @@ -66,6 +75,9 @@ android:paddingVertical="15dp" android:text="@string/download_queue" android:textStyle="bold" + android:clickable="true" + android:focusable="true" + android:background="?android:attr/selectableItemBackground" android:paddingHorizontal="15dp" app:drawableLeftCompat="@drawable/ic_concurrent_downloads" /> @@ -75,6 +87,9 @@ android:layout_height="wrap_content" android:drawablePadding="35dp" android:textSize="16sp" + android:clickable="true" + android:focusable="true" + android:background="?android:attr/selectableItemBackground" android:paddingVertical="15dp" android:text="@string/kill_app" android:textStyle="bold" @@ -93,6 +108,9 @@ android:paddingVertical="15dp" android:text="@string/settings" android:textStyle="bold" + android:clickable="true" + android:focusable="true" + android:background="?android:attr/selectableItemBackground" android:paddingHorizontal="15dp" app:drawableLeftCompat="@drawable/ic_settings" /> diff --git a/app/src/main/res/layout/template_shortcuts_list.xml b/app/src/main/res/layout/template_shortcuts_list.xml new file mode 100644 index 00000000..26800567 --- /dev/null +++ b/app/src/main/res/layout/template_shortcuts_list.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/terminal_menu.xml b/app/src/main/res/menu/terminal_menu.xml new file mode 100644 index 00000000..afce27b1 --- /dev/null +++ b/app/src/main/res/menu/terminal_menu.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6beb7417..9ff9f3e4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -184,4 +184,6 @@ Restrict Filenames Terminate App + + Don\'t ask again \ No newline at end of file