diff --git a/app/src/main/java/org/linphone/LinphoneApplication.kt b/app/src/main/java/org/linphone/LinphoneApplication.kt
index f1dbd61ab..800aa390c 100644
--- a/app/src/main/java/org/linphone/LinphoneApplication.kt
+++ b/app/src/main/java/org/linphone/LinphoneApplication.kt
@@ -131,7 +131,7 @@ class LinphoneApplication : Application(), ImageLoaderFactory {
.maxSizePercent(0.02)
.build()
}
- .networkCachePolicy(CachePolicy.DISABLED)
+ .networkCachePolicy(CachePolicy.ENABLED)
.diskCachePolicy(diskCachePolicy)
.memoryCachePolicy(CachePolicy.ENABLED)
.build()
diff --git a/app/src/main/java/org/linphone/ui/main/fragment/DrawerMenuFragment.kt b/app/src/main/java/org/linphone/ui/main/fragment/DrawerMenuFragment.kt
index 38312ce11..e371f13fc 100644
--- a/app/src/main/java/org/linphone/ui/main/fragment/DrawerMenuFragment.kt
+++ b/app/src/main/java/org/linphone/ui/main/fragment/DrawerMenuFragment.kt
@@ -20,6 +20,7 @@
package org.linphone.ui.main.fragment
import android.content.Intent
+import android.net.Uri
import android.os.Bundle
import android.view.Gravity
import android.view.LayoutInflater
@@ -129,6 +130,19 @@ class DrawerMenuFragment : GenericMainFragment() {
}
}
+ viewModel.openLinkInBrowserEvent.observe(viewLifecycleOwner) {
+ it.consume { link ->
+ try {
+ val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
+ startActivity(browserIntent)
+ } catch (ise: IllegalStateException) {
+ Log.e(
+ "$TAG Can't start ACTION_VIEW intent for URL [$link], IllegalStateException: $ise"
+ )
+ }
+ }
+ }
+
sharedViewModel.refreshDrawerMenuAccountsListEvent.observe(viewLifecycleOwner) {
it.consume { recreate ->
if (recreate) {
diff --git a/app/src/main/java/org/linphone/ui/main/model/ShortcutModel.kt b/app/src/main/java/org/linphone/ui/main/model/ShortcutModel.kt
new file mode 100644
index 000000000..76069794f
--- /dev/null
+++ b/app/src/main/java/org/linphone/ui/main/model/ShortcutModel.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2010-2024 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-android
+ * (see https://www.linphone.org).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.linphone.ui.main.model
+
+data class ShortcutModel(
+ val label: String,
+ val iconUrl: String,
+ val link: String,
+ private val lambda: (link: String) -> Unit
+) {
+ fun clicked() {
+ lambda.invoke(link)
+ }
+}
diff --git a/app/src/main/java/org/linphone/ui/main/viewmodel/DrawerMenuViewModel.kt b/app/src/main/java/org/linphone/ui/main/viewmodel/DrawerMenuViewModel.kt
index c5ec60294..af2784337 100644
--- a/app/src/main/java/org/linphone/ui/main/viewmodel/DrawerMenuViewModel.kt
+++ b/app/src/main/java/org/linphone/ui/main/viewmodel/DrawerMenuViewModel.kt
@@ -32,6 +32,7 @@ import org.linphone.core.CoreListenerStub
import org.linphone.core.tools.Log
import org.linphone.ui.GenericViewModel
import org.linphone.ui.main.model.AccountModel
+import org.linphone.ui.main.model.ShortcutModel
import org.linphone.utils.Event
class DrawerMenuViewModel @UiThread constructor() : GenericViewModel() {
@@ -47,6 +48,8 @@ class DrawerMenuViewModel @UiThread constructor() : GenericViewModel() {
val hideSettings = MutableLiveData()
+ val shortcuts = MutableLiveData>()
+
val startAssistantEvent: MutableLiveData> by lazy {
MutableLiveData>()
}
@@ -63,6 +66,10 @@ class DrawerMenuViewModel @UiThread constructor() : GenericViewModel() {
MutableLiveData>()
}
+ val openLinkInBrowserEvent: MutableLiveData> by lazy {
+ MutableLiveData>()
+ }
+
private val coreListener = object : CoreListenerStub() {
@WorkerThread
override fun onDefaultAccountChanged(core: Core, account: Account?) {
@@ -104,8 +111,9 @@ class DrawerMenuViewModel @UiThread constructor() : GenericViewModel() {
if (status != ConfiguringState.Skipped) {
accounts.value.orEmpty().forEach(AccountModel::destroy)
- Log.i("$TAG Configuring status is [$status], reload accounts")
+ Log.i("$TAG Configuring status is [$status], reload accounts & shortcuts")
computeAccountsList()
+ computeShortcuts()
}
}
}
@@ -118,6 +126,7 @@ class DrawerMenuViewModel @UiThread constructor() : GenericViewModel() {
hideSettings.postValue(corePreferences.hideSettings)
computeAccountsList()
+ computeShortcuts()
}
}
@@ -173,7 +182,7 @@ class DrawerMenuViewModel @UiThread constructor() : GenericViewModel() {
val maxAccount = corePreferences.maxAccountsCount
val accountsCount = list.size
- val maxAccountsReached = maxAccount > 0 && accountsCount >= maxAccount
+ val maxAccountsReached = maxAccount in 1..accountsCount
if (maxAccountsReached) {
Log.w(
"$TAG Max number of allowed accounts reached [$maxAccount], hiding add account button"
@@ -181,4 +190,29 @@ class DrawerMenuViewModel @UiThread constructor() : GenericViewModel() {
}
hideAddAccount.postValue(maxAccountsReached)
}
+
+ @WorkerThread
+ private fun computeShortcuts() {
+ val config = coreContext.core.config
+ val shortcutsList = arrayListOf()
+ val shortcutsCount = config.getInt("ui", "shortcut_count", 0)
+ if (shortcutsCount > 0) {
+ Log.i("$TAG Found [$shortcutsCount] shortcuts to display")
+ for (i in 0 until shortcutsCount) {
+ val key = "shortcut_$i"
+ val label = config.getString(key, "name", "").orEmpty()
+ val iconUrl = config.getString(key, "icon", "").orEmpty()
+ val linkUrl = config.getString(key, "link", "").orEmpty()
+ val shortcutModel = ShortcutModel(
+ label,
+ iconUrl,
+ linkUrl
+ ) { link ->
+ openLinkInBrowserEvent.postValue(Event(link))
+ }
+ shortcutsList.add(shortcutModel)
+ }
+ }
+ shortcuts.postValue(shortcutsList)
+ }
}
diff --git a/app/src/main/java/org/linphone/utils/DataBindingUtils.kt b/app/src/main/java/org/linphone/utils/DataBindingUtils.kt
index 0a49f185c..cee98b1ae 100644
--- a/app/src/main/java/org/linphone/utils/DataBindingUtils.kt
+++ b/app/src/main/java/org/linphone/utils/DataBindingUtils.kt
@@ -284,6 +284,22 @@ fun AppCompatTextView.setColor(@ColorRes color: Int) {
setTextColor(context.getColor(color))
}
+@UiThread
+@BindingAdapter("coilUrl")
+fun ImageView.loadUrlImage(url: String?) {
+ if (!url.isNullOrEmpty()) {
+ load(url) {
+ listener(
+ onError = { _, result ->
+ Log.e(
+ "$TAG Error getting shortcut icon from URL [$url]: ${result.throwable}"
+ )
+ }
+ )
+ }
+ }
+}
+
@UiThread
@BindingAdapter("coilFile")
fun ImageView.loadFileImage(file: String?) {
diff --git a/app/src/main/res/drawable/arrow_square_out.xml b/app/src/main/res/drawable/arrow_square_out.xml
new file mode 100644
index 000000000..ff1badb6c
--- /dev/null
+++ b/app/src/main/res/drawable/arrow_square_out.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/drawer_menu.xml b/app/src/main/res/layout/drawer_menu.xml
index 6dc05b324..1709ebbfa 100644
--- a/app/src/main/res/layout/drawer_menu.xml
+++ b/app/src/main/res/layout/drawer_menu.xml
@@ -135,6 +135,21 @@
android:background="?attr/color_main2_200"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/shortcuts" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/settings_calls.xml b/app/src/main/res/layout/settings_calls.xml
index 0c9ead44d..43fe0e779 100644
--- a/app/src/main/res/layout/settings_calls.xml
+++ b/app/src/main/res/layout/settings_calls.xml
@@ -218,7 +218,7 @@
android:layout_marginTop="16dp"
android:layout_marginBottom="@dimen/screen_bottom_margin"
android:text="@string/settings_calls_change_ringtone_title"
- android:drawableEnd="@drawable/caret_right"
+ android:drawableEnd="@drawable/arrow_square_out"
android:drawableTint="?attr/color_main2_600"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toStartOf="parent"