implemented custom command

This commit is contained in:
Denis Çerri 2023-03-04 12:23:59 +01:00
parent 4d859ee8d4
commit 24fa1d1509
No known key found for this signature in database
GPG key ID: 95C43D517D830350
34 changed files with 1050 additions and 78 deletions

View file

@ -320,7 +320,7 @@
<PersistentState>
<option name="values">
<map>
<entry key="url" value="file:/$USER_HOME$/AppData/Local/Android/Sdk/icons/material/materialicons/refresh/baseline_refresh_24.xml" />
<entry key="url" value="file:/$USER_HOME$/AppData/Local/Android/Sdk/icons/material/materialicons/content_paste/baseline_content_paste_24.xml" />
</map>
</option>
</PersistentState>
@ -330,7 +330,7 @@
</option>
<option name="values">
<map>
<entry key="outputName" value="ic_refresh" />
<entry key="outputName" value="ic_clipboard" />
<entry key="sourceFile" value="C:\Users\denis\Desktop\adaptiveproduct_youtube_foreground_color_108 (1).svg" />
</map>
</option>

View file

@ -125,7 +125,7 @@ dependencies {
implementation "androidx.appcompat:appcompat:$appCompatVer"
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
implementation 'com.google.android.material:material:1.8.0'
implementation 'com.google.android.material:material:1.7.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.core:core:1.9.0'
implementation 'androidx.recyclerview:recyclerview:1.2.1'

View file

@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "2a124f7c327413ab152e18a9e4a66ef7",
"identityHash": "6d6dea309a9207aaaedcc2a745ae2a2a",
"entities": [
{
"tableName": "results",
@ -339,12 +339,38 @@
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "templateShortcuts",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `content` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "content",
"columnName": "content",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '2a124f7c327413ab152e18a9e4a66ef7')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6d6dea309a9207aaaedcc2a745ae2a2a')"
]
}
}

View file

@ -95,7 +95,7 @@
</intent-filter>
</activity>
<activity
android:name=".ui.more.CustomCommandActivity"
android:name=".ui.more.TerminalActivity"
android:configChanges="layoutDirection|locale"
android:theme="@style/AppDefaultTheme"
android:parentActivityName=".MainActivity"
@ -111,8 +111,19 @@
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
<receiver android:name=".receiver.CancelDownloadNotificationReceiver" />
<activity
android:name=".ui.more.CommandTemplatesActivity"
android:configChanges="layoutDirection|locale"
android:parentActivityName=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="ytdlnis.CommandTemplatesActivity" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<receiver android:name=".receiver.CancelDownloadNotificationReceiver" />
<service
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
android:enabled="false"

View file

@ -0,0 +1,77 @@
package com.deniscerri.ytdlnis.adapter
import android.app.Activity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.CommandTemplate
class TemplatesAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter<CommandTemplate?, TemplatesAdapter.ViewHolder>(AsyncDifferConfig.Builder(DIFF_CALLBACK).build()) {
private val onItemClickListener: OnItemClickListener
private val activity: Activity
init {
this.onItemClickListener = onItemClickListener
this.activity = activity
}
class ViewHolder(itemView: View, onItemClickListener: OnItemClickListener?) : RecyclerView.ViewHolder(itemView) {
val item: ConstraintLayout
init {
item = itemView.findViewById(R.id.command_template_item_constraint)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val cardView = LayoutInflater.from(parent.context)
.inflate(R.layout.command_template_item, parent, false)
return ViewHolder(cardView, onItemClickListener)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = getItem(position)
val card = holder.item
val title = card.findViewById<TextView>(R.id.title)
title.text = item?.title
val content = card.findViewById<TextView>(R.id.content)
content.text = item?.content
val check = card.findViewById<Button>(R.id.check)
check.setOnClickListener {
onItemClickListener.onSelected(item!!)
}
title.setOnClickListener {
onItemClickListener.onItemClick(item!!)
}
}
interface OnItemClickListener {
fun onItemClick(commandTemplate: CommandTemplate)
fun onSelected(commandTemplate: CommandTemplate)
}
companion object {
private val DIFF_CALLBACK: DiffUtil.ItemCallback<CommandTemplate> = object : DiffUtil.ItemCallback<CommandTemplate>() {
override fun areItemsTheSame(oldItem: CommandTemplate, newItem: CommandTemplate): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: CommandTemplate, newItem: CommandTemplate): Boolean {
return oldItem.content == newItem.content
}
}
}
}

View file

@ -10,7 +10,7 @@ import com.deniscerri.ytdlnis.database.models.*
@TypeConverters(Converters::class)
@Database(
entities = [ResultItem::class, HistoryItem::class, DownloadItem::class, CommandTemplate::class, SearchHistoryItem::class],
entities = [ResultItem::class, HistoryItem::class, DownloadItem::class, CommandTemplate::class, SearchHistoryItem::class, TemplateShortcut::class],
version = 1,
autoMigrations = []
)

View file

@ -5,18 +5,27 @@ import androidx.room.*
import com.deniscerri.ytdlnis.database.models.CommandTemplate
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.HistoryItem
import com.deniscerri.ytdlnis.database.models.TemplateShortcut
import com.deniscerri.ytdlnis.util.FileUtil
@Dao
interface CommandTemplateDao {
@Query("SELECT * FROM commandTemplates")
@Query("SELECT * FROM commandTemplates ORDER BY id DESC")
fun getAllTemplates() : List<CommandTemplate>
@Query("SELECT * FROM commandTemplates ORDER BY id DESC")
fun getAllTemplatesLiveData() : LiveData<List<CommandTemplate>>
@Query("SELECT * FROM templateShortcuts ORDER BY id DESC")
fun getAllShortcuts() : LiveData<List<TemplateShortcut>>
@Query("SELECT COUNT(id) FROM commandTemplates")
fun getTotalNumber() : Int
@Query("SELECT * FROM commandTemplates WHERE id=:id LIMIT 1")
fun getTemplate(id: Long) : CommandTemplate
@Query("SELECT * FROM commandTemplates LIMIT 1")
@Query("SELECT * FROM commandTemplates ORDER BY id DESC LIMIT 1")
fun getFirst() : CommandTemplate
@Insert(onConflict = OnConflictStrategy.IGNORE)
@ -25,8 +34,14 @@ interface CommandTemplateDao {
@Query("DELETE FROM commandTemplates")
suspend fun deleteAll()
@Delete
suspend fun delete(item: CommandTemplate)
@Query("DELETE FROM commandTemplates WHERE id=:itemId")
suspend fun delete(itemId: Long)
@Query("INSERT INTO templateShortcuts(content) VALUES(:content)")
suspend fun insertShortcut(content: String)
@Query("DELETE FROM templateShortcuts WHERE id=:itemId")
suspend fun deleteShortcut(itemId: Long)
@Update(onConflict = OnConflictStrategy.REPLACE)
suspend fun update(item: CommandTemplate)

View file

@ -0,0 +1,12 @@
package com.deniscerri.ytdlnis.database.models
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "templateShortcuts")
data class TemplateShortcut(
@PrimaryKey(autoGenerate = true)
var id: Long,
val content: String
)

View file

@ -0,0 +1,45 @@
package com.deniscerri.ytdlnis.database.repository
import androidx.lifecycle.LiveData
import com.deniscerri.ytdlnis.database.dao.CommandTemplateDao
import com.deniscerri.ytdlnis.database.models.CommandTemplate
import com.deniscerri.ytdlnis.database.models.TemplateShortcut
class CommandTemplateRepository(private val commandDao: CommandTemplateDao) {
val items : LiveData<List<CommandTemplate>> = commandDao.getAllTemplatesLiveData()
val shortcuts : LiveData<List<TemplateShortcut>> = commandDao.getAllShortcuts()
fun getAll() : List<CommandTemplate> {
return commandDao.getAllTemplates();
}
fun getItem(id: Long) : CommandTemplate {
return commandDao.getTemplate(id)
}
suspend fun insert(item: CommandTemplate){
commandDao.insert(item)
}
suspend fun delete(item: CommandTemplate){
commandDao.delete(item.id)
}
suspend fun insertShortcut(item: TemplateShortcut){
commandDao.insertShortcut(item.content)
}
suspend fun deleteShortcut(item: TemplateShortcut){
commandDao.deleteShortcut(item.id)
}
suspend fun deleteAll(){
commandDao.deleteAll()
}
suspend fun update(item: CommandTemplate){
commandDao.update(item)
}
}

View file

@ -0,0 +1,69 @@
package com.deniscerri.ytdlnis.database.viewmodel
import android.app.Application
import androidx.lifecycle.*
import com.deniscerri.ytdlnis.database.DBManager
import com.deniscerri.ytdlnis.database.models.CommandTemplate
import com.deniscerri.ytdlnis.database.models.HistoryItem
import com.deniscerri.ytdlnis.database.models.TemplateShortcut
import com.deniscerri.ytdlnis.database.repository.CommandTemplateRepository
import com.deniscerri.ytdlnis.database.repository.HistoryRepository
import com.deniscerri.ytdlnis.database.repository.HistoryRepository.HistorySort
import com.deniscerri.ytdlnis.database.repository.HistoryRepository.HistorySortType
import com.deniscerri.ytdlnis.util.FileUtil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class CommandTemplateViewModel(application: Application) : AndroidViewModel(application) {
private val repository: CommandTemplateRepository
val items: LiveData<List<CommandTemplate>>
val shortcuts : LiveData<List<TemplateShortcut>>
init {
val dao = DBManager.getInstance(application).commandTemplateDao
repository = CommandTemplateRepository(dao)
items = repository.items
shortcuts = repository.shortcuts
}
fun getTemplate(itemId: Long): CommandTemplate {
return repository.getItem(itemId)
}
fun getAll(): List<CommandTemplate> {
return repository.getAll()
}
fun insert(item: CommandTemplate) = viewModelScope.launch(Dispatchers.IO) {
repository.insert(item)
}
fun delete(item: CommandTemplate) = viewModelScope.launch(Dispatchers.IO) {
repository.delete(item)
}
fun insertShortcut(item: TemplateShortcut) = viewModelScope.launch(Dispatchers.IO) {
repository.insertShortcut(item)
}
fun deleteShortcut(item: TemplateShortcut) = viewModelScope.launch(Dispatchers.IO) {
repository.deleteShortcut(item)
}
fun deleteAll() = viewModelScope.launch(Dispatchers.IO) {
repository.deleteAll()
}
fun update(item: CommandTemplate) = viewModelScope.launch(Dispatchers.IO) {
repository.update(item)
}
fun importFromClipboard() = viewModelScope.launch(Dispatchers.IO) {
}
fun exportToClipboard() {
}
}

View file

@ -7,10 +7,16 @@ import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.LinearLayout
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.viewpager2.widget.ViewPager2
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.DBManager
import com.deniscerri.ytdlnis.database.dao.CommandTemplateDao
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
@ -24,6 +30,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel.Type
import kotlinx.coroutines.withContext
class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, private val type: Type) : BottomSheetDialogFragment() {
private lateinit var tabLayout: TabLayout
@ -31,11 +38,13 @@ class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, pri
private lateinit var fragmentAdapter : DownloadFragmentAdapter
private lateinit var downloadViewModel: DownloadViewModel
private lateinit var resultViewModel: ResultViewModel
private lateinit var commandTemplateDao: CommandTemplateDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
commandTemplateDao = DBManager.getInstance(requireContext()).commandTemplateDao
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -54,11 +63,27 @@ class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, pri
tabLayout = view.findViewById(R.id.download_tablayout)
viewPager2 = view.findViewById(R.id.download_viewpager)
val fragments = mutableListOf<Fragment>(DownloadAudioFragment(resultItem), DownloadVideoFragment(resultItem))
lifecycleScope.launch{
withContext(Dispatchers.IO){
val nr = commandTemplateDao.getTotalNumber()
if(nr > 0){
fragments.add(DownloadCommandFragment(resultItem))
}else{
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(2)?.isEnabled = false
}
}
}
val fragmentManager = parentFragmentManager
fragmentAdapter = DownloadFragmentAdapter(
resultItem,
fragmentManager,
lifecycle
lifecycle,
fragments
)
viewPager2.adapter = fragmentAdapter
viewPager2.isSaveFromParentEnabled = false
@ -78,7 +103,6 @@ class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, pri
}
}
tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
viewPager2.currentItem = tab!!.position
@ -123,6 +147,7 @@ class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, pri
downloadViewModel.updateDownload(item)
dismiss()
}
}
override fun onCancel(dialog: DialogInterface) {

View file

@ -10,15 +10,20 @@ import android.util.DisplayMetrics
import android.view.Display
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Adapter
import android.widget.Button
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.get
import androidx.core.view.size
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.DBManager
import com.deniscerri.ytdlnis.database.dao.CommandTemplateDao
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
@ -34,6 +39,9 @@ import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.tabs.TabLayout
import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.TimeFormat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.*
@ -43,10 +51,12 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
private lateinit var fragmentAdapter : DownloadFragmentAdapter
private lateinit var downloadViewModel: DownloadViewModel
private lateinit var behavior: BottomSheetBehavior<View>
private lateinit var commandTemplateDao : CommandTemplateDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
commandTemplateDao = DBManager.getInstance(requireContext()).commandTemplateDao
}
@SuppressLint("RestrictedApi")
@ -71,12 +81,29 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
overScrollMode = View.OVER_SCROLL_NEVER
}
val fragments = mutableListOf<Fragment>(DownloadAudioFragment(resultItem), DownloadVideoFragment(resultItem))
lifecycleScope.launch{
withContext(Dispatchers.IO){
val nr = commandTemplateDao.getTotalNumber()
if(nr > 0){
fragments.add(DownloadCommandFragment(resultItem))
}else{
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(2)?.isEnabled = false
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(2)?.alpha = 0.3f
}
}
}
val fragmentManager = parentFragmentManager
fragmentAdapter = DownloadFragmentAdapter(
resultItem,
fragmentManager,
lifecycle
lifecycle,
fragments
)
viewPager2.adapter = fragmentAdapter
viewPager2.isSaveFromParentEnabled = false

View file

@ -1,6 +1,7 @@
package com.deniscerri.ytdlnis.ui.downloadcard
import android.app.Activity
import android.content.ClipboardManager
import android.content.Intent
import android.os.Bundle
import android.text.Editable
@ -13,33 +14,35 @@ import android.widget.ArrayAdapter
import android.widget.AutoCompleteTextView
import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.content.res.AppCompatResources
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.DBManager
import com.deniscerri.ytdlnis.database.dao.CommandTemplateDao
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.Format
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.UiUtil
import com.google.android.material.chip.Chip
import com.google.android.material.textfield.TextInputLayout
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class DownloadCommandFragment(private val resultItem: ResultItem) : Fragment() {
private var _binding : FragmentHomeBinding? = null
private var fragmentView: View? = null
private var activity: Activity? = null
private lateinit var downloadViewModel : DownloadViewModel
private lateinit var commandTemplateViewModel : CommandTemplateViewModel
private lateinit var fileUtil : FileUtil
private lateinit var uiUtil : UiUtil
private lateinit var saveDir : TextInputLayout
private lateinit var commandTemplateDao : CommandTemplateDao
lateinit var downloadItem: DownloadItem
@ -52,9 +55,10 @@ class DownloadCommandFragment(private val resultItem: ResultItem) : Fragment() {
fragmentView = inflater.inflate(R.layout.fragment_download_command, container, false)
activity = getActivity()
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java]
downloadItem = downloadViewModel.createDownloadItemFromResult(resultItem, DownloadViewModel.Type.command)
fileUtil = FileUtil()
commandTemplateDao = DBManager.getInstance(requireContext()).commandTemplateDao
uiUtil = UiUtil(fileUtil)
return fragmentView
}
@ -62,12 +66,13 @@ class DownloadCommandFragment(private val resultItem: ResultItem) : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycleScope.launch {
val sharedPreferences = requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
try {
val commands = commandTemplateDao.getAllTemplates()
val id = sharedPreferences.getLong("commandTemplate", commands[0].id)
val chosenCommand = commands.find { it.id == id }
val templates = withContext(Dispatchers.IO){
commandTemplateViewModel.getAll()
}
val id = sharedPreferences.getLong("commandTemplate", templates[0].id)
val chosenCommand = templates.find { it.id == id }
downloadItem.format = Format(
chosenCommand!!.title,
"",
@ -78,7 +83,40 @@ class DownloadCommandFragment(private val resultItem: ResultItem) : Fragment() {
chosenCommand.content
)
val templates = commandTemplateDao.getAllTemplates()
val chosenCommandView = view.findViewById<TextInputLayout>(R.id.content)
chosenCommandView.editText!!.setText(chosenCommand.content)
chosenCommandView.endIconDrawable = AppCompatResources.getDrawable(requireContext(), R.drawable.ic_delete_all)
chosenCommandView.editText!!.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun afterTextChanged(p0: Editable?) {
if (p0!!.isNotEmpty()){
chosenCommandView.endIconDrawable = AppCompatResources.getDrawable(requireContext(), R.drawable.ic_delete_all)
}else{
chosenCommandView.endIconDrawable = AppCompatResources.getDrawable(requireContext(), R.drawable.ic_clipboard)
}
downloadItem.format = Format(
"Custom",
"",
"",
"",
"",
0,
p0.toString()
)
}
})
chosenCommandView.setEndIconOnClickListener {
if(chosenCommandView.editText!!.text.isEmpty()){
val clipboard: ClipboardManager =
requireContext().getSystemService(AppCompatActivity.CLIPBOARD_SERVICE) as ClipboardManager
chosenCommandView.editText!!.setText(clipboard.primaryClip?.getItemAt(0)?.text)
}else{
chosenCommandView.editText!!.setText("")
}
}
val templateTitles = templates.map {it.title}
val commandTemplates = view.findViewById<TextInputLayout>(R.id.template)
@ -97,12 +135,18 @@ class DownloadCommandFragment(private val resultItem: ResultItem) : Fragment() {
(commandTemplates!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
// TODO
chosenCommandView.editText!!.setText(templates[index].content)
downloadItem.format = Format(
templates[index].title,
"",
"",
"",
"",
0,
templates[index].content
)
}
val templateContent = view.findViewById<TextView>(R.id.template_content_textview)
templateContent.text = downloadItem.format.format_note
saveDir = view.findViewById(R.id.outputPath)
val downloadPath = sharedPreferences.getString(
"command_path",
@ -122,6 +166,13 @@ class DownloadCommandFragment(private val resultItem: ResultItem) : Fragment() {
commandPathResultLauncher.launch(intent)
}
val newTemplate : Chip = view.findViewById(R.id.newTemplate)
newTemplate.setOnClickListener {
uiUtil.showCreationSheet(requireActivity(), viewLifecycleOwner, commandTemplateViewModel) {
}
}
//
// val embedSubs = view.findViewById<Chip>(R.id.embed_subtitles)
// embedSubs!!.isChecked = embedSubs.isChecked

View file

@ -13,23 +13,16 @@ import com.google.gson.Gson
class DownloadFragmentAdapter (
private val resultItem: ResultItem,
fragmentManager : FragmentManager,
lifecycle : Lifecycle
lifecycle : Lifecycle,
private val fragments: List<Fragment>
) : FragmentStateAdapter(fragmentManager, lifecycle) {
override fun getItemCount(): Int {
return 3
return fragments.size
}
override fun createFragment(position: Int): Fragment {
when (position) {
0 -> {
return DownloadAudioFragment(resultItem)
}
1 -> {
return DownloadVideoFragment(resultItem)
}
}
return DownloadCommandFragment(resultItem)
return fragments[position]
}
}

View file

@ -0,0 +1,101 @@
package com.deniscerri.ytdlnis.ui.more
import android.content.ClipboardManager
import android.content.Context
import android.os.*
import android.text.Editable
import android.text.TextWatcher
import android.view.MenuItem
import android.view.ViewGroup
import android.view.Window
import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.content.res.AppCompatResources
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.adapter.TemplatesAdapter
import com.deniscerri.ytdlnis.database.models.CommandTemplate
import com.deniscerri.ytdlnis.database.models.TemplateShortcut
import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.UiUtil
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import com.google.android.material.textfield.TextInputLayout
class CommandTemplatesActivity : AppCompatActivity(), TemplatesAdapter.OnItemClickListener {
private lateinit var recyclerView: RecyclerView
private lateinit var templatesAdapter: TemplatesAdapter
private lateinit var topAppBar: MaterialToolbar
private lateinit var commandTemplateViewModel: CommandTemplateViewModel
private lateinit var uiUtil: UiUtil
var context: Context? = null
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_command_templates)
context = baseContext
topAppBar = findViewById(R.id.logs_toolbar)
topAppBar.setNavigationOnClickListener { onBackPressedDispatcher.onBackPressed() }
templatesAdapter =
TemplatesAdapter(
this,
this@CommandTemplatesActivity
)
recyclerView = findViewById(R.id.template_recyclerview)
recyclerView.layoutManager = LinearLayoutManager(context)
recyclerView.adapter = templatesAdapter
uiUtil = UiUtil(FileUtil())
commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java]
commandTemplateViewModel.items.observe(this) {
templatesAdapter.submitList(it)
}
initMenu()
initChips()
}
private fun initMenu() {
topAppBar.setOnMenuItemClickListener { m: MenuItem ->
val itemId = m.itemId
if (itemId == R.id.export_clipboard) {
commandTemplateViewModel.exportToClipboard()
}else if (itemId == R.id.import_clipboard){
commandTemplateViewModel.importFromClipboard()
}
true
}
}
private fun initChips() {
val new = findViewById<Chip>(R.id.newTemplate)
new.setOnClickListener {
uiUtil.showCreationSheet(this@CommandTemplatesActivity, this, commandTemplateViewModel) {}
}
val shortcuts = findViewById<Chip>(R.id.shortcuts)
shortcuts.setOnClickListener {
uiUtil.showShortcutsSheet(this@CommandTemplatesActivity,this, commandTemplateViewModel)
}
}
companion object {
private const val TAG = "DownloadLogActivity"
}
override fun onItemClick(commandTemplate: CommandTemplate) {
TODO("Not yet implemented")
}
override fun onSelected(commandTemplate: CommandTemplate) {
TODO("Not yet implemented")
}
}

View file

@ -35,7 +35,7 @@ import java.io.File
import java.util.regex.Pattern
class CustomCommandActivity : AppCompatActivity() {
class TerminalActivity : AppCompatActivity() {
private var topAppBar: MaterialToolbar? = null
private lateinit var notificationUtil: NotificationUtil
private var output: TextView? = null
@ -47,7 +47,7 @@ class CustomCommandActivity : AppCompatActivity() {
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_custom_command)
setContentView(R.layout.activity_terminal)
context = baseContext
scrollView = findViewById(R.id.custom_command_scrollview)
topAppBar = findViewById(R.id.custom_command_toolbar)
@ -107,7 +107,7 @@ class CustomCommandActivity : AppCompatActivity() {
downloadID = System.currentTimeMillis().toInt()
val theIntent = Intent(this, CustomCommandActivity::class.java)
val theIntent = Intent(this, TerminalActivity::class.java)
val pendingIntent = PendingIntent.getActivity(this, 0, theIntent, PendingIntent.FLAG_IMMUTABLE)
val commandNotification: Notification =

View file

@ -1,11 +1,30 @@
package com.deniscerri.ytdlnis.util
import android.app.Activity
import android.content.ClipboardManager
import android.content.Context
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.content.res.AppCompatResources
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat.getSystemService
import androidx.lifecycle.LifecycleOwner
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.CommandTemplate
import com.deniscerri.ytdlnis.database.models.Format
import com.deniscerri.ytdlnis.database.models.TemplateShortcut
import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import com.google.android.material.textfield.TextInputLayout
class UiUtil(private val fileUtil: FileUtil) {
fun populateFormatCard(formatCard : ConstraintLayout, chosenFormat: Format){
@ -29,4 +48,122 @@ class UiUtil(private val fileUtil: FileUtil) {
formatCard.findViewById<TextView>(R.id.file_size).text = fileUtil.convertFileSize(chosenFormat.filesize)
}
fun showCreationSheet(context: Activity, lifeCycle: LifecycleOwner, commandTemplateViewModel: CommandTemplateViewModel, newTemplate: (newTemplate: CommandTemplate) -> Unit){
val bottomSheet = BottomSheetDialog(context)
bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE)
bottomSheet.setContentView(R.layout.create_command_template)
val ok : Button = bottomSheet.findViewById(R.id.template_create)!!
val title : TextInputLayout = bottomSheet.findViewById(R.id.title)!!
val content : TextInputLayout = bottomSheet.findViewById(R.id.content)!!
val shortcutsChipGroup : ChipGroup = bottomSheet.findViewById(R.id.shortcutsChipGroup)!!
val editShortcuts : Button = bottomSheet.findViewById(R.id.edit_shortcuts)!!
ok.isEnabled = false
title.editText!!.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun afterTextChanged(p0: Editable?) {
ok.isEnabled =
!(title.editText!!.text.isEmpty() || content.editText!!.text.isEmpty())
}
})
content.editText!!.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun afterTextChanged(p0: Editable?) {
ok.isEnabled =
!(title.editText!!.text.isEmpty() || content.editText!!.text.isEmpty())
if (ok.isEnabled){
content.endIconDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_delete_all)
}else{
content.endIconDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_clipboard)
}
}
})
content.setEndIconOnClickListener {
if(content.editText!!.text.isEmpty()){
val clipboard: ClipboardManager =
context.getSystemService(AppCompatActivity.CLIPBOARD_SERVICE) as ClipboardManager
content.editText!!.setText(clipboard.primaryClip?.getItemAt(0)?.text)
}else{
content.editText!!.setText("")
}
}
commandTemplateViewModel.shortcuts.observe(lifeCycle){ it ->
shortcutsChipGroup.removeAllViews()
it.forEach {shortcut ->
val chip = context.layoutInflater.inflate(R.layout.suggestion_chip, shortcutsChipGroup, false) as Chip
chip.text = shortcut.content
chip.setOnClickListener {
content.editText!!.append(shortcut.content + " ")
}
shortcutsChipGroup.addView(chip)
}
}
editShortcuts.setOnClickListener {
showShortcutsSheet(context, lifeCycle, commandTemplateViewModel)
}
ok.setOnClickListener {
val t = CommandTemplate(0, title.editText!!.text.toString(), content.editText!!.text.toString())
commandTemplateViewModel.insert(t)
newTemplate(t)
bottomSheet.cancel()
}
bottomSheet.show()
bottomSheet.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}
fun showShortcutsSheet(context: Activity, lifeCycle: LifecycleOwner, commandTemplateViewModel: CommandTemplateViewModel){
val bottomSheet = BottomSheetDialog(context)
bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE)
bottomSheet.setContentView(R.layout.template_shortcuts)
val title : TextInputLayout = bottomSheet.findViewById(R.id.title)!!
val shortcutsChipGroup : ChipGroup = bottomSheet.findViewById(R.id.shortcutsChipGroup)!!
title.isEndIconVisible = false
title.editText!!.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun afterTextChanged(p0: Editable?) {
title.isEndIconVisible = p0!!.isNotEmpty()
}
})
title.setEndIconOnClickListener {
commandTemplateViewModel.insertShortcut(TemplateShortcut(0, title.editText!!.text.toString()))
title.editText!!.setText("")
title.isEndIconVisible = false
}
commandTemplateViewModel.shortcuts.observe(lifeCycle){ it ->
shortcutsChipGroup.removeAllViews()
it.forEach {shortcut ->
val chip = context.layoutInflater.inflate(R.layout.input_chip, shortcutsChipGroup, false) as Chip
chip.text = shortcut.content
chip.setOnClickListener{
commandTemplateViewModel.deleteShortcut(shortcut)
}
shortcutsChipGroup.addView(chip)
}
}
bottomSheet.show()
bottomSheet.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}
}

View file

@ -185,8 +185,7 @@ class DownloadWorker(
}
DownloadViewModel.Type.command -> {
val commandRegex = "\"([^\"]*)\"|(-\\S+)"
val command = commandTemplateDao.getTemplate(downloadItem.format.format_id.toLong())
val m = Pattern.compile(commandRegex).matcher(command.content)
val m = Pattern.compile(commandRegex).matcher(downloadItem.format.format_note)
while (m.find()) {
if (m.group(1) != null) {
request.addOption(m.group(1)!!)
@ -268,7 +267,7 @@ class DownloadWorker(
tempFileDir.delete()
handler.postDelayed({
Toast.makeText(context, it.message, Toast.LENGTH_SHORT).show()
Toast.makeText(context, it.message, Toast.LENGTH_LONG).show()
}, 1000)
Log.e(TAG, context.getString(R.string.failed_download), it)

View 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="?attr/colorAccent" android:pathData="M19,2h-4.18C14.4,0.84 13.3,0 12,0c-1.3,0 -2.4,0.84 -2.82,2L5,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,4c0,-1.1 -0.9,-2 -2,-2zM12,2c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM19,20L5,20L5,4h2v3h10L17,4h2v16z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="?attr/colorAccent" android:pathData="M21,11l-6,-6v5H8c-2.76,0 -5,2.24 -5,5v4h2v-4c0,-1.65 1.35,-3 3,-3h7v5L21,11z"/>
</vector>

View file

@ -4,12 +4,12 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.deniscerri.ytdlnis.ui.more.CustomCommandActivity">
tools:context="com.deniscerri.ytdlnis.ui.more.TerminalActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:liftOnScroll="false"
app:liftOnScroll="true"
android:background="@android:color/transparent"
android:fitsSystemWindows="true">
@ -17,6 +17,7 @@
android:id="@+id/logs_toolbar"
android:elevation="0dp"
app:title="@string/command_templates"
app:menu="@menu/command_templates_menu"
android:layout_width="match_parent"
app:navigationIcon="@drawable/ic_back"
android:layout_height="match_parent"/>
@ -24,15 +25,64 @@
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:id="@+id/template_recyclerview"
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
>
android:orientation="vertical"
android:layout_margin="10dp"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
</androidx.recyclerview.widget.RecyclerView>
<HorizontalScrollView
android:id="@+id/chips_command"
android:layout_width="wrap_content"
android:scrollbars="none"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/command_selection_chips"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.google.android.material.chip.ChipGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:selectionRequired="false"
app:singleSelection="false">
<com.google.android.material.chip.Chip
android:id="@+id/newTemplate"
style="@style/Widget.Material3.Chip.Assist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/new_template"
app:chipIcon="@drawable/ic_plus" />
<com.google.android.material.chip.Chip
android:id="@+id/shortcuts"
style="@style/Widget.Material3.Chip.Assist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/shortcuts"
app:chipIcon="@drawable/ic_shortcut" />
</com.google.android.material.chip.ChipGroup>
</LinearLayout>
</HorizontalScrollView>
<androidx.recyclerview.widget.RecyclerView
android:layout_below="@+id/chips_command"
android:id="@+id/template_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
>
</androidx.recyclerview.widget.RecyclerView>
</RelativeLayout>
<include layout="@layout/history_no_results"

View file

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.deniscerri.ytdlnis.ui.more.CustomCommandActivity">
tools:context="com.deniscerri.ytdlnis.ui.more.TerminalActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"

View file

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.deniscerri.ytdlnis.ui.more.CustomCommandActivity">
tools:context="com.deniscerri.ytdlnis.ui.more.TerminalActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"

View file

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.deniscerri.ytdlnis.ui.more.CustomCommandActivity">
tools:context="com.deniscerri.ytdlnis.ui.more.TerminalActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"

View file

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.deniscerri.ytdlnis.ui.more.CustomCommandActivity">
tools:context="com.deniscerri.ytdlnis.ui.more.TerminalActivity">
<FrameLayout
android:id="@+id/custom_command_frame_layout"

View file

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/command_template_item_constraint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="15dp">
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:textSize="15sp"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@+id/divider"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="15dp"
android:maxLines="2"
android:textSize="12sp"
app:layout_constraintEnd_toStartOf="@+id/divider"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title" />
<View
android:id="@+id/divider"
style="@style/Divider.Vertical"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/check"
app:layout_constraintStart_toEndOf="@+id/title"
app:layout_constraintTop_toTopOf="parent" />
<RadioButton
android:id="@+id/check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:minWidth="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.558" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,146 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:orientation="horizontal"
android:paddingTop="20dp">
<TextView
android:id="@+id/bottom_sheet_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/template"
android:textSize="25sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/bottom_sheet_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/create_template"
android:textSize="12sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/bottom_sheet_title" />
<Button
android:id="@+id/template_create"
style="@style/Widget.Material3.Button.ElevatedButton.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoLink="all"
android:text="@string/ok"
app:icon="@drawable/ic_check"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="15dp">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/title"
android:layout_width="match_parent"
android:paddingBottom="10dp"
android:layout_height="wrap_content"
android:hint="@string/title"
style="@style/Widget.Material3.TextInputLayout.FilledBox">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:inputType="text"
android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
app:endIconMode="custom"
app:endIconDrawable="@drawable/ic_clipboard"
android:hint="@string/command"
style="@style/Widget.Material3.TextInputLayout.FilledBox">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:maxLines="4"
android:inputType="textMultiLine"
android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>
<View
android:layout_marginVertical="10dp"
style="@style/Divider.Horizontal"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingBottom="5dp"
android:text="@string/shortcuts"
android:textSize="15sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/edit_shortcuts"
style="@style/Widget.Material3.Button.TextButton.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoLink="all"
android:text="@string/edit"
app:icon="@drawable/ic_edit"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.chip.ChipGroup
android:id="@+id/shortcutsChipGroup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:singleLine="false">
</com.google.android.material.chip.ChipGroup>
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -88,7 +88,7 @@
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/run_command" />
android:text="@string/command" />
</com.google.android.material.tabs.TabLayout>

View file

@ -10,13 +10,31 @@
android:padding="10dp"
android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
app:endIconMode="custom"
app:endIconDrawable="@drawable/ic_clipboard"
android:hint="@string/command"
style="@style/Widget.Material3.TextInputLayout.FilledBox">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:maxLines="4"
android:inputType="textMultiLine"
android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/template"
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:hint="@string/commands">
android:hint="@string/command_templates">
<AutoCompleteTextView
android:id="@+id/template_textview"
@ -28,21 +46,6 @@
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.card.MaterialCardView
style="@style/Widget.Material3.CardView.Filled"
android:layout_width="match_parent"
android:layout_marginBottom="10dp"
android:layout_height="wrap_content">
<TextView
android:id="@+id/template_content_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"/>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/outputPath"
android:layout_width="match_parent"

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.chip.Chip xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/input_chip"
style="@style/Widget.Material3.Chip.Input.Icon.Elevated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.chip.Chip xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/suggestion_chip"
style="@style/Widget.Material3.Chip.Suggestion.Elevated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:theme="@style/Theme.Material3.DayNight"
/>

View file

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:orientation="horizontal"
android:paddingTop="20dp">
<TextView
android:id="@+id/bottom_sheet_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/shortcuts"
android:textSize="25sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/bottom_sheet_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/shortcuts_desc"
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/bottom_sheet_title" />
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="15dp">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/title"
android:layout_width="match_parent"
android:paddingBottom="10dp"
android:layout_height="wrap_content"
app:endIconMode="custom"
app:endIconDrawable="@drawable/ic_plus"
android:hint="@string/create_template"
style="@style/Widget.Material3.TextInputLayout.FilledBox">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:maxLines="4"
android:inputType="textMultiLine"
android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.chip.ChipGroup
android:id="@+id/shortcutsChipGroup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:singleLine="false">
</com.google.android.material.chip.ChipGroup>
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/import_clipboard"
android:title="@string/import_from_clipboard"
app:showAsAction="never" />
<item
android:id="@+id/export_clipboard"
android:title="@string/export_from_clipboard"
app:showAsAction="never" />
</menu>

View file

@ -184,4 +184,11 @@
<string name="new_update">New Update</string>
<string name="download_queue">Download Queue</string>
<string name="running">Running</string>
<string name="command">Command</string>
<string name="import_from_clipboard">Import from clipboard</string>
<string name="export_from_clipboard">Export to clipboard</string>
<string name="shortcuts">Shortcuts</string>
<string name="create_template">Create Template</string>
<string name="edit">Edit</string>
<string name="shortcuts_desc">Use commonly used commands to create templates</string>
</resources>