From d6935d65d601aaf68074995aa602b9b0029a7f9f Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Thu, 2 Jan 2020 12:31:47 +0100 Subject: [PATCH] Move to compat shortcut manager --- app/build.gradle | 3 +- app/src/main/AndroidManifest.xml | 22 ++- .../application/NextcloudTalkApplication.kt | 7 +- .../talk/controllers/ContactsController.kt | 6 +- .../talk/jobs/PushRegistrationWorker.kt | 5 +- .../offline/ConversationsRepositoryImpl.kt | 4 +- .../talk/newarch/di/module/ServiceModule.kt | 3 +- .../offline/ConversationsRepository.kt | 2 +- .../newarch/local/dao/ConversationsDao.kt | 4 +- .../services/shortcuts/ShortcutService.kt | 149 ++++++++++++++++++ .../talk/newarch/utils/ShortcutService.kt | 123 --------------- .../nextcloud/talk/utils/bundle/BundleKeys.kt | 1 + app/src/main/res/values/setup.xml | 3 + app/src/main/res/xml/shortcuts.xml | 29 ++++ 14 files changed, 217 insertions(+), 144 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/newarch/services/shortcuts/ShortcutService.kt delete mode 100644 app/src/main/java/com/nextcloud/talk/newarch/utils/ShortcutService.kt create mode 100644 app/src/main/res/xml/shortcuts.xml diff --git a/app/build.gradle b/app/build.gradle index 334ce38e9..933b35a37 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -191,7 +191,6 @@ dependencies { implementation "com.github.stateless4j:stateless4j:2.6.0" // ViewModel and LiveData - implementation "androidx.core:core-ktx:1.1.0" implementation "androidx.sqlite:sqlite-ktx:2.0.1" implementation "androidx.collection:collection-ktx:1.1.0" implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" @@ -229,6 +228,8 @@ dependencies { androidTestImplementation "androidx.work:work-testing:$work_version" implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.core:core-ktx:1.2.0-rc01' + implementation 'androidx.sharetarget:sharetarget:1.0.0-rc01' implementation 'com.google.android.material:material:1.2.0-alpha03' implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4' implementation 'com.github.vanniktech:Emoji:0.6.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 81814b1c6..7251809c5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -63,9 +63,9 @@ + + + + + - = Build.VERSION_CODES.P) { - val shortcutService: ShortcutService = get() - } + val shortcutService: ShortcutService = get() Security.insertProviderAt(Conscrypt.newProvider(), 1) ClosedInterfaceImpl().providerInstallerInstallIfNeededAsync() diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.kt index 0f57d4e10..217b0f774 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ContactsController.kt @@ -664,9 +664,9 @@ class ContactsController : BaseController, private fun prepareViews() { layoutManager = SmoothScrollLinearLayoutManager(activity!!) - recyclerView!!.layoutManager = layoutManager - recyclerView!!.setHasFixedSize(true) - recyclerView!!.adapter = adapter + recyclerView?.layoutManager = layoutManager + recyclerView?.setHasFixedSize(true) + recyclerView?.adapter = adapter adapter!!.setStickyHeaderElevation(5) .setUnlinkAllItemsOnRemoveHeaders(true) diff --git a/app/src/main/java/com/nextcloud/talk/jobs/PushRegistrationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/PushRegistrationWorker.kt index 2a8d4fa76..864a388fe 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/PushRegistrationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/PushRegistrationWorker.kt @@ -23,7 +23,6 @@ package com.nextcloud.talk.jobs import android.annotation.SuppressLint import android.app.Application import android.content.Context -import android.text.TextUtils import android.util.Base64 import androidx.work.CoroutineWorker import androidx.work.ListenableWorker.Result @@ -68,8 +67,8 @@ class PushRegistrationWorker( } private fun pushRegistrationToServer() { - val token: String = appPreferences.pushToken - if (!TextUtils.isEmpty(token)) { + val token: String? = appPreferences.pushToken + if (!token.isNullOrEmpty()) { var credentials: String val pushUtils = PushUtils(usersRepository) val pushTokenHash = token.hashWithAlgorithm("SHA-512") diff --git a/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/ConversationsRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/ConversationsRepositoryImpl.kt index 765e0ef02..9824e09fe 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/ConversationsRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/ConversationsRepositoryImpl.kt @@ -65,8 +65,8 @@ class ConversationsRepositoryImpl(val conversationsDao: ConversationsDao) : } } - override fun getLastThreeActiveConversationsForUser(userId: Long): LiveData> { - return conversationsDao.getLastThreeConversationsForUser(userId).distinctUntilChanged().map { data -> + override fun getShortcutTargetConversations(userId: Long): LiveData> { + return conversationsDao.getShortcutTargetConversations(userId).distinctUntilChanged().map { data -> data.map { it.toConversation() } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/di/module/ServiceModule.kt b/app/src/main/java/com/nextcloud/talk/newarch/di/module/ServiceModule.kt index f921f57ea..d90ad7ca9 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/di/module/ServiceModule.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/di/module/ServiceModule.kt @@ -6,7 +6,7 @@ import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository import com.nextcloud.talk.newarch.domain.usecases.GetConversationUseCase import com.nextcloud.talk.newarch.domain.usecases.JoinConversationUseCase import com.nextcloud.talk.newarch.services.GlobalService -import com.nextcloud.talk.newarch.utils.ShortcutService +import com.nextcloud.talk.newarch.services.shortcuts.ShortcutService import okhttp3.OkHttpClient import org.koin.dsl.module import java.net.CookieManager @@ -14,7 +14,6 @@ import java.net.CookieManager val ServiceModule = module { single { createGlobalService(get(), get(), get(), get(), get(), get()) } single { createShortcutService(get(), get(), get()) } - } fun createGlobalService(usersRepository: UsersRepository, cookieManager: CookieManager, diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/offline/ConversationsRepository.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/offline/ConversationsRepository.kt index d965ea25a..b5cdc65ba 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/offline/ConversationsRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/offline/ConversationsRepository.kt @@ -25,7 +25,7 @@ import com.nextcloud.talk.models.json.conversations.Conversation interface ConversationsRepository { fun getConversationsForUser(userId: Long): LiveData> - fun getLastThreeActiveConversationsForUser(userId: Long): LiveData> + fun getShortcutTargetConversations(userId: Long): LiveData> suspend fun getConversationForUserWithToken(userId: Long, token: String): Conversation? suspend fun clearConversationsForUser(userId: Long) diff --git a/app/src/main/java/com/nextcloud/talk/newarch/local/dao/ConversationsDao.kt b/app/src/main/java/com/nextcloud/talk/newarch/local/dao/ConversationsDao.kt index 9999fd942..0bf841a0f 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/local/dao/ConversationsDao.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/local/dao/ConversationsDao.kt @@ -30,8 +30,8 @@ abstract class ConversationsDao { @Query("SELECT * FROM conversations WHERE user_id = :userId ORDER BY favorite DESC, last_activity DESC") abstract fun getConversationsForUser(userId: Long): LiveData> - @Query("SELECT * FROM conversations WHERE user_id = :userId ORDER BY favorite DESC, last_activity DESC LIMIT 3") - abstract fun getLastThreeConversationsForUser(userId: Long): LiveData> + @Query("SELECT * FROM conversations WHERE user_id = :userId ORDER BY favorite DESC, last_activity DESC LIMIT 4") + abstract fun getShortcutTargetConversations(userId: Long): LiveData> @Query("DELETE FROM conversations WHERE user_id = :userId") abstract suspend fun clearConversationsForUser(userId: Long) diff --git a/app/src/main/java/com/nextcloud/talk/newarch/services/shortcuts/ShortcutService.kt b/app/src/main/java/com/nextcloud/talk/newarch/services/shortcuts/ShortcutService.kt new file mode 100644 index 000000000..15e963f3e --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/newarch/services/shortcuts/ShortcutService.kt @@ -0,0 +1,149 @@ +/* + * + * * Nextcloud Talk application + * * + * * @author Mario Danic + * * Copyright (C) 2017-2020 Mario Danic + * * + * * 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 com.nextcloud.talk.newarch.services.shortcuts + +import android.content.Context +import android.content.Intent +import android.graphics.drawable.BitmapDrawable +import androidx.core.app.Person +import androidx.core.content.pm.ShortcutInfoCompat +import androidx.core.content.pm.ShortcutManagerCompat +import androidx.core.graphics.drawable.IconCompat +import androidx.core.graphics.drawable.toBitmap +import androidx.lifecycle.LiveData +import androidx.lifecycle.Transformations +import coil.Coil +import coil.api.get +import coil.transform.CircleCropTransformation +import com.nextcloud.talk.R +import com.nextcloud.talk.activities.MainActivity +import com.nextcloud.talk.models.json.conversations.Conversation +import com.nextcloud.talk.newarch.domain.repository.offline.ConversationsRepository +import com.nextcloud.talk.newarch.local.models.UserNgEntity +import com.nextcloud.talk.newarch.local.models.getCredentials +import com.nextcloud.talk.newarch.services.GlobalService +import com.nextcloud.talk.newarch.utils.Images +import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.bundle.BundleKeys +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlin.math.abs + + +class ShortcutService constructor(private var context: Context, + private val conversationsRepository: ConversationsRepository, + globalService: GlobalService +) { + private var currentUser: UserNgEntity? = null + private var lastFourActiveConversations: LiveData> = Transformations.switchMap(globalService.currentUserLiveData) { user -> + currentUser = user + var internalUserId: Long = -1 + currentUser?.let { + internalUserId = it.id!! + } + conversationsRepository.getShortcutTargetConversations(internalUserId) + } + + init { + lastFourActiveConversations.observeForever { + GlobalScope.launch { + registerShortcuts(it) + } + } + } + + private suspend fun registerShortcuts(conversations: List) { + val openNewConversationIntent = Intent(context, MainActivity::class.java) + openNewConversationIntent.action = BundleKeys.KEY_NEW_CONVERSATION + + val shortcuts: MutableList = mutableListOf() + val contactCategories: MutableSet = HashSet() + contactCategories.add(context.getString(R.string.nc_text_share_target)) + + val images = Images() + + currentUser?.let { user -> + shortcuts.add(ShortcutInfoCompat.Builder(context, "new") + //.setRank(4) + .setShortLabel(context.resources.getString(R.string.nc_new_conversation)) + .setIcon(IconCompat.createWithBitmap(context.resources.getDrawable(R.drawable.new_conversation_shortcut).toBitmap())) + .setIntent(openNewConversationIntent) + .setAlwaysBadged() + .build()) + + for ((index, conversation) in conversations.withIndex()) { + val intent = Intent(context, MainActivity::class.java) + intent.action = BundleKeys.KEY_OPEN_CONVERSATION + intent.putExtra(BundleKeys.KEY_INTERNAL_USER_ID, user.id) + intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, conversation.token) + + val persons = mutableListOf() + conversation.participants?.forEach { + val hashMap = it.value as HashMap<*, *> + val personBuilder = Person.Builder() + personBuilder.setName(hashMap["name"].toString()) + personBuilder.setBot(false) + // we need a key for each of the users + + /*val isGuest = hashMap["type"]?.equals(Participant.ParticipantType.GUEST) == true + || hashMap["type"]?.equals(Participant.ParticipantType.GUEST_AS_MODERATOR) == true + || hashMap["type"]?.equals(Participant.ParticipantType.USER_FOLLOWING_LINK) == true + + val avatarUrl = if (isGuest) ApiUtils.getUrlForAvatarWithNameForGuests(user.baseUrl, hashMap["name"].toString(), R.dimen.avatar_size_big) + else ApiUtils.getUrlForAvatarWithName(user.baseUrl, hashMap["userId"].toString(), R.dimen.avatar_size_big) + + iconImage = Coil.get(avatarUrl) { + addHeader("Authorization", user.getCredentials()) + transformations(CircleCropTransformation()) + } + personBuilder.setIcon(IconCompat.createWithBitmap((iconImage as BitmapDrawable).bitmap))*/ + persons.add(personBuilder.build()) + } + + var iconImage = images.getImageForConversation(context, conversation) + + if (iconImage == null) { + iconImage = Coil.get(ApiUtils.getUrlForAvatarWithName(user.baseUrl, conversation.name, R.dimen.avatar_size_big)) { + addHeader("Authorization", user.getCredentials()) + transformations(CircleCropTransformation()) + } + } + + shortcuts.add(ShortcutInfoCompat.Builder(context, "current_conversation_" + (index + 1)) + .setShortLabel(conversation.displayName as String) + .setLongLabel(conversation.displayName as String) + .setIcon(IconCompat.createWithBitmap((iconImage as BitmapDrawable).bitmap)) + .setIntent(intent) + .setRank(abs(index - 4 + 1)) + .setRank(0) + .setAlwaysBadged() + .setCategories(contactCategories) + .setPersons(persons.toTypedArray()) + .build()) + } + } + + ShortcutManagerCompat.removeAllDynamicShortcuts(context) + ShortcutManagerCompat.addDynamicShortcuts(context, shortcuts) + } +} diff --git a/app/src/main/java/com/nextcloud/talk/newarch/utils/ShortcutService.kt b/app/src/main/java/com/nextcloud/talk/newarch/utils/ShortcutService.kt deleted file mode 100644 index 88ec4d34d..000000000 --- a/app/src/main/java/com/nextcloud/talk/newarch/utils/ShortcutService.kt +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Nextcloud Talk application - * - * @author Thomas Ebert - * @author Mario Danic - * Copyright (C) 2017-2019 Mario Danic - * - * 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 com.nextcloud.talk.newarch.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.graphics.drawable.BitmapDrawable -import android.graphics.drawable.Icon -import android.os.Build -import androidx.annotation.RequiresApi -import androidx.core.graphics.drawable.toBitmap -import androidx.lifecycle.LiveData -import androidx.lifecycle.Transformations -import coil.Coil -import coil.api.get -import coil.transform.CircleCropTransformation -import com.nextcloud.talk.R -import com.nextcloud.talk.activities.MainActivity -import com.nextcloud.talk.models.json.conversations.Conversation -import com.nextcloud.talk.newarch.domain.repository.offline.ConversationsRepository -import com.nextcloud.talk.newarch.local.models.UserNgEntity -import com.nextcloud.talk.newarch.local.models.getCredentials -import com.nextcloud.talk.newarch.services.GlobalService -import com.nextcloud.talk.utils.ApiUtils -import com.nextcloud.talk.utils.bundle.BundleKeys -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch - -class ShortcutService constructor(private val context: Context, - private val conversationsRepository: ConversationsRepository, - conversationsService: GlobalService -) { - private var currentUser: UserNgEntity? = null - @RequiresApi(Build.VERSION_CODES.N_MR1) - private val shortcutManager = context.getSystemService(ShortcutManager::class.java) - - @RequiresApi(Build.VERSION_CODES.P) - private var lastThreeActiveConversations: LiveData> = Transformations.switchMap(conversationsService.currentUserLiveData) { user -> - currentUser = user - var internalUserId: Long = -1 - currentUser?.let { - internalUserId = it.id!! - } - conversationsRepository.getLastThreeActiveConversationsForUser(internalUserId) - } - - init { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - lastThreeActiveConversations.observeForever { - GlobalScope.launch { - registerShortcuts() - } - } - } - } - - @TargetApi(Build.VERSION_CODES.P) - private suspend fun registerShortcuts() { - val openNewConversationIntent = Intent(context, MainActivity::class.java) - openNewConversationIntent.action = BundleKeys.KEY_NEW_CONVERSATION - - val shortcuts: MutableList = mutableListOf() - val images = Images() - - currentUser?.let { user -> - shortcuts.add(ShortcutInfo.Builder(context, "new") - .setShortLabel(context.resources.getString(R.string.nc_new_conversation_short)) - .setLongLabel(context.resources.getString(R.string.nc_new_conversation)) - .setIcon(Icon.createWithBitmap(context.resources.getDrawable(R.drawable.new_conversation_shortcut).toBitmap())) - .setIntent(openNewConversationIntent) - .build()) - - lastThreeActiveConversations.value?.let { conversations -> - for ((index, conversation) in conversations.withIndex()) { - val intent = Intent(context, MainActivity::class.java) - intent.action = BundleKeys.KEY_OPEN_CONVERSATION - intent.putExtra(BundleKeys.KEY_INTERNAL_USER_ID, user.id) - intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, conversation.token) - - var iconImage = images.getImageForConversation(context, conversation) - - if (iconImage == null) { - iconImage = Coil.get(ApiUtils.getUrlForAvatarWithName(user.baseUrl, conversation.name, R.dimen.avatar_size_big)) { - addHeader("Authorization", user.getCredentials()) - transformations(CircleCropTransformation()) - } - } - - shortcuts.add(ShortcutInfo.Builder(context, "current_conversation_" + (index + 1)) - .setShortLabel(conversation.displayName as String) - .setLongLabel(conversation.displayName as String) - .setIcon(Icon.createWithBitmap((iconImage as BitmapDrawable).bitmap)) - .setIntent(intent) - .build()) - } - } - } - - shortcutManager?.dynamicShortcuts = shortcuts - } -} diff --git a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt index 1b9f4b26d..ecfb7d923 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt @@ -60,4 +60,5 @@ object BundleKeys { val KEY_ACCOUNT = "KEY_ACCOUNT" val KEY_FILE_ID = "KEY_FILE_ID" val KEY_NOTIFICATION_ID = "KEY_NOTIFICATION_ID" + val KEY_CONVERSATION_ID = "KEY_CONVERSATION_ID" } diff --git a/app/src/main/res/values/setup.xml b/app/src/main/res/values/setup.xml index ddfeb9157..34b172c44 100644 --- a/app/src/main/res/values/setup.xml +++ b/app/src/main/res/values/setup.xml @@ -50,4 +50,7 @@ 1:829118773643:android:54b65087c544d819 AIzaSyAWIyOcLafaFp8PFL61h64cy1NNZW2cU_s nextcloud-a7dea.appspot.com + + + com.nextcloud.talk.newarch.services.shortcuts.category.TEXT_SHARE_TARGET diff --git a/app/src/main/res/xml/shortcuts.xml b/app/src/main/res/xml/shortcuts.xml new file mode 100644 index 000000000..f14f6e834 --- /dev/null +++ b/app/src/main/res/xml/shortcuts.xml @@ -0,0 +1,29 @@ + + + + + + + + + \ No newline at end of file