Added a contacts cache at app level + get notified on low memory and clear glide cache when it happens
This commit is contained in:
parent
37cb30047e
commit
536e78e98e
12 changed files with 71 additions and 19 deletions
|
@ -20,7 +20,9 @@
|
|||
package org.linphone.activities.main
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ComponentCallbacks2
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
|
@ -58,6 +60,7 @@ import org.linphone.databinding.MainActivityBinding
|
|||
import org.linphone.utils.AppUtils
|
||||
import org.linphone.utils.Event
|
||||
import org.linphone.utils.FileUtils
|
||||
import org.linphone.utils.GlideApp
|
||||
|
||||
class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestinationChangedListener {
|
||||
private lateinit var binding: MainActivityBinding
|
||||
|
@ -84,6 +87,19 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin
|
|||
private var initPosY = 0f
|
||||
private var overlay: View? = null
|
||||
|
||||
private val componentCallbacks = object : ComponentCallbacks2 {
|
||||
override fun onConfigurationChanged(newConfig: Configuration) { }
|
||||
|
||||
override fun onLowMemory() {
|
||||
Log.w("[Main Activity] onLowMemory !")
|
||||
}
|
||||
|
||||
override fun onTrimMemory(level: Int) {
|
||||
Log.w("[Main Activity] onTrimMemory called with level $level !")
|
||||
GlideApp.get(this@MainActivity).clearMemory()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLayoutChanges(foldingFeature: FoldingFeature?) {
|
||||
sharedViewModel.layoutChangedEvent.value = Event(true)
|
||||
}
|
||||
|
@ -155,6 +171,7 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin
|
|||
|
||||
override fun onPostCreate(savedInstanceState: Bundle?) {
|
||||
super.onPostCreate(savedInstanceState)
|
||||
registerComponentCallbacks(componentCallbacks)
|
||||
findNavController(R.id.nav_host_fragment).addOnDestinationChangedListener(this)
|
||||
|
||||
if (intent != null) handleIntentParams(intent)
|
||||
|
@ -162,6 +179,7 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin
|
|||
|
||||
override fun onDestroy() {
|
||||
findNavController(R.id.nav_host_fragment).removeOnDestinationChangedListener(this)
|
||||
unregisterComponentCallbacks(componentCallbacks)
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ class MasterChatRoomsFragment : MasterFragment<ChatRoomMasterFragmentBinding, Ch
|
|||
}
|
||||
})*/
|
||||
|
||||
/* End of hared view model & sliding pane related */
|
||||
/* End of shared view model & sliding pane related */
|
||||
|
||||
_adapter = ChatRoomsListAdapter(listSelectionViewModel, viewLifecycleOwner)
|
||||
// SubmitList is done on a background thread
|
||||
|
|
|
@ -108,7 +108,7 @@ class MasterContactsFragment : MasterFragment<ContactMasterFragmentBinding, Cont
|
|||
}
|
||||
})*/
|
||||
|
||||
/* End of hared view model & sliding pane related */
|
||||
/* End of shared view model & sliding pane related */
|
||||
|
||||
_adapter = ContactsListAdapter(listSelectionViewModel, viewLifecycleOwner)
|
||||
binding.contactsList.setHasFixedSize(true)
|
||||
|
|
|
@ -111,7 +111,7 @@ class MasterCallLogsFragment : MasterFragment<HistoryMasterFragmentBinding, Call
|
|||
}
|
||||
})*/
|
||||
|
||||
/* End of hared view model & sliding pane related */
|
||||
/* End of shared view model & sliding pane related */
|
||||
|
||||
_adapter = CallLogsListAdapter(listSelectionViewModel, viewLifecycleOwner)
|
||||
// SubmitList is done on a background thread
|
||||
|
|
|
@ -97,6 +97,9 @@ class CallLogsListViewModel : ViewModel() {
|
|||
}
|
||||
|
||||
private fun updateCallLogs() {
|
||||
callLogs.value.orEmpty().forEach(GroupedCallLogData::destroy)
|
||||
missedCallLogs.value.orEmpty().forEach(GroupedCallLogData::destroy)
|
||||
|
||||
val list = arrayListOf<GroupedCallLogData>()
|
||||
val missedList = arrayListOf<GroupedCallLogData>()
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ class SettingsFragment : SecureFragment<SettingsFragmentBinding>() {
|
|||
})
|
||||
binding.slidingPane.lockMode = SlidingPaneLayout.LOCK_MODE_LOCKED
|
||||
|
||||
/* End of hared view model & sliding pane related */
|
||||
/* End of shared view model & sliding pane related */
|
||||
|
||||
viewModel = ViewModelProvider(this).get(SettingsViewModel::class.java)
|
||||
binding.viewModel = viewModel
|
||||
|
|
|
@ -25,6 +25,7 @@ import android.app.Activity
|
|||
import android.bluetooth.BluetoothAdapter
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
|
@ -63,6 +64,10 @@ class Api21Compatibility {
|
|||
vibrator.vibrate(pattern, -1)
|
||||
}
|
||||
|
||||
fun getBitmapFromUri(context: Context, uri: Uri): Bitmap {
|
||||
return MediaStore.Images.Media.getBitmap(context.contentResolver, uri)
|
||||
}
|
||||
|
||||
suspend fun addImageToMediaStore(context: Context, content: Content): Boolean {
|
||||
if (!PermissionHelper.get().hasWriteExternalStorage()) {
|
||||
Log.e("[Media Store] Write external storage permission denied")
|
||||
|
|
|
@ -25,6 +25,8 @@ import android.app.NotificationManager
|
|||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.ImageDecoder
|
||||
import android.net.Uri
|
||||
import android.os.Environment
|
||||
import android.provider.MediaStore
|
||||
|
@ -83,6 +85,10 @@ class Api29Compatibility {
|
|||
return bubblesAllowed
|
||||
}
|
||||
|
||||
fun getBitmapFromUri(context: Context, uri: Uri): Bitmap {
|
||||
return ImageDecoder.decodeBitmap(ImageDecoder.createSource(context.contentResolver, uri))
|
||||
}
|
||||
|
||||
suspend fun addImageToMediaStore(context: Context, content: Content): Boolean {
|
||||
val filePath = content.filePath
|
||||
if (filePath == null) {
|
||||
|
|
|
@ -23,6 +23,8 @@ import android.app.Activity
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.os.Vibrator
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
|
@ -74,6 +76,14 @@ class Compatibility {
|
|||
}
|
||||
}
|
||||
|
||||
fun getBitmapFromUri(context: Context, uri: Uri): Bitmap {
|
||||
return if (Version.sdkStrictlyBelow(Version.API29_ANDROID_10)) {
|
||||
Api21Compatibility.getBitmapFromUri(context, uri)
|
||||
} else {
|
||||
Api29Compatibility.getBitmapFromUri(context, uri)
|
||||
}
|
||||
}
|
||||
|
||||
/* Notifications */
|
||||
|
||||
fun createNotificationChannels(
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
package org.linphone.contact
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import org.linphone.LinphoneApplication
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.activities.main.viewmodels.ErrorReportingViewModel
|
||||
import org.linphone.core.Address
|
||||
import org.linphone.core.ChatRoomSecurityLevel
|
||||
|
@ -50,18 +50,18 @@ open class GenericContactData(private val sipAddress: Address) : ContactDataInte
|
|||
|
||||
init {
|
||||
securityLevel.value = ChatRoomSecurityLevel.ClearText
|
||||
LinphoneApplication.coreContext.contactsManager.addListener(contactsUpdatedListener)
|
||||
coreContext.contactsManager.addListener(contactsUpdatedListener)
|
||||
contactLookup()
|
||||
}
|
||||
|
||||
open fun destroy() {
|
||||
LinphoneApplication.coreContext.contactsManager.removeListener(contactsUpdatedListener)
|
||||
coreContext.contactsManager.removeListener(contactsUpdatedListener)
|
||||
}
|
||||
|
||||
private fun contactLookup() {
|
||||
displayName.value = LinphoneUtils.getDisplayName(sipAddress)
|
||||
contact.value =
|
||||
LinphoneApplication.coreContext.contactsManager.findContactByAddress(sipAddress)
|
||||
coreContext.contactsManager.findContactByAddress(sipAddress)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,18 +78,18 @@ abstract class GenericContactViewModel(private val sipAddress: Address) : ErrorR
|
|||
|
||||
init {
|
||||
securityLevel.value = ChatRoomSecurityLevel.ClearText
|
||||
LinphoneApplication.coreContext.contactsManager.addListener(contactsUpdatedListener)
|
||||
coreContext.contactsManager.addListener(contactsUpdatedListener)
|
||||
contactLookup()
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
LinphoneApplication.coreContext.contactsManager.removeListener(contactsUpdatedListener)
|
||||
coreContext.contactsManager.removeListener(contactsUpdatedListener)
|
||||
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
private fun contactLookup() {
|
||||
displayName.value = LinphoneUtils.getDisplayName(sipAddress)
|
||||
contact.value = LinphoneApplication.coreContext.contactsManager.findContactByAddress(sipAddress)
|
||||
contact.value = coreContext.contactsManager.findContactByAddress(sipAddress)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,6 +93,8 @@ class ContactsManager(private val context: Context) {
|
|||
|
||||
var latestContactFetch: String = ""
|
||||
|
||||
private val friendsMap: HashMap<String, Friend> = HashMap()
|
||||
|
||||
private val contactsUpdatedListeners = ArrayList<ContactsUpdatedListener>()
|
||||
|
||||
private var loadContactsTask: AsyncContactsLoader? = null
|
||||
|
@ -227,7 +229,9 @@ class ContactsManager(private val context: Context) {
|
|||
|
||||
@Synchronized
|
||||
fun findContactByPhoneNumber(number: String): Contact? {
|
||||
val friend: Friend? = coreContext.core.findFriendByPhoneNumber(number)
|
||||
val cacheFriend = friendsMap[number]
|
||||
val friend: Friend? = cacheFriend ?: coreContext.core.findFriendByPhoneNumber(number)
|
||||
if (cacheFriend == null && friend != null) friendsMap[number] = friend
|
||||
return friend?.userData as? Contact
|
||||
}
|
||||
|
||||
|
@ -240,8 +244,14 @@ class ContactsManager(private val context: Context) {
|
|||
}
|
||||
if (localContact != null) return localContact
|
||||
|
||||
val cleanAddress = address.clone()
|
||||
cleanAddress.clean() // To remove gruu if any
|
||||
val cleanStringAddress = cleanAddress.asStringUriOnly()
|
||||
|
||||
val cacheFriend = friendsMap[cleanStringAddress]
|
||||
val friend: Friend? = coreContext.core.findFriend(address)
|
||||
val contact: Contact? = friend?.userData as? Contact
|
||||
if (cacheFriend == null && friend != null) friendsMap[cleanStringAddress] = friend
|
||||
if (contact != null) return contact
|
||||
|
||||
val username = address.username
|
||||
|
@ -283,12 +293,14 @@ class ContactsManager(private val context: Context) {
|
|||
context.contentResolver.unregisterContentObserver(contactsObserver)
|
||||
loadContactsTask?.cancel(true)
|
||||
|
||||
friendsMap.clear()
|
||||
// Contact has a Friend field and Friend can have a Contact has userData
|
||||
// Friend also keeps a ref on the Core, so we have to clean them
|
||||
for (contact in contacts) {
|
||||
contact.friend = null
|
||||
}
|
||||
contacts.clear()
|
||||
|
||||
for (contact in sipContacts) {
|
||||
contact.friend = null
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import android.net.Uri
|
|||
import android.provider.MediaStore
|
||||
import java.io.File
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.compatibility.Compatibility
|
||||
import org.linphone.core.tools.Log
|
||||
|
||||
class ImageUtils {
|
||||
|
@ -36,22 +37,19 @@ class ImageUtils {
|
|||
fromPictureUri: Uri?
|
||||
): Bitmap? {
|
||||
var bm: Bitmap? = null
|
||||
val roundBm: Bitmap?
|
||||
if (fromPictureUri != null) {
|
||||
bm = try {
|
||||
@Suppress("DEPRECATION")
|
||||
MediaStore.Images.Media.getBitmap(
|
||||
context.contentResolver, fromPictureUri
|
||||
)
|
||||
Compatibility.getBitmapFromUri(context, fromPictureUri)
|
||||
} catch (e: Exception) {
|
||||
Log.e("[Image Utils] Failed to get bitmap from URI [$fromPictureUri]: $e")
|
||||
return null
|
||||
}
|
||||
}
|
||||
if (bm != null) {
|
||||
roundBm = getRoundBitmap(bm)
|
||||
val roundBm = getRoundBitmap(bm)
|
||||
if (roundBm != null) {
|
||||
bm.recycle()
|
||||
bm = roundBm
|
||||
return roundBm
|
||||
}
|
||||
}
|
||||
return bm
|
||||
|
|
Loading…
Reference in a new issue