Improved enabling, ordering, and disabling codecs

This commit is contained in:
Juha Heinanen
2022-09-10 19:23:00 +03:00
parent 4b7537b588
commit 2eb45fe8a7
12 changed files with 249 additions and 73 deletions
+4 -1
View File
@@ -1,5 +1,6 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
android { android {
compileSdkVersion = 32 compileSdkVersion = 32
@@ -23,6 +24,7 @@ android {
lintOptions { lintOptions {
checkDependencies true checkDependencies true
} }
vectorDrawables.useSupportLibrary = true
} }
splits { splits {
abi { abi {
@@ -38,6 +40,7 @@ android {
} }
buildFeatures { buildFeatures {
viewBinding true viewBinding true
dataBinding true
} }
sourceSets { sourceSets {
main.java.srcDirs += 'src/main/kotlin' main.java.srcDirs += 'src/main/kotlin'
@@ -52,7 +55,7 @@ android {
dependencies { dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.appcompat:appcompat:1.5.0' implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'com.google.android.material:material:1.6.1' implementation 'com.google.android.material:material:1.6.1'
@@ -0,0 +1,3 @@
package com.tutpro.baresip
data class Codec(val name: String, var enabled: Boolean)
@@ -1,22 +1,26 @@
package com.tutpro.baresip package com.tutpro.baresip
import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View
import android.widget.*
import android.widget.LinearLayout.LayoutParams
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.ItemTouchHelper.*
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.tutpro.baresip.databinding.ActivityCodecsBinding import com.tutpro.baresip.databinding.ActivityCodecsBinding
import java.util.*
class CodecsActivity : AppCompatActivity() { class CodecsActivity : AppCompatActivity() {
private lateinit var binding: ActivityCodecsBinding private lateinit var binding: ActivityCodecsBinding
private lateinit var acc: Account private lateinit var acc: Account
private lateinit var ua: UserAgent private lateinit var ua: UserAgent
private lateinit var codecsAdapter: CodecsAdapter
private var aor = "" private var aor = ""
private var newCodecs = ArrayList<String>() private var newCodecs = ArrayList<Codec>()
private var media = "" private var media = ""
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@@ -35,58 +39,48 @@ class CodecsActivity : AppCompatActivity() {
ua = UserAgent.ofAor(aor)!! ua = UserAgent.ofAor(aor)!!
acc = ua.account acc = ua.account
val codecs: ArrayList<String> val allCodecs: ArrayList<String>
val accCodecs: ArrayList<String> val accCodecs: ArrayList<String>
val title = binding.CodecsTitle val codecsTitle = binding.CodecsTitle
val codecList = binding.CodecList
itemTouchHelper.attachToRecyclerView(codecList)
if (media == "audio") { if (media == "audio") {
title.text = getString(R.string.audio_codecs) codecsTitle.text = getString(R.string.audio_codecs)
codecs = ArrayList(Api.audio_codecs().split(",")) allCodecs = ArrayList(Api.audio_codecs().split(","))
accCodecs = acc.audioCodec accCodecs = acc.audioCodec
} else { } else {
title.text = getString(R.string.video_codecs) codecsTitle.text = getString(R.string.video_codecs)
codecs = ArrayList(Api.video_codecs().split(",").distinct()) allCodecs = ArrayList(Api.video_codecs().split(",").distinct())
accCodecs = acc.videoCodec accCodecs = acc.videoCodec
} }
newCodecs.addAll(accCodecs) for (codec in accCodecs)
newCodecs.add(Codec(codec, true))
for (codec in allCodecs)
if (codec !in accCodecs)
newCodecs.add(Codec(codec, false))
while (newCodecs.size < codecs.size) newCodecs.add("-") codecsAdapter = CodecsAdapter(newCodecs)
val layout = binding.SpinnerTable codecList.layoutManager = LinearLayoutManager(this)
val spinnerList = Array(codecs.size) { ArrayList<String>() } codecList.adapter = codecsAdapter
for (i in codecs.indices) {
val spinner = Spinner(applicationContext)
spinner.id = i + 100
spinner.layoutParams = TableRow.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT)
spinner.layoutParams.height = 75
layout.addView(spinner)
if (accCodecs.size > i) {
val codec = accCodecs[i]
spinnerList[i].add(codec)
spinnerList[i].add("-")
for (c in codecs) if (c != codec) spinnerList[i].add(c)
} else {
spinnerList[i].addAll(codecs)
spinnerList[i].add(0, "-")
}
val codecSpinner = findViewById<Spinner>(spinner.id)
val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item,
spinnerList[i])
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
codecSpinner.adapter = adapter
codecSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
newCodecs[parent.id - 100] = parent.selectedItem.toString()
}
override fun onNothingSelected(parent: AdapterView<*>) {
}
}
}
binding.CodecsTitle.setOnClickListener { codecsAdapter.setOnItemClickListener(object : CodecsAdapter.OnItemClickListener {
@SuppressLint("NotifyDataSetChanged")
override fun onItemClick(position: Int) {
if (!newCodecs[position].enabled) {
codecsAdapter.enableItem(position)
codecsAdapter.notifyDataSetChanged()
}
}
})
itemTouchHelper.attachToRecyclerView(binding.CodecList)
codecsTitle.setOnClickListener {
if (media == "audio") if (media == "audio")
Utils.alertView(this, getString(R.string.audio_codecs), Utils.alertView(this, getString(R.string.audio_codecs),
getString(R.string.audio_codecs_help)) getString(R.string.audio_codecs_help))
@@ -97,6 +91,38 @@ class CodecsActivity : AppCompatActivity() {
} }
private val itemTouchHelper by lazy {
val simpleItemTouchCallback = object : ItemTouchHelper.SimpleCallback(
UP or DOWN or START or END,
RIGHT
) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
val fromPosition = viewHolder.adapterPosition
val toPosition = target.adapterPosition
codecsAdapter.moveItem(fromPosition, toPosition)
codecsAdapter.notifyItemMoved(fromPosition, toPosition)
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
codecsAdapter.removeItem(viewHolder.adapterPosition)
codecsAdapter.notifyItemRemoved(viewHolder.adapterPosition)
}
@SuppressLint("NotifyDataSetChanged")
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder)
recyclerView.post { codecsAdapter.notifyDataSetChanged() }
}
}
ItemTouchHelper(simpleItemTouchCallback)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu) super.onCreateOptionsMenu(menu)
@@ -116,28 +142,33 @@ class CodecsActivity : AppCompatActivity() {
R.id.checkIcon -> { R.id.checkIcon -> {
var save = false var save = false
val mc = ArrayList(LinkedHashSet<String>(newCodecs.filter { it != "-" } as ArrayList<String>)) val codecs = ArrayList<String>()
val mcList = Utils.implode(mc, ",")
for (codec in newCodecs)
if (codec.enabled)
codecs.add(codec.name)
val codecList = Utils.implode(codecs, ",")
if (media == "audio") if (media == "audio")
if (mc != acc.audioCodec) { if (codecs != acc.audioCodec) {
if (Api.account_set_audio_codecs(acc.accp, mcList) == 0) { if (Api.account_set_audio_codecs(acc.accp, codecList) == 0) {
Log.d(TAG, "New audio codecs '$mcList'") Log.d(TAG, "New audio codecs '$codecList'")
acc.audioCodec = mc acc.audioCodec = codecs
save = true save = true
} else { } else {
Log.e(TAG, "Setting of audio codecs '$mcList' failed") Log.e(TAG, "Setting of audio codecs '$codecList' failed")
} }
} }
if (media == "video") if (media == "video")
if (mc != acc.videoCodec) { if (codecs != acc.videoCodec) {
if (Api.account_set_video_codecs(acc.accp, mcList) == 0) { if (Api.account_set_video_codecs(acc.accp, codecList) == 0) {
Log.d(TAG, "New video codecs '$mcList'") Log.d(TAG, "New video codecs '$codecs'")
acc.videoCodec = mc acc.videoCodec = codecs
save = true save = true
} else { } else {
Log.e(TAG, "Setting of video codecs '$mcList' failed") Log.e(TAG, "Setting of video codecs '$codecs' failed")
} }
} }
@@ -0,0 +1,78 @@
package com.tutpro.baresip
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import java.util.*
class CodecsAdapter(private val codecs: ArrayList<Codec>):
RecyclerView.Adapter<CodecsAdapter.CodecViewHolder>() {
private var codecListener: OnItemClickListener? = null
interface OnItemClickListener {
fun onItemClick(position: Int)
}
fun setOnItemClickListener(listener: OnItemClickListener) {
codecListener = listener
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CodecViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.codec, parent, false)
return CodecViewHolder(v, codecListener!!)
}
override fun getItemCount(): Int {
return codecs.size
}
fun moveItem(fromPosition: Int, toPosition: Int) {
val codec: Codec = codecs[fromPosition]
codec.enabled = codecs[toPosition].enabled
codecs.removeAt(fromPosition)
codecs.add(toPosition, codec)
}
fun removeItem(position: Int) {
val codec: Codec = codecs[position]
if (codec.enabled) {
codec.enabled = false
codecs.removeAt(position)
codecs.add(codec)
}
}
fun enableItem(position: Int) {
val codec: Codec = codecs[position]
codec.enabled = true
codecs.removeAt(position)
codecs.add(0, codec)
}
override fun onBindViewHolder(holder: CodecViewHolder, position: Int) {
val codec = codecs[position]
holder.codecName.text = codec.name
holder.codecName.alpha = if (codec.enabled) 1.0f else 0.5f
holder.actionImage.setImageResource(
if (codec.enabled) R.drawable.reorder else R.drawable.add
)
}
class CodecViewHolder(itemView: View, listener: OnItemClickListener):
RecyclerView.ViewHolder(itemView) {
var codecName: TextView = itemView.findViewById(R.id.codecName)
var actionImage: ImageView = itemView.findViewById(R.id.actionImage)
init {
itemView.setOnClickListener {
val position = adapterPosition
if (position != RecyclerView.NO_POSITION) {
listener.onItemClick(position)
}
}
}
}
}
@@ -38,7 +38,6 @@ import java.io.File
import java.net.URLDecoder import java.net.URLDecoder
import kotlin.system.exitProcess import kotlin.system.exitProcess
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding private lateinit var binding: ActivityMainBinding
+8
View File
@@ -0,0 +1,8 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM17,13h-4v4h-2v-4L7,13v-2h4L11,7h2v4h4v2z"/>
</vector>
+10
View File
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M3,15h18v-2L3,13v2zM3,19h18v-2L3,17v2zM3,11h18L21,9L3,9v2zM3,5v2h18L21,5L3,5z"/>
</vector>
@@ -140,6 +140,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_toStartOf="@id/Register" android:layout_toStartOf="@id/Register"
android:textSize="18sp" android:textSize="18sp"
android:text="@string/register" > android:text="@string/register" >
@@ -150,6 +151,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_gravity="end" android:layout_gravity="end"
android:layout_centerVertical="true"
android:checked="false" > android:checked="false" >
</CheckBox> </CheckBox>
</RelativeLayout> </RelativeLayout>
@@ -367,6 +369,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_toStartOf="@id/Default" android:layout_toStartOf="@id/Default"
android:textSize="18sp" android:textSize="18sp"
android:text="@string/default_account" > android:text="@string/default_account" >
@@ -376,6 +379,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_gravity="end" android:layout_gravity="end"
android:checked="false" > android:checked="false" >
</CheckBox> </CheckBox>
+4 -4
View File
@@ -17,11 +17,11 @@
android:text="@string/audio_codecs" > android:text="@string/audio_codecs" >
</TextView> </TextView>
<TableLayout <androidx.recyclerview.widget.RecyclerView
android:id="@+id/SpinnerTable" android:id="@+id/CodecList"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingTop="36dp" > android:layout_marginTop="32dp"
</TableLayout> android:scrollbars="vertical" />
</RelativeLayout> </RelativeLayout>
+36
View File
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp" >
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:layout_margin="4dp" >
<TextView
android:id="@+id/codecName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:textSize="18sp"
android:padding="10dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ImageView
android:id="@+id/actionImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:srcCompat="@drawable/reorder"
android:contentDescription="@string/codec_action" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
+8 -6
View File
@@ -143,12 +143,6 @@
<string name="register_help">Jos merkitty, rekisteröinti on <string name="register_help">Jos merkitty, rekisteröinti on
aktiivinen ja REGISTER-sanomat lähetetään noin 12 minuutin välein. aktiivinen ja REGISTER-sanomat lähetetään noin 12 minuutin välein.
</string> </string>
<string name="audio_codecs">Audion koodausmenetelmät</string>
<string name="audio_codecs_help">Luettelo käytössä olevista audion
koodausmenetelmistä prioriteettijärjestyksessä</string>
<string name="video_codecs">Videon koodausmenetelmät</string>
<string name="video_codecs_help">Luettelo käytössä olevista videon
koodausmenetelmistä prioriteettijärjestyksessä</string>
<string name="media_nat">Media NAT hallinta</string> <string name="media_nat">Media NAT hallinta</string>
<string name="media_nat_help">Valitsee media NAT hallintaprotokollan <string name="media_nat_help">Valitsee media NAT hallintaprotokollan
(vapaaehtoinen). Vaihtoehtoja ovat STUN (Session Traversal Utilities (vapaaehtoinen). Vaihtoehtoja ovat STUN (Session Traversal Utilities
@@ -291,6 +285,14 @@
<string name="short_chat_question">Haluatko poistaa viestiketjun \'%1$s\'\?</string> <string name="short_chat_question">Haluatko poistaa viestiketjun \'%1$s\'\?</string>
<string name="delete_chats">Tyhjennä</string> <string name="delete_chats">Tyhjennä</string>
<string name="delete_chats_alert">Haluatko tyhjentää tilin \'%1$s\' viestihistorian\?</string> <string name="delete_chats_alert">Haluatko tyhjentää tilin \'%1$s\' viestihistorian\?</string>
<!-- Codecs Activity -->
<string name="audio_codecs">Audion koodausmenetelmät</string>
<string name="audio_codecs_help">Luettelo käytössä olevista audion
koodausmenetelmistä prioriteettijärjestyksessä</string>
<string name="video_codecs">Videon koodausmenetelmät</string>
<string name="video_codecs_help">Luettelo käytössä olevista videon
koodausmenetelmistä prioriteettijärjestyksessä</string>
<string name="codec_action">Koodausmenetelmä toiminto (järjestä tai lisää)</string>
<!-- Config Activity --> <!-- Config Activity -->
<string name="configuration">Asetukset</string> <string name="configuration">Asetukset</string>
<string name="start_automatically">Käynnistä automaattisesti</string> <string name="start_automatically">Käynnistä automaattisesti</string>
+6 -4
View File
@@ -144,10 +144,6 @@
<string name="register_help">If checked, registration is enabled and REGISTER requests are sent at <string name="register_help">If checked, registration is enabled and REGISTER requests are sent at
12 minute intervals. 12 minute intervals.
</string> </string>
<string name="audio_codecs">Audio Codecs</string>
<string name="audio_codecs_help">List of supported audio codecs in priority order</string>
<string name="video_codecs">Video Codecs</string>
<string name="video_codecs_help">List of supported video codecs in priority order</string>
<string name="media_nat">Media NAT Traversal</string> <string name="media_nat">Media NAT Traversal</string>
<string name="media_nat_help">Selects media NAT traversal protocol (if any). Possible choices are STUN <string name="media_nat_help">Selects media NAT traversal protocol (if any). Possible choices are STUN
(Session Traversal Utilities for NAT, RFC 5389) and ICE (Interactive Connectivity (Session Traversal Utilities for NAT, RFC 5389) and ICE (Interactive Connectivity
@@ -281,6 +277,12 @@
<string name="short_chat_question">Do you want to delete chat with \'%1$s\'\?</string> <string name="short_chat_question">Do you want to delete chat with \'%1$s\'\?</string>
<string name="delete_chats">Delete</string> <string name="delete_chats">Delete</string>
<string name="delete_chats_alert">Do you want to delete chat history of account \'%1$s\'\?</string> <string name="delete_chats_alert">Do you want to delete chat history of account \'%1$s\'\?</string>
<!-- Codecs Activity -->
<string name="audio_codecs">Audio Codecs</string>
<string name="audio_codecs_help">List of supported audio codecs in priority order</string>
<string name="video_codecs">Video Codecs</string>
<string name="video_codecs_help">List of supported video codecs in priority order</string>
<string name="codec_action">Codec action (reorder or add)</string>
<!-- Config Activity --> <!-- Config Activity -->
<string name="configuration">Settings</string> <string name="configuration">Settings</string>
<string name="start_automatically">Start Automatically</string> <string name="start_automatically">Start Automatically</string>