diff --git a/app/src/main/java/org/linphone/activities/main/MainActivity.kt b/app/src/main/java/org/linphone/activities/main/MainActivity.kt index 042b62df3..bedc39a33 100644 --- a/app/src/main/java/org/linphone/activities/main/MainActivity.kt +++ b/app/src/main/java/org/linphone/activities/main/MainActivity.kt @@ -57,9 +57,11 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin private val listener = object : ContactsUpdatedListenerStub() { override fun onContactsUpdated() { + Log.i("[Main Activity] Contact(s) updated, update shortcuts") if (corePreferences.contactsShortcuts) { - Log.i("[Main Activity] Contact(s) updated, update shortcuts") Compatibility.createShortcutsToContacts(this@MainActivity) + } else if (corePreferences.chatRoomShortcuts) { + Compatibility.createShortcutsToChatRooms(this@MainActivity) } } } @@ -89,8 +91,6 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin startActivity(intent) } - if (intent != null) handleIntentParams(intent) - if (coreContext.core.proxyConfigList.isEmpty()) { if (corePreferences.firstStart) { corePreferences.firstStart = false @@ -122,6 +122,8 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin override fun onPostCreate(savedInstanceState: Bundle?) { super.onPostCreate(savedInstanceState) findNavController(R.id.nav_host_fragment).addOnDestinationChangedListener(this) + + if (intent != null) handleIntentParams(intent) } override fun onDestroy() { @@ -194,8 +196,16 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin findNavController(R.id.nav_host_fragment).navigate(Uri.parse(deepLink)) } intent.hasExtra("Chat") -> { - Log.i("[Main Activity] Found chat intent extra, go to chat rooms list") - findNavController(R.id.nav_host_fragment).navigate(R.id.action_global_masterChatRoomsFragment) + val deepLink = if (intent.hasExtra("RemoteSipUri") && intent.hasExtra("LocalSipUri")) { + val peerAddress = intent.getStringExtra("RemoteSipUri") + val localAddress = intent.getStringExtra("LocalSipUri") + Log.i("[Main Activity] Found chat room intent extra: local SIP URI=[$localAddress], peer SIP URI=[$peerAddress]") + "linphone-android://chat-room/$localAddress/$peerAddress" + } else { + Log.i("[Main Activity] Found chat intent extra, go to chat rooms list") + "linphone-android://chat/" + } + findNavController(R.id.nav_host_fragment).navigate(Uri.parse(deepLink)) } intent.hasExtra("Dialer") -> { Log.i("[Main Activity] Found dialer intent extra, go to dialer") diff --git a/app/src/main/java/org/linphone/activities/main/chat/fragments/MasterChatRoomsFragment.kt b/app/src/main/java/org/linphone/activities/main/chat/fragments/MasterChatRoomsFragment.kt index 510a031b6..483527391 100644 --- a/app/src/main/java/org/linphone/activities/main/chat/fragments/MasterChatRoomsFragment.kt +++ b/app/src/main/java/org/linphone/activities/main/chat/fragments/MasterChatRoomsFragment.kt @@ -196,7 +196,7 @@ class MasterChatRoomsFragment : MasterFragment() { val localSipUri = arguments?.getString("LocalSipUri") val remoteSipUri = arguments?.getString("RemoteSipUri") if (localSipUri != null && remoteSipUri != null) { - Log.i("[Chat] Found local ($localSipUri) & remote addresses ($remoteSipUri) in arguments") + Log.i("[Chat] Found local [$localSipUri] & remote [$remoteSipUri] addresses in arguments") arguments?.clear() val localAddress = Factory.instance().createAddress(localSipUri) val remoteSipAddress = Factory.instance().createAddress(remoteSipUri) diff --git a/app/src/main/java/org/linphone/activities/main/settings/fragments/ChatSettingsFragment.kt b/app/src/main/java/org/linphone/activities/main/settings/fragments/ChatSettingsFragment.kt index eed6cd9ff..4bc4bc9d1 100644 --- a/app/src/main/java/org/linphone/activities/main/settings/fragments/ChatSettingsFragment.kt +++ b/app/src/main/java/org/linphone/activities/main/settings/fragments/ChatSettingsFragment.kt @@ -24,10 +24,12 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment +import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import androidx.navigation.fragment.findNavController import org.linphone.R import org.linphone.activities.main.settings.viewmodels.ChatSettingsViewModel +import org.linphone.compatibility.Compatibility import org.linphone.databinding.SettingsChatFragmentBinding class ChatSettingsFragment : Fragment() { @@ -53,5 +55,15 @@ class ChatSettingsFragment : Fragment() { binding.setBackClickListener { findNavController().popBackStack() } binding.back.visibility = if (resources.getBoolean(R.bool.isTablet)) View.INVISIBLE else View.VISIBLE + + viewModel.launcherShortcutsEvent.observe(viewLifecycleOwner, Observer { + it.consume { newValue -> + if (newValue) { + Compatibility.createShortcutsToChatRooms(requireContext()) + } else { + Compatibility.removeShortcuts(requireContext()) + } + } + }) } } diff --git a/app/src/main/java/org/linphone/activities/main/settings/fragments/ContactsSettingsFragment.kt b/app/src/main/java/org/linphone/activities/main/settings/fragments/ContactsSettingsFragment.kt index 38edbe1a0..0968ed7b9 100644 --- a/app/src/main/java/org/linphone/activities/main/settings/fragments/ContactsSettingsFragment.kt +++ b/app/src/main/java/org/linphone/activities/main/settings/fragments/ContactsSettingsFragment.kt @@ -27,6 +27,7 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import androidx.navigation.fragment.findNavController +import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.R import org.linphone.activities.main.settings.viewmodels.ContactsSettingsViewModel import org.linphone.compatibility.Compatibility @@ -61,7 +62,10 @@ class ContactsSettingsFragment : Fragment() { if (newValue) { Compatibility.createShortcutsToContacts(requireContext()) } else { - Compatibility.removeShortcutsToContacts(requireContext()) + Compatibility.removeShortcuts(requireContext()) + if (corePreferences.chatRoomShortcuts) { + Compatibility.createShortcutsToChatRooms(requireContext()) + } } } }) diff --git a/app/src/main/java/org/linphone/activities/main/settings/viewmodels/ChatSettingsViewModel.kt b/app/src/main/java/org/linphone/activities/main/settings/viewmodels/ChatSettingsViewModel.kt index 6dcb5ef42..d7fd7fc1b 100644 --- a/app/src/main/java/org/linphone/activities/main/settings/viewmodels/ChatSettingsViewModel.kt +++ b/app/src/main/java/org/linphone/activities/main/settings/viewmodels/ChatSettingsViewModel.kt @@ -21,6 +21,7 @@ package org.linphone.activities.main.settings.viewmodels import androidx.lifecycle.MutableLiveData import org.linphone.activities.main.settings.SettingListenerStub +import org.linphone.utils.Event class ChatSettingsViewModel : GenericSettingsViewModel() { val fileSharingUrlListener = object : SettingListenerStub() { @@ -37,6 +38,15 @@ class ChatSettingsViewModel : GenericSettingsViewModel() { } val downloadedImagesPublic = MutableLiveData() + val launcherShortcutsListener = object : SettingListenerStub() { + override fun onBoolValueChanged(newValue: Boolean) { + prefs.chatRoomShortcuts = newValue + launcherShortcutsEvent.value = Event(newValue) + } + } + val launcherShortcuts = MutableLiveData() + val launcherShortcutsEvent = MutableLiveData>() + val hideEmptyRoomsListener = object : SettingListenerStub() { override fun onBoolValueChanged(newValue: Boolean) { prefs.hideEmptyRooms = newValue @@ -53,6 +63,7 @@ class ChatSettingsViewModel : GenericSettingsViewModel() { init { downloadedImagesPublic.value = prefs.makePublicDownloadedImages + launcherShortcuts.value = prefs.chatRoomShortcuts hideEmptyRooms.value = prefs.hideEmptyRooms hideRoomsRemovedProxies.value = prefs.hideRoomsFromRemovedProxies fileSharingUrl.value = core.fileTransferServer diff --git a/app/src/main/java/org/linphone/compatibility/Api25Compatibility.kt b/app/src/main/java/org/linphone/compatibility/Api25Compatibility.kt index 2d66d357a..084ee9d71 100644 --- a/app/src/main/java/org/linphone/compatibility/Api25Compatibility.kt +++ b/app/src/main/java/org/linphone/compatibility/Api25Compatibility.kt @@ -24,7 +24,7 @@ import android.bluetooth.BluetoothAdapter import android.content.Context import android.os.Build import android.provider.Settings -import org.linphone.contact.ShortcutsHelper +import org.linphone.utils.ShortcutsHelper @TargetApi(25) class Api25Compatibility { @@ -52,7 +52,11 @@ class Api25Compatibility { ShortcutsHelper.createShortcutsToContacts(context) } - fun removeShortcutsToContacts(context: Context) { + fun createShortcutsToChatRooms(context: Context) { + ShortcutsHelper.createShortcutsToChatRooms(context) + } + + fun removeShortcuts(context: Context) { ShortcutsHelper.removeShortcuts(context) } } diff --git a/app/src/main/java/org/linphone/compatibility/Compatibility.kt b/app/src/main/java/org/linphone/compatibility/Compatibility.kt index 8f115069d..5d11747be 100644 --- a/app/src/main/java/org/linphone/compatibility/Compatibility.kt +++ b/app/src/main/java/org/linphone/compatibility/Compatibility.kt @@ -95,14 +95,20 @@ class Compatibility { } } - fun removeShortcutsToContacts(context: Context) { + fun removeShortcuts(context: Context) { if (Version.sdkAboveOrEqual(Version.API25_NOUGAT_71)) { - Api25Compatibility.removeShortcutsToContacts(context) + Api25Compatibility.removeShortcuts(context) } } /* Chat */ + fun createShortcutsToChatRooms(context: Context) { + if (Version.sdkAboveOrEqual(Version.API25_NOUGAT_71)) { + Api25Compatibility.createShortcutsToChatRooms(context) + } + } + fun addImageToMediaStore(context: Context, content: Content): Boolean { if (Version.sdkAboveOrEqual(Version.API29_ANDROID_10)) { return Api29Compatibility.addImageToMediaStore(context, content) diff --git a/app/src/main/java/org/linphone/core/CorePreferences.kt b/app/src/main/java/org/linphone/core/CorePreferences.kt index 67cdfa847..8557f8201 100644 --- a/app/src/main/java/org/linphone/core/CorePreferences.kt +++ b/app/src/main/java/org/linphone/core/CorePreferences.kt @@ -108,6 +108,12 @@ class CorePreferences constructor(private val context: Context) { get() = config.getString("app", "device_name", Compatibility.getDeviceName(context)) set(value) = config.setString("app", "device_name", value) + var chatRoomShortcuts: Boolean + get() = config.getBool("app", "chat_room_shortcuts", true) + set(value) { + config.setBool("app", "chat_room_shortcuts", value) + } + /* Contacts */ // TODO: use it @@ -124,9 +130,9 @@ class CorePreferences constructor(private val context: Context) { } var contactsShortcuts: Boolean - get() = config.getBool("app", "shortcuts", true) + get() = config.getBool("app", "contact_shortcuts", false) set(value) { - config.setBool("app", "shortcuts", value) + config.setBool("app", "contact_shortcuts", value) } /* Call */ diff --git a/app/src/main/java/org/linphone/contact/ShortcutsHelper.kt b/app/src/main/java/org/linphone/utils/ShortcutsHelper.kt similarity index 55% rename from app/src/main/java/org/linphone/contact/ShortcutsHelper.kt rename to app/src/main/java/org/linphone/utils/ShortcutsHelper.kt index c0bc9d4b0..37f57e262 100644 --- a/app/src/main/java/org/linphone/contact/ShortcutsHelper.kt +++ b/app/src/main/java/org/linphone/utils/ShortcutsHelper.kt @@ -17,18 +17,25 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.linphone.contact +package org.linphone.utils import android.annotation.TargetApi import android.content.Context import android.content.Intent import android.content.pm.ShortcutInfo import android.content.pm.ShortcutManager +import android.os.Bundle import androidx.collection.ArraySet +import androidx.core.app.Person import androidx.core.content.pm.ShortcutInfoCompat +import androidx.core.graphics.drawable.IconCompat import org.linphone.LinphoneApplication.Companion.coreContext +import org.linphone.R import org.linphone.activities.main.MainActivity +import org.linphone.contact.Contact +import org.linphone.contact.NativeContact import org.linphone.core.Address +import org.linphone.core.ChatRoom import org.linphone.core.ChatRoomCapabilities import org.linphone.core.tools.Log @@ -114,6 +121,90 @@ class ShortcutsHelper(val context: Context) { return null } + fun createShortcutsToChatRooms(context: Context) { + val shortcuts = ArrayList() + val shortcutManager = context.getSystemService(ShortcutManager::class.java) + if (shortcutManager.isRateLimitingActive) { + Log.e("[Shortcut Helper] Rate limiting is active, aborting") + return + } + + val maxShortcuts = shortcutManager.maxShortcutCountPerActivity + var count = 0 + for (room in coreContext.core.chatRooms) { + // Android can usually only have around 4-5 shortcuts at a time + if (count >= maxShortcuts) { + Log.w("[Shortcut Helper] Max amount of shortcuts reached ($count)") + break + } + + val shortcut: ShortcutInfo? = createChatRoomShortcut(context, room) + if (shortcut != null) { + Log.i("[Shortcut Helper] Creating launcher shortcut for ${shortcut.shortLabel}") + shortcuts.add(shortcut) + count += 1 + } + } + shortcutManager.dynamicShortcuts = shortcuts + } + + private fun createChatRoomShortcut(context: Context, chatRoom: ChatRoom): ShortcutInfo? { + try { + val categories: ArraySet = ArraySet() + categories.add(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION) + val peerAddress = chatRoom.peerAddress.asStringUriOnly() + val localAddress = chatRoom.localAddress.asStringUriOnly() + + val personsList = arrayListOf() + var subject = "" + var icon: IconCompat + if (chatRoom.hasCapability(ChatRoomCapabilities.Basic.toInt())) { + val contact = + coreContext.contactsManager.findContactByAddress(chatRoom.peerAddress) + if (contact != null) { + personsList.add(contact.getPerson()) + } + subject = contact?.fullName ?: LinphoneUtils.getDisplayName(chatRoom.peerAddress) + icon = contact?.getPerson()?.icon ?: IconCompat.createWithResource(context, R.drawable.avatar) + } else { + for (participant in chatRoom.participants) { + val contact = + coreContext.contactsManager.findContactByAddress(participant.address) + if (contact != null) { + personsList.add(contact.getPerson()) + } + } + subject = chatRoom.subject + icon = IconCompat.createWithResource(context, R.drawable.chat_group_avatar) + } + + val persons = arrayOfNulls(personsList.size) + personsList.toArray(persons) + + val args = Bundle() + args.putString("RemoteSipUri", peerAddress) + args.putString("LocalSipUri", localAddress) + + val intent = Intent(Intent.ACTION_MAIN) + intent.setClass(context, MainActivity::class.java) + intent.putExtra("Chat", true) + intent.putExtra("RemoteSipUri", peerAddress) + intent.putExtra("LocalSipUri", localAddress) + + return ShortcutInfoCompat.Builder(context, "$localAddress#$peerAddress") + .setShortLabel(subject) + .setIcon(icon) + .setPersons(persons) + .setCategories(categories) + .setIntent(intent) + .build().toShortcutInfo() + } catch (e: Exception) { + Log.e("[Shortcuts Helper] ShortcutInfo.Builder exception: $e") + } + + return null + } + fun removeShortcuts(context: Context) { Log.w("[Shortcut Helper] Removing all contacts shortcuts") val shortcutManager = context.getSystemService(ShortcutManager::class.java) diff --git a/app/src/main/res/layout/settings_chat_fragment.xml b/app/src/main/res/layout/settings_chat_fragment.xml index 767110df4..c425cd766 100644 --- a/app/src/main/res/layout/settings_chat_fragment.xml +++ b/app/src/main/res/layout/settings_chat_fragment.xml @@ -80,6 +80,13 @@ linphone:listener="@{viewModel.downloadedImagesPublicListener}" linphone:checked="@={viewModel.downloadedImagesPublic}"/> + + + Do not edit unless you know what you are doing! Make downloaded images visible in native gallery Images in ephemeral messages won\'t be + Create shortcuts to chat rooms in launcher + Will be replaced by contacts shortcuts if enabled Hide empty chat rooms Hide chat rooms from removed proxy configs @@ -371,8 +373,8 @@ Inserting information shortcuts from the &appName; contact into native Android contacts Display contact organization - Create shortcuts in launcher - + Create shortcuts to contacts in launcher + Will replace chat room shortcuts if any Debug logs