mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 11:28:06 +00:00
Added conversations & meetings settings
This commit is contained in:
parent
6c03a6fb7a
commit
281b44a240
8 changed files with 324 additions and 138 deletions
|
|
@ -100,6 +100,15 @@ class CorePreferences @UiThread constructor(private val context: Context) {
|
|||
config.setBool("app", "auto_start_call_record", value)
|
||||
}
|
||||
|
||||
// Conversation settings
|
||||
|
||||
var exportMediaToNativeGallery: Boolean // TODO: use it!
|
||||
// Keep old name for backward compatibility
|
||||
get() = config.getBool("app", "make_downloaded_images_public_in_gallery", true)
|
||||
set(value) {
|
||||
config.setBool("app", "make_downloaded_images_public_in_gallery", value)
|
||||
}
|
||||
|
||||
/* Voice Recordings */
|
||||
|
||||
var voiceRecordingMaxDuration: Int
|
||||
|
|
|
|||
|
|
@ -38,6 +38,18 @@ class SettingsFragment : GenericFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private val layoutListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
val label = viewModel.availableLayoutsNames[position]
|
||||
val value = viewModel.availableLayoutsValues[position]
|
||||
Log.i("$TAG Selected meeting default layout is now [$label] ($value)")
|
||||
viewModel.setDefaultLayout(value)
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||
}
|
||||
}
|
||||
|
||||
private val themeListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
val label = viewModel.availableThemesNames[position]
|
||||
|
|
@ -97,6 +109,22 @@ class SettingsFragment : GenericFragment() {
|
|||
|
||||
binding.callsSettings.deviceRingtoneSpinner.onItemSelectedListener = ringtoneListener
|
||||
|
||||
// Meeting default layout related
|
||||
val layoutAdapter = ArrayAdapter(
|
||||
requireContext(),
|
||||
R.layout.drop_down_item,
|
||||
viewModel.availableLayoutsNames
|
||||
)
|
||||
layoutAdapter.setDropDownViewResource(R.layout.generic_dropdown_cell)
|
||||
binding.meetingsSettings.layoutSpinner.adapter = layoutAdapter
|
||||
|
||||
viewModel.defaultLayout.observe(viewLifecycleOwner) { layout ->
|
||||
binding.meetingsSettings.layoutSpinner.setSelection(
|
||||
viewModel.availableLayoutsValues.indexOf(layout)
|
||||
)
|
||||
}
|
||||
binding.meetingsSettings.layoutSpinner.onItemSelectedListener = layoutListener
|
||||
|
||||
// Light/Dark theme related
|
||||
val themeAdapter = ArrayAdapter(
|
||||
requireContext(),
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import java.util.Locale
|
|||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.LinphoneApplication.Companion.corePreferences
|
||||
import org.linphone.R
|
||||
import org.linphone.core.Conference
|
||||
import org.linphone.core.Player
|
||||
import org.linphone.core.PlayerListener
|
||||
import org.linphone.core.tools.Log
|
||||
|
|
@ -71,16 +72,32 @@ class SettingsViewModel @UiThread constructor() : ViewModel() {
|
|||
// Conversations settings
|
||||
val showConversationsSettings = MutableLiveData<Boolean>()
|
||||
|
||||
val autoDownloadEnabled = MutableLiveData<Boolean>()
|
||||
val exportMediaEnabled = MutableLiveData<Boolean>()
|
||||
|
||||
// Meetings settings
|
||||
val showMeetingsSettings = MutableLiveData<Boolean>()
|
||||
|
||||
val defaultLayout = MutableLiveData<Int>()
|
||||
val availableLayoutsNames = arrayListOf(
|
||||
AppUtils.getString(R.string.settings_meetings_layout_active_speaker_label),
|
||||
AppUtils.getString(R.string.settings_meetings_layout_mosaic_label)
|
||||
)
|
||||
val availableLayoutsValues = arrayListOf(
|
||||
Conference.Layout.ActiveSpeaker.toInt(),
|
||||
Conference.Layout.Grid.toInt()
|
||||
)
|
||||
|
||||
// Network settings
|
||||
val useWifiOnly = MutableLiveData<Boolean>()
|
||||
|
||||
// User Interface settings
|
||||
|
||||
val theme = MutableLiveData<Int>()
|
||||
val availableThemesNames = arrayListOf<String>()
|
||||
val availableThemesNames = arrayListOf(
|
||||
AppUtils.getString(R.string.settings_user_interface_auto_theme_label),
|
||||
AppUtils.getString(R.string.settings_user_interface_light_theme_label),
|
||||
AppUtils.getString(R.string.settings_user_interface_dark_theme_label)
|
||||
)
|
||||
val availableThemesValues = arrayListOf(-1, 0, 1)
|
||||
|
||||
// Other
|
||||
|
|
@ -114,16 +131,6 @@ class SettingsViewModel @UiThread constructor() : ViewModel() {
|
|||
|
||||
computeAvailableRingtones()
|
||||
|
||||
availableThemesNames.add(
|
||||
AppUtils.getString(R.string.settings_user_interface_auto_theme_label)
|
||||
)
|
||||
availableThemesNames.add(
|
||||
AppUtils.getString(R.string.settings_user_interface_light_theme_label)
|
||||
)
|
||||
availableThemesNames.add(
|
||||
AppUtils.getString(R.string.settings_user_interface_dark_theme_label)
|
||||
)
|
||||
|
||||
coreContext.postOnCoreThread { core ->
|
||||
echoCancellerEnabled.postValue(core.isEchoCancellationEnabled)
|
||||
routeAudioToBluetooth.postValue(corePreferences.routeAudioToBluetoothIfAvailable)
|
||||
|
|
@ -137,6 +144,11 @@ class SettingsViewModel @UiThread constructor() : ViewModel() {
|
|||
Log.i("Currently configured ringtone in Core is [$ringtone]")
|
||||
selectedRingtone.postValue(ringtone)
|
||||
|
||||
autoDownloadEnabled.postValue(core.maxSizeForAutoDownloadIncomingFiles == 0)
|
||||
exportMediaEnabled.postValue(corePreferences.exportMediaToNativeGallery)
|
||||
|
||||
defaultLayout.postValue(core.defaultConferenceLayout.toInt())
|
||||
|
||||
theme.postValue(corePreferences.darkMode)
|
||||
}
|
||||
}
|
||||
|
|
@ -296,11 +308,39 @@ class SettingsViewModel @UiThread constructor() : ViewModel() {
|
|||
expandConversations.value = expandConversations.value == false
|
||||
}
|
||||
|
||||
@UiThread
|
||||
fun toggleAutoDownload() {
|
||||
val newValue = autoDownloadEnabled.value == false
|
||||
coreContext.postOnCoreThread { core ->
|
||||
core.maxSizeForAutoDownloadIncomingFiles = if (newValue) 0 else -1
|
||||
autoDownloadEnabled.postValue(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
fun toggleExportMedia() {
|
||||
val newValue = exportMediaEnabled.value == false
|
||||
coreContext.postOnCoreThread {
|
||||
corePreferences.exportMediaToNativeGallery = newValue
|
||||
exportMediaEnabled.postValue(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
fun toggleMeetingsExpand() {
|
||||
expandMeetings.value = expandMeetings.value == false
|
||||
}
|
||||
|
||||
@UiThread
|
||||
fun setDefaultLayout(layoutValue: Int) {
|
||||
coreContext.postOnCoreThread { core ->
|
||||
val newDefaultLayout = Conference.Layout.fromInt(layoutValue)
|
||||
core.defaultConferenceLayout = newDefaultLayout
|
||||
Log.i("$TAG Default meeting layout [$newDefaultLayout] saved")
|
||||
defaultLayout.postValue(layoutValue)
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
fun toggleNetworkExpand() {
|
||||
expandNetwork.value = expandNetwork.value == false
|
||||
|
|
|
|||
|
|
@ -1,13 +1,9 @@
|
|||
package org.linphone.ui.main.viewer.viewmodel
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.pdf.PdfRenderer
|
||||
import android.net.Uri
|
||||
import android.os.Environment
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.provider.MediaStore
|
||||
import android.widget.ImageView
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
|
|
@ -21,9 +17,7 @@ import java.lang.StringBuilder
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.R
|
||||
import org.linphone.compatibility.Compatibility
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.utils.AppUtils
|
||||
import org.linphone.utils.Event
|
||||
|
|
@ -208,7 +202,7 @@ class FileViewModel @UiThread constructor() : ViewModel() {
|
|||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
Log.i("$TAG Export file [$filePath] to Android's MediaStore")
|
||||
val mediaStorePath = addContentToMediaStore(filePath)
|
||||
val mediaStorePath = FileUtils.addContentToMediaStore(filePath)
|
||||
if (mediaStorePath.isNotEmpty()) {
|
||||
Log.i(
|
||||
"$TAG File [$filePath] has been successfully exported to MediaStore"
|
||||
|
|
@ -302,123 +296,4 @@ class FileViewModel @UiThread constructor() : ViewModel() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private suspend fun addContentToMediaStore(
|
||||
path: String
|
||||
): String {
|
||||
if (path.isEmpty()) {
|
||||
Log.e("$TAG No file path to export to MediaStore!")
|
||||
return ""
|
||||
}
|
||||
|
||||
val isImage = FileUtils.isExtensionImage(path)
|
||||
val isVideo = FileUtils.isExtensionVideo(path)
|
||||
val isAudio = FileUtils.isExtensionAudio(path)
|
||||
|
||||
val directory = when {
|
||||
isImage -> Environment.DIRECTORY_PICTURES
|
||||
isVideo -> Environment.DIRECTORY_MOVIES
|
||||
isAudio -> Environment.DIRECTORY_MUSIC
|
||||
else -> Environment.DIRECTORY_DOWNLOADS
|
||||
}
|
||||
|
||||
val appName = AppUtils.getString(R.string.app_name)
|
||||
val relativePath = "$directory/$appName"
|
||||
val fileName = FileUtils.getNameFromFilePath(path)
|
||||
val extension = FileUtils.getExtensionFromFileName(fileName)
|
||||
val mime = FileUtils.getMimeTypeFromExtension(extension)
|
||||
|
||||
val context = coreContext.context
|
||||
val mediaStoreFilePath = when {
|
||||
isImage -> {
|
||||
val values = ContentValues().apply {
|
||||
put(MediaStore.Images.Media.DISPLAY_NAME, fileName)
|
||||
put(MediaStore.Images.Media.MIME_TYPE, mime)
|
||||
put(MediaStore.Images.Media.RELATIVE_PATH, relativePath)
|
||||
put(MediaStore.Images.Media.IS_PENDING, 1)
|
||||
}
|
||||
val collection = Compatibility.getMediaCollectionUri(isImage = true)
|
||||
addContentValuesToCollection(
|
||||
context,
|
||||
path,
|
||||
collection,
|
||||
values,
|
||||
MediaStore.Images.Media.IS_PENDING
|
||||
)
|
||||
}
|
||||
isVideo -> {
|
||||
val values = ContentValues().apply {
|
||||
put(MediaStore.Video.Media.TITLE, fileName)
|
||||
put(MediaStore.Video.Media.DISPLAY_NAME, fileName)
|
||||
put(MediaStore.Video.Media.MIME_TYPE, mime)
|
||||
put(MediaStore.Video.Media.RELATIVE_PATH, relativePath)
|
||||
put(MediaStore.Video.Media.IS_PENDING, 1)
|
||||
}
|
||||
val collection = Compatibility.getMediaCollectionUri(isVideo = true)
|
||||
addContentValuesToCollection(
|
||||
context,
|
||||
path,
|
||||
collection,
|
||||
values,
|
||||
MediaStore.Video.Media.IS_PENDING
|
||||
)
|
||||
}
|
||||
isAudio -> {
|
||||
val values = ContentValues().apply {
|
||||
put(MediaStore.Audio.Media.TITLE, fileName)
|
||||
put(MediaStore.Audio.Media.DISPLAY_NAME, fileName)
|
||||
put(MediaStore.Audio.Media.MIME_TYPE, mime)
|
||||
put(MediaStore.Audio.Media.RELATIVE_PATH, relativePath)
|
||||
put(MediaStore.Audio.Media.IS_PENDING, 1)
|
||||
}
|
||||
val collection = Compatibility.getMediaCollectionUri(isAudio = true)
|
||||
addContentValuesToCollection(
|
||||
context,
|
||||
path,
|
||||
collection,
|
||||
values,
|
||||
MediaStore.Audio.Media.IS_PENDING
|
||||
)
|
||||
}
|
||||
else -> ""
|
||||
}
|
||||
|
||||
if (mediaStoreFilePath.isNotEmpty()) {
|
||||
Log.i("$TAG Exported file path to MediaStore is: $mediaStoreFilePath")
|
||||
return mediaStoreFilePath
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private suspend fun addContentValuesToCollection(
|
||||
context: Context,
|
||||
filePath: String,
|
||||
collection: Uri,
|
||||
values: ContentValues,
|
||||
pendingKey: String
|
||||
): String {
|
||||
try {
|
||||
val fileUri = context.contentResolver.insert(collection, values)
|
||||
if (fileUri == null) {
|
||||
Log.e("$TAG Failed to get a URI to where store the file, aborting")
|
||||
return ""
|
||||
}
|
||||
|
||||
context.contentResolver.openOutputStream(fileUri).use { out ->
|
||||
if (FileUtils.copyFileTo(filePath, out)) {
|
||||
values.clear()
|
||||
values.put(pendingKey, 0)
|
||||
context.contentResolver.update(fileUri, values, null, null)
|
||||
|
||||
return fileUri.toString()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("$TAG Exception: $e")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,17 +19,20 @@
|
|||
*/
|
||||
package org.linphone.utils
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.CursorIndexOutOfBoundsException
|
||||
import android.net.Uri
|
||||
import android.os.Environment
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.os.Process
|
||||
import android.provider.MediaStore
|
||||
import android.provider.OpenableColumns
|
||||
import android.system.Os
|
||||
import android.text.format.Formatter
|
||||
import android.webkit.MimeTypeMap
|
||||
import androidx.annotation.AnyThread
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.core.content.FileProvider
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
|
|
@ -42,6 +45,7 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.withContext
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.R
|
||||
import org.linphone.compatibility.Compatibility
|
||||
import org.linphone.core.tools.Log
|
||||
|
||||
class FileUtils {
|
||||
|
|
@ -478,5 +482,121 @@ class FileUtils {
|
|||
}
|
||||
return name
|
||||
}
|
||||
|
||||
suspend fun addContentToMediaStore(path: String): String {
|
||||
if (path.isEmpty()) {
|
||||
Log.e("$TAG No file path to export to MediaStore!")
|
||||
return ""
|
||||
}
|
||||
|
||||
val isImage = isExtensionImage(path)
|
||||
val isVideo = isExtensionVideo(path)
|
||||
val isAudio = isExtensionAudio(path)
|
||||
|
||||
val directory = when {
|
||||
isImage -> Environment.DIRECTORY_PICTURES
|
||||
isVideo -> Environment.DIRECTORY_MOVIES
|
||||
isAudio -> Environment.DIRECTORY_MUSIC
|
||||
else -> Environment.DIRECTORY_DOWNLOADS
|
||||
}
|
||||
|
||||
val appName = AppUtils.getString(R.string.app_name)
|
||||
val relativePath = "$directory/$appName"
|
||||
val fileName = getNameFromFilePath(path)
|
||||
val extension = getExtensionFromFileName(fileName)
|
||||
val mime = getMimeTypeFromExtension(extension)
|
||||
|
||||
val context = coreContext.context
|
||||
val mediaStoreFilePath = when {
|
||||
isImage -> {
|
||||
val values = ContentValues().apply {
|
||||
put(MediaStore.Images.Media.DISPLAY_NAME, fileName)
|
||||
put(MediaStore.Images.Media.MIME_TYPE, mime)
|
||||
put(MediaStore.Images.Media.RELATIVE_PATH, relativePath)
|
||||
put(MediaStore.Images.Media.IS_PENDING, 1)
|
||||
}
|
||||
val collection = Compatibility.getMediaCollectionUri(isImage = true)
|
||||
addContentValuesToCollection(
|
||||
context,
|
||||
path,
|
||||
collection,
|
||||
values,
|
||||
MediaStore.Images.Media.IS_PENDING
|
||||
)
|
||||
}
|
||||
isVideo -> {
|
||||
val values = ContentValues().apply {
|
||||
put(MediaStore.Video.Media.TITLE, fileName)
|
||||
put(MediaStore.Video.Media.DISPLAY_NAME, fileName)
|
||||
put(MediaStore.Video.Media.MIME_TYPE, mime)
|
||||
put(MediaStore.Video.Media.RELATIVE_PATH, relativePath)
|
||||
put(MediaStore.Video.Media.IS_PENDING, 1)
|
||||
}
|
||||
val collection = Compatibility.getMediaCollectionUri(isVideo = true)
|
||||
addContentValuesToCollection(
|
||||
context,
|
||||
path,
|
||||
collection,
|
||||
values,
|
||||
MediaStore.Video.Media.IS_PENDING
|
||||
)
|
||||
}
|
||||
isAudio -> {
|
||||
val values = ContentValues().apply {
|
||||
put(MediaStore.Audio.Media.TITLE, fileName)
|
||||
put(MediaStore.Audio.Media.DISPLAY_NAME, fileName)
|
||||
put(MediaStore.Audio.Media.MIME_TYPE, mime)
|
||||
put(MediaStore.Audio.Media.RELATIVE_PATH, relativePath)
|
||||
put(MediaStore.Audio.Media.IS_PENDING, 1)
|
||||
}
|
||||
val collection = Compatibility.getMediaCollectionUri(isAudio = true)
|
||||
addContentValuesToCollection(
|
||||
context,
|
||||
path,
|
||||
collection,
|
||||
values,
|
||||
MediaStore.Audio.Media.IS_PENDING
|
||||
)
|
||||
}
|
||||
else -> ""
|
||||
}
|
||||
|
||||
if (mediaStoreFilePath.isNotEmpty()) {
|
||||
Log.i("$TAG Exported file path to MediaStore is: $mediaStoreFilePath")
|
||||
return mediaStoreFilePath
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private suspend fun addContentValuesToCollection(
|
||||
context: Context,
|
||||
filePath: String,
|
||||
collection: Uri,
|
||||
values: ContentValues,
|
||||
pendingKey: String
|
||||
): String {
|
||||
try {
|
||||
val fileUri = context.contentResolver.insert(collection, values)
|
||||
if (fileUri == null) {
|
||||
Log.e("$TAG Failed to get a URI to where store the file, aborting")
|
||||
return ""
|
||||
}
|
||||
|
||||
context.contentResolver.openOutputStream(fileUri).use { out ->
|
||||
if (copyFileTo(filePath, out)) {
|
||||
values.clear()
|
||||
values.put(pendingKey, 0)
|
||||
context.contentResolver.update(fileUri, values, null, null)
|
||||
|
||||
return fileUri.toString()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("$TAG Exception: $e")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,76 @@
|
|||
android:paddingBottom="20dp"
|
||||
android:background="@drawable/shape_squircle_white_background">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/settings_title_style"
|
||||
android:onClick="@{() -> viewModel.toggleAutoDownload()}"
|
||||
android:id="@+id/auto_download_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:text="@string/settings_conversations_auto_download_title"
|
||||
android:maxLines="2"
|
||||
android:ellipsize="end"
|
||||
app:layout_constraintTop_toTopOf="@id/auto_download_switch"
|
||||
app:layout_constraintBottom_toBottomOf="@id/auto_download_switch"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/auto_download_switch"/>
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
style="@style/material_switch_style"
|
||||
android:id="@+id/auto_download_switch"
|
||||
android:onClick="@{() -> viewModel.toggleAutoDownload()}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:checked="@{viewModel.autoDownloadEnabled}"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/settings_title_style"
|
||||
android:onClick="@{() -> viewModel.toggleExportMedia()}"
|
||||
android:id="@+id/export_media_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:text="@string/settings_conversations_export_media_title"
|
||||
android:maxLines="2"
|
||||
android:ellipsize="end"
|
||||
app:layout_constraintTop_toTopOf="@id/export_media_switch"
|
||||
app:layout_constraintBottom_toTopOf="@id/export_media_subtitle"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/export_media_switch"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/settings_subtitle_style"
|
||||
android:onClick="@{() -> viewModel.toggleExportMedia()}"
|
||||
android:id="@+id/export_media_subtitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:text="@string/settings_conversations_export_media_subtitle"
|
||||
app:layout_constraintTop_toBottomOf="@id/export_media_title"
|
||||
app:layout_constraintBottom_toBottomOf="@id/export_media_switch"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/export_media_switch"/>
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
style="@style/material_switch_style"
|
||||
android:id="@+id/export_media_switch"
|
||||
android:onClick="@{() -> viewModel.toggleExportMedia()}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:checked="@{viewModel.exportMediaEnabled}"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/auto_download_switch" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</layout>
|
||||
|
|
@ -16,6 +16,44 @@
|
|||
android:paddingBottom="20dp"
|
||||
android:background="@drawable/shape_squircle_white_background">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/settings_title_style"
|
||||
android:id="@+id/layout_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:text="@string/settings_meetings_default_layout_title"
|
||||
android:maxLines="2"
|
||||
android:ellipsize="end"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatSpinner
|
||||
style="@style/material_switch_style"
|
||||
android:id="@+id/layout_spinner"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:background="@drawable/edit_text_background"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/layout_title"
|
||||
app:layout_constraintStart_toStartOf="@id/layout_title"
|
||||
app:layout_constraintEnd_toEndOf="@id/layout_title" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/layout_spinner_caret"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:src="@drawable/caret_down"
|
||||
app:layout_constraintTop_toTopOf="@id/layout_spinner"
|
||||
app:layout_constraintBottom_toBottomOf="@id/layout_spinner"
|
||||
app:layout_constraintEnd_toEndOf="@id/layout_spinner"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</layout>
|
||||
|
|
@ -267,7 +267,13 @@
|
|||
<string name="settings_calls_vibrate_while_ringing_title">Vibrate while incoming call is ringing</string>
|
||||
<string name="settings_calls_auto_record_title">Automatically start recording calls</string>
|
||||
<string name="settings_conversations_title">Conversations</string>
|
||||
<string name="settings_conversations_auto_download_title">Auto-download files</string>
|
||||
<string name="settings_conversations_export_media_title">Export media in native gallery</string>
|
||||
<string name="settings_conversations_export_media_subtitle">Media from ephemeral messages will never be exported</string>
|
||||
<string name="settings_meetings_title">Meetings</string>
|
||||
<string name="settings_meetings_default_layout_title">Default layout</string>
|
||||
<string name="settings_meetings_layout_active_speaker_label">Active speaker</string>
|
||||
<string name="settings_meetings_layout_mosaic_label">Mosaic</string>
|
||||
<string name="settings_network_title">Network</string>
|
||||
<string name="settings_network_use_wifi_only">Use only Wi-Fi networks</string>
|
||||
<string name="settings_user_interface_title">User interface</string>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue