mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-22 04:59:34 +01:00
Lots of cleanups & work on contacts
Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
parent
7efb05184c
commit
4901534da7
@ -38,11 +38,11 @@ import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler
|
|||||||
import com.google.android.material.appbar.MaterialToolbar
|
import com.google.android.material.appbar.MaterialToolbar
|
||||||
import com.nextcloud.talk.R
|
import com.nextcloud.talk.R
|
||||||
import com.nextcloud.talk.controllers.CallNotificationController
|
import com.nextcloud.talk.controllers.CallNotificationController
|
||||||
import com.nextcloud.talk.controllers.ContactsController
|
|
||||||
import com.nextcloud.talk.controllers.LockedController
|
import com.nextcloud.talk.controllers.LockedController
|
||||||
import com.nextcloud.talk.controllers.base.providers.ActionBarProvider
|
import com.nextcloud.talk.controllers.base.providers.ActionBarProvider
|
||||||
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
|
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
|
||||||
import com.nextcloud.talk.newarch.features.account.serverentry.ServerEntryView
|
import com.nextcloud.talk.newarch.features.account.serverentry.ServerEntryView
|
||||||
|
import com.nextcloud.talk.newarch.features.contactsflow.ContactsView
|
||||||
import com.nextcloud.talk.newarch.features.conversationslist.ConversationsListView
|
import com.nextcloud.talk.newarch.features.conversationslist.ConversationsListView
|
||||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||||
import com.nextcloud.talk.utils.ConductorRemapping
|
import com.nextcloud.talk.utils.ConductorRemapping
|
||||||
@ -102,12 +102,7 @@ class MainActivity : BaseActivity(), ActionBarProvider {
|
|||||||
|
|
||||||
@OnClick(R.id.floatingActionButton)
|
@OnClick(R.id.floatingActionButton)
|
||||||
fun onFloatingActionButtonClick() {
|
fun onFloatingActionButtonClick() {
|
||||||
val bundle = Bundle()
|
openNewConversationScreen()
|
||||||
bundle.putBoolean(BundleKeys.KEY_NEW_CONVERSATION, true)
|
|
||||||
router?.pushController(
|
|
||||||
RouterTransaction.with(ContactsController(bundle))
|
|
||||||
.pushChangeHandler(HorizontalChangeHandler())
|
|
||||||
.popChangeHandler(HorizontalChangeHandler()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
@ -150,7 +145,7 @@ class MainActivity : BaseActivity(), ActionBarProvider {
|
|||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
ConductorRemapping.remapChatController(
|
ConductorRemapping.remapChatController(
|
||||||
router!!, it.id!!,
|
router!!, it.id!!,
|
||||||
intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN)!!, extras, false)
|
intent.getStringExtra(BundleKeys.KEY_CONVERSATION_TOKEN)!!, extras, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,7 +159,7 @@ class MainActivity : BaseActivity(), ActionBarProvider {
|
|||||||
} else {
|
} else {
|
||||||
ConductorRemapping.remapChatController(
|
ConductorRemapping.remapChatController(
|
||||||
router!!, intent.getLongExtra(BundleKeys.KEY_INTERNAL_USER_ID, -1),
|
router!!, intent.getLongExtra(BundleKeys.KEY_INTERNAL_USER_ID, -1),
|
||||||
intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN)!!, intent.extras!!, false
|
intent.getStringExtra(BundleKeys.KEY_CONVERSATION_TOKEN)!!, intent.extras!!, false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,11 +176,8 @@ class MainActivity : BaseActivity(), ActionBarProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun openNewConversationScreen() {
|
private fun openNewConversationScreen() {
|
||||||
val bundle = Bundle()
|
|
||||||
bundle.putBoolean(BundleKeys.KEY_NEW_CONVERSATION, true)
|
|
||||||
|
|
||||||
router?.pushController(
|
router?.pushController(
|
||||||
RouterTransaction.with(ContactsController(bundle))
|
RouterTransaction.with(ContactsView())
|
||||||
.pushChangeHandler(HorizontalChangeHandler())
|
.pushChangeHandler(HorizontalChangeHandler())
|
||||||
.popChangeHandler(HorizontalChangeHandler())
|
.popChangeHandler(HorizontalChangeHandler())
|
||||||
)
|
)
|
||||||
|
@ -46,6 +46,7 @@ import com.nextcloud.talk.models.json.signaling.settings.SignalingSettings
|
|||||||
import com.nextcloud.talk.newarch.di.module.*
|
import com.nextcloud.talk.newarch.di.module.*
|
||||||
import com.nextcloud.talk.newarch.domain.di.module.UseCasesModule
|
import com.nextcloud.talk.newarch.domain.di.module.UseCasesModule
|
||||||
import com.nextcloud.talk.newarch.features.account.di.module.AccountModule
|
import com.nextcloud.talk.newarch.features.account.di.module.AccountModule
|
||||||
|
import com.nextcloud.talk.newarch.features.contactsflow.di.module.ContactsFlowModule
|
||||||
import com.nextcloud.talk.newarch.features.conversationslist.di.module.ConversationsListModule
|
import com.nextcloud.talk.newarch.features.conversationslist.di.module.ConversationsListModule
|
||||||
import com.nextcloud.talk.newarch.local.dao.UsersDao
|
import com.nextcloud.talk.newarch.local.dao.UsersDao
|
||||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||||
@ -175,7 +176,7 @@ class NextcloudTalkApplication : Application(), LifecycleObserver, Configuration
|
|||||||
startKoin {
|
startKoin {
|
||||||
androidContext(this@NextcloudTalkApplication)
|
androidContext(this@NextcloudTalkApplication)
|
||||||
androidLogger()
|
androidLogger()
|
||||||
modules(listOf(CommunicationModule, StorageModule, NetworkModule, ConversationsListModule, ServiceModule, AccountModule, UseCasesModule))
|
modules(listOf(CommunicationModule, StorageModule, NetworkModule, ConversationsListModule, ServiceModule, AccountModule, UseCasesModule, ContactsFlowModule))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ class BrowserController(args: Bundle) : BaseController(), ListingInterface, Flex
|
|||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
browserType = Parcels.unwrap(args.getParcelable(BundleKeys.KEY_BROWSER_TYPE))
|
browserType = Parcels.unwrap(args.getParcelable(BundleKeys.KEY_BROWSER_TYPE))
|
||||||
activeUser = Parcels.unwrap(args.getParcelable(BundleKeys.KEY_USER_ENTITY))
|
activeUser = Parcels.unwrap(args.getParcelable(BundleKeys.KEY_USER_ENTITY))
|
||||||
roomToken = args.getString(BundleKeys.KEY_ROOM_TOKEN)
|
roomToken = args.getString(BundleKeys.KEY_CONVERSATION_TOKEN)
|
||||||
|
|
||||||
currentPath = "/"
|
currentPath = "/"
|
||||||
if (BrowserType.DAV_BROWSER == browserType) {
|
if (BrowserType.DAV_BROWSER == browserType) {
|
||||||
@ -127,7 +127,7 @@ class BrowserController(args: Bundle) : BaseController(), ListingInterface, Flex
|
|||||||
if (paths.size == 10 || !iterator.hasNext()) {
|
if (paths.size == 10 || !iterator.hasNext()) {
|
||||||
data = Data.Builder()
|
data = Data.Builder()
|
||||||
.putLong(BundleKeys.KEY_INTERNAL_USER_ID, activeUser.id!!)
|
.putLong(BundleKeys.KEY_INTERNAL_USER_ID, activeUser.id!!)
|
||||||
.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
|
.putString(BundleKeys.KEY_CONVERSATION_TOKEN, roomToken)
|
||||||
.putStringArray(BundleKeys.KEY_FILE_PATHS, paths.toTypedArray())
|
.putStringArray(BundleKeys.KEY_FILE_PATHS, paths.toTypedArray())
|
||||||
.build()
|
.build()
|
||||||
shareWorker = OneTimeWorkRequest.Builder(ShareOperationWorker::class.java)
|
shareWorker = OneTimeWorkRequest.Builder(ShareOperationWorker::class.java)
|
||||||
|
@ -204,7 +204,7 @@ class CallController(args: Bundle) : BaseController() {
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
roomId = args.getString(BundleKeys.KEY_ROOM_ID, "")
|
roomId = args.getString(BundleKeys.KEY_ROOM_ID, "")
|
||||||
roomToken = args.getString(BundleKeys.KEY_ROOM_TOKEN, "")
|
roomToken = args.getString(BundleKeys.KEY_CONVERSATION_TOKEN, "")
|
||||||
conversationUser = args.getParcelable(BundleKeys.KEY_USER_ENTITY)
|
conversationUser = args.getParcelable(BundleKeys.KEY_USER_ENTITY)
|
||||||
conversationPassword = args.getString(BundleKeys.KEY_CONVERSATION_PASSWORD, "")
|
conversationPassword = args.getString(BundleKeys.KEY_CONVERSATION_PASSWORD, "")
|
||||||
isVoiceOnlyCall = args.getBoolean(BundleKeys.KEY_CALL_VOICE_ONLY, false)
|
isVoiceOnlyCall = args.getBoolean(BundleKeys.KEY_CALL_VOICE_ONLY, false)
|
||||||
|
@ -172,7 +172,7 @@ class CallNotificationController(private val originalBundle: Bundle) : BaseContr
|
|||||||
|
|
||||||
private fun proceedToCall() {
|
private fun proceedToCall() {
|
||||||
originalBundle.putString(
|
originalBundle.putString(
|
||||||
BundleKeys.KEY_ROOM_TOKEN,
|
BundleKeys.KEY_CONVERSATION_TOKEN,
|
||||||
currentConversation!!.token
|
currentConversation!!.token
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -179,7 +179,7 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
|
|
||||||
this.conversationUser = args.getParcelable(BundleKeys.KEY_USER_ENTITY)
|
this.conversationUser = args.getParcelable(BundleKeys.KEY_USER_ENTITY)
|
||||||
this.roomId = args.getString(BundleKeys.KEY_ROOM_ID, "")
|
this.roomId = args.getString(BundleKeys.KEY_ROOM_ID, "")
|
||||||
this.roomToken = args.getString(BundleKeys.KEY_ROOM_TOKEN, "")
|
this.roomToken = args.getString(BundleKeys.KEY_CONVERSATION_TOKEN, "")
|
||||||
|
|
||||||
if (args.containsKey(BundleKeys.KEY_ACTIVE_CONVERSATION)) {
|
if (args.containsKey(BundleKeys.KEY_ACTIVE_CONVERSATION)) {
|
||||||
this.currentConversation = Parcels.unwrap<Conversation>(
|
this.currentConversation = Parcels.unwrap<Conversation>(
|
||||||
@ -603,7 +603,7 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
BundleKeys.KEY_BROWSER_TYPE, Parcels.wrap<BrowserController.BrowserType>(browserType)
|
BundleKeys.KEY_BROWSER_TYPE, Parcels.wrap<BrowserController.BrowserType>(browserType)
|
||||||
)
|
)
|
||||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap<UserNgEntity>(conversationUser))
|
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap<UserNgEntity>(conversationUser))
|
||||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
|
bundle.putString(BundleKeys.KEY_CONVERSATION_TOKEN, roomToken)
|
||||||
router.pushController(
|
router.pushController(
|
||||||
RouterTransaction.with(BrowserController(bundle))
|
RouterTransaction.with(BrowserController(bundle))
|
||||||
.pushChangeHandler(VerticalChangeHandler())
|
.pushChangeHandler(VerticalChangeHandler())
|
||||||
@ -614,7 +614,7 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
private fun showConversationInfoScreen() {
|
private fun showConversationInfoScreen() {
|
||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationUser)
|
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationUser)
|
||||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
|
bundle.putString(BundleKeys.KEY_CONVERSATION_TOKEN, roomToken)
|
||||||
router.pushController(
|
router.pushController(
|
||||||
RouterTransaction.with(ConversationInfoController(bundle))
|
RouterTransaction.with(ConversationInfoController(bundle))
|
||||||
.pushChangeHandler(HorizontalChangeHandler())
|
.pushChangeHandler(HorizontalChangeHandler())
|
||||||
@ -1354,7 +1354,7 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
private fun getIntentForCall(isVoiceOnlyCall: Boolean): Intent? {
|
private fun getIntentForCall(isVoiceOnlyCall: Boolean): Intent? {
|
||||||
if (currentConversation != null) {
|
if (currentConversation != null) {
|
||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
|
bundle.putString(BundleKeys.KEY_CONVERSATION_TOKEN, roomToken)
|
||||||
bundle.putString(BundleKeys.KEY_ROOM_ID, roomId)
|
bundle.putString(BundleKeys.KEY_ROOM_ID, roomId)
|
||||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationUser)
|
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationUser)
|
||||||
bundle.putString(BundleKeys.KEY_CONVERSATION_PASSWORD, roomPassword)
|
bundle.putString(BundleKeys.KEY_CONVERSATION_PASSWORD, roomPassword)
|
||||||
@ -1497,7 +1497,7 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter
|
|||||||
val conversationIntent = Intent(activity, MagicCallActivity::class.java)
|
val conversationIntent = Intent(activity, MagicCallActivity::class.java)
|
||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationUser)
|
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationUser)
|
||||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.ocs.data.token)
|
bundle.putString(BundleKeys.KEY_CONVERSATION_TOKEN, roomOverall.ocs.data.token)
|
||||||
bundle.putString(BundleKeys.KEY_ROOM_ID, roomOverall.ocs.data.conversationId)
|
bundle.putString(BundleKeys.KEY_ROOM_ID, roomOverall.ocs.data.conversationId)
|
||||||
|
|
||||||
if (conversationUser != null) {
|
if (conversationUser != null) {
|
||||||
|
@ -266,7 +266,7 @@ class ContactsController : BaseController,
|
|||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, currentUser)
|
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, currentUser)
|
||||||
bundle.putString(
|
bundle.putString(
|
||||||
BundleKeys.KEY_ROOM_TOKEN,
|
BundleKeys.KEY_CONVERSATION_TOKEN,
|
||||||
roomOverall.ocs.data.token
|
roomOverall.ocs.data.token
|
||||||
)
|
)
|
||||||
bundle.putString(
|
bundle.putString(
|
||||||
@ -440,174 +440,7 @@ class ContactsController : BaseController,
|
|||||||
|
|
||||||
val query = adapter!!.getFilter(String::class.java)
|
val query = adapter!!.getFilter(String::class.java)
|
||||||
|
|
||||||
val retrofitBucket: RetrofitBucket = ApiUtils.getRetrofitBucketForContactsSearchFor14(currentUser!!.baseUrl, query)
|
|
||||||
val modifiedQueryMap = HashMap<String, Any>(retrofitBucket.queryMap)
|
|
||||||
modifiedQueryMap["limit"] = 50
|
|
||||||
|
|
||||||
var shareTypesList: MutableList<String> = mutableListOf()
|
|
||||||
// user
|
|
||||||
shareTypesList.add("0")
|
|
||||||
// group
|
|
||||||
shareTypesList.add("1")
|
|
||||||
// remote/circles
|
|
||||||
shareTypesList.add("7");
|
|
||||||
|
|
||||||
conversationToken?.let {
|
|
||||||
modifiedQueryMap["itemId"] = it
|
|
||||||
// emails
|
|
||||||
shareTypesList.add("4")
|
|
||||||
}
|
|
||||||
|
|
||||||
modifiedQueryMap["shareTypes[]"] = shareTypesList
|
|
||||||
|
|
||||||
ncApi.getContactsWithSearchParam(
|
|
||||||
credentials,
|
|
||||||
retrofitBucket.url, shareTypesList, modifiedQueryMap
|
|
||||||
)
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.retry(3)
|
|
||||||
.`as`(AutoDispose.autoDisposable(scopeProvider))
|
|
||||||
.subscribe(object : Observer<ResponseBody> {
|
|
||||||
override fun onSubscribe(d: Disposable) {}
|
|
||||||
|
|
||||||
override fun onNext(responseBody: ResponseBody) {
|
|
||||||
var participant: Participant
|
|
||||||
|
|
||||||
val newUserItemList = ArrayList<AbstractFlexibleItem<*>>()
|
|
||||||
|
|
||||||
try {
|
|
||||||
val autocompleteOverall =
|
|
||||||
LoganSquare.parse(responseBody.string(), AutocompleteOverall::class.java)
|
|
||||||
autocompleteUsersHashSet.addAll(autocompleteOverall.ocs.data)
|
|
||||||
|
|
||||||
for (autocompleteUser in autocompleteUsersHashSet) {
|
|
||||||
participant = Participant()
|
|
||||||
participant.userId = autocompleteUser.id
|
|
||||||
participant.displayName = autocompleteUser.label
|
|
||||||
participant.source = autocompleteUser.source
|
|
||||||
|
|
||||||
var headerTitle: String?
|
|
||||||
|
|
||||||
|
|
||||||
if (autocompleteUser.source == "groups") {
|
|
||||||
headerTitle = resources?.getString(R.string.nc_groups)
|
|
||||||
} else if (autocompleteUser.source == "users") {
|
|
||||||
headerTitle = resources?.getString(R.string.nc_contacts)
|
|
||||||
} else if (autocompleteUser.source == "emails") {
|
|
||||||
headerTitle = resources?.getString(R.string.nc_emails)
|
|
||||||
} else {
|
|
||||||
headerTitle = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
headerTitle as String
|
|
||||||
|
|
||||||
val genericTextHeaderItem: GenericTextHeaderItem
|
|
||||||
if (!userHeaderItems.containsKey(headerTitle)) {
|
|
||||||
genericTextHeaderItem = GenericTextHeaderItem(headerTitle)
|
|
||||||
userHeaderItems[headerTitle] = genericTextHeaderItem
|
|
||||||
}
|
|
||||||
|
|
||||||
val newContactItem = UserItem(
|
|
||||||
participant, currentUser!!,
|
|
||||||
userHeaderItems[headerTitle], activity!!
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!contactItems!!.contains(newContactItem)) {
|
|
||||||
newUserItemList.add(newContactItem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
Log.e(TAG, "Parsing response body failed while getting contacts")
|
|
||||||
}
|
|
||||||
|
|
||||||
userHeaderItems = HashMap()
|
|
||||||
contactItems!!.addAll(newUserItemList)
|
|
||||||
|
|
||||||
newUserItemList.sortWith(Comparator { o1, o2 ->
|
|
||||||
val firstName: String
|
|
||||||
val secondName: String
|
|
||||||
|
|
||||||
if (o1 is UserItem) {
|
|
||||||
firstName = o1.model.displayName
|
|
||||||
} else {
|
|
||||||
firstName = (o1 as GenericTextHeaderItem).model
|
|
||||||
}
|
|
||||||
|
|
||||||
if (o2 is UserItem) {
|
|
||||||
secondName = o2.model.displayName
|
|
||||||
} else {
|
|
||||||
secondName = (o2 as GenericTextHeaderItem).model
|
|
||||||
}
|
|
||||||
|
|
||||||
if (o1 is UserItem && o2 is UserItem) {
|
|
||||||
if ("groups" == o1.model.source && "groups" == o2.model.source) {
|
|
||||||
firstName.compareTo(secondName, ignoreCase = true)
|
|
||||||
} else if ("groups" == o1.model.source) {
|
|
||||||
-1
|
|
||||||
} else if ("groups" == o2.model.source) {
|
|
||||||
1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
firstName.compareTo(secondName, ignoreCase = true)
|
|
||||||
})
|
|
||||||
|
|
||||||
contactItems!!.sortWith(Comparator { o1, o2 ->
|
|
||||||
val firstName: String
|
|
||||||
val secondName: String
|
|
||||||
|
|
||||||
if (o1 is UserItem) {
|
|
||||||
firstName = o1.model.displayName
|
|
||||||
} else {
|
|
||||||
firstName = (o1 as GenericTextHeaderItem).model
|
|
||||||
}
|
|
||||||
|
|
||||||
if (o2 is UserItem) {
|
|
||||||
secondName = o2.model.displayName
|
|
||||||
} else {
|
|
||||||
secondName = (o2 as GenericTextHeaderItem).model
|
|
||||||
}
|
|
||||||
|
|
||||||
if (o1 is UserItem && o2 is UserItem) {
|
|
||||||
if ("groups" == o1.model.source && "groups" == o2.model.source) {
|
|
||||||
firstName.compareTo(secondName, ignoreCase = true)
|
|
||||||
} else if ("groups" == o1.model.source) {
|
|
||||||
-1
|
|
||||||
} else if ("groups" == o2.model.source) {
|
|
||||||
1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
firstName.compareTo(secondName, ignoreCase = true)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (newUserItemList.size > 0) {
|
|
||||||
adapter!!.updateDataSet(newUserItemList)
|
|
||||||
} else {
|
|
||||||
adapter!!.filterItems()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (swipeRefreshLayout != null) {
|
|
||||||
swipeRefreshLayout!!.isRefreshing = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: Throwable) {
|
|
||||||
if (swipeRefreshLayout != null) {
|
|
||||||
swipeRefreshLayout!!.isRefreshing = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onComplete() {
|
|
||||||
if (swipeRefreshLayout != null) {
|
|
||||||
swipeRefreshLayout!!.isRefreshing = false
|
|
||||||
}
|
|
||||||
alreadyFetching = false
|
|
||||||
|
|
||||||
disengageProgressBar()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prepareViews() {
|
private fun prepareViews() {
|
||||||
|
@ -179,7 +179,7 @@ class ConversationInfoController(args: Bundle) : BaseController(),
|
|||||||
get() {
|
get() {
|
||||||
if (!TextUtils.isEmpty(conversationToken) && conversationUser != null) {
|
if (!TextUtils.isEmpty(conversationToken) && conversationUser != null) {
|
||||||
val data = Data.Builder()
|
val data = Data.Builder()
|
||||||
data.putString(BundleKeys.KEY_ROOM_TOKEN, conversationToken)
|
data.putString(BundleKeys.KEY_CONVERSATION_TOKEN, conversationToken)
|
||||||
data.putLong(BundleKeys.KEY_INTERNAL_USER_ID, conversationUser.id!!)
|
data.putLong(BundleKeys.KEY_INTERNAL_USER_ID, conversationUser.id!!)
|
||||||
return data.build()
|
return data.build()
|
||||||
}
|
}
|
||||||
@ -190,7 +190,7 @@ class ConversationInfoController(args: Bundle) : BaseController(),
|
|||||||
init {
|
init {
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
conversationUser = args.getParcelable(BundleKeys.KEY_USER_ENTITY)
|
conversationUser = args.getParcelable(BundleKeys.KEY_USER_ENTITY)
|
||||||
conversationToken = args.getString(BundleKeys.KEY_ROOM_TOKEN)
|
conversationToken = args.getString(BundleKeys.KEY_CONVERSATION_TOKEN)
|
||||||
credentials = ApiUtils.getCredentials(conversationUser!!.username, conversationUser.token)
|
credentials = ApiUtils.getCredentials(conversationUser!!.username, conversationUser.token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -572,7 +572,7 @@ class OperationsMenuController(args: Bundle) : BaseController() {
|
|||||||
true, true, dismissView))
|
true, true, dismissView))
|
||||||
|
|
||||||
val conversationIntent = Intent(activity, MagicCallActivity::class.java)
|
val conversationIntent = Intent(activity, MagicCallActivity::class.java)
|
||||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, conversation!!.token)
|
bundle.putString(BundleKeys.KEY_CONVERSATION_TOKEN, conversation!!.token)
|
||||||
bundle.putString(BundleKeys.KEY_ROOM_ID, conversation!!.conversationId)
|
bundle.putString(BundleKeys.KEY_ROOM_ID, conversation!!.conversationId)
|
||||||
bundle.putString(BundleKeys.KEY_CONVERSATION_NAME,
|
bundle.putString(BundleKeys.KEY_CONVERSATION_NAME,
|
||||||
conversation!!.displayName)
|
conversation!!.displayName)
|
||||||
@ -604,7 +604,7 @@ class OperationsMenuController(args: Bundle) : BaseController() {
|
|||||||
private fun initiateCall() {
|
private fun initiateCall() {
|
||||||
eventBus.post(BottomSheetLockEvent(true, 0, true, true))
|
eventBus.post(BottomSheetLockEvent(true, 0, true, true))
|
||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, conversation!!.token)
|
bundle.putString(BundleKeys.KEY_CONVERSATION_TOKEN, conversation!!.token)
|
||||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, currentUser)
|
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, currentUser)
|
||||||
if (baseUrl != null && baseUrl != currentUser!!.baseUrl) {
|
if (baseUrl != null && baseUrl != currentUser!!.baseUrl) {
|
||||||
bundle.putString(BundleKeys.KEY_MODIFIED_BASE_URL, baseUrl)
|
bundle.putString(BundleKeys.KEY_MODIFIED_BASE_URL, baseUrl)
|
||||||
|
@ -30,7 +30,7 @@ import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
|||||||
import com.nextcloud.talk.newarch.local.models.getCredentials
|
import com.nextcloud.talk.newarch.local.models.getCredentials
|
||||||
import com.nextcloud.talk.utils.ApiUtils
|
import com.nextcloud.talk.utils.ApiUtils
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_TOKEN
|
||||||
import io.reactivex.Observer
|
import io.reactivex.Observer
|
||||||
import io.reactivex.disposables.Disposable
|
import io.reactivex.disposables.Disposable
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
@ -53,7 +53,7 @@ class DeleteConversationWorker(context: Context,
|
|||||||
override suspend fun doWork(): Result {
|
override suspend fun doWork(): Result {
|
||||||
val data = inputData
|
val data = inputData
|
||||||
val operationUserId = data.getLong(KEY_INTERNAL_USER_ID, -1)
|
val operationUserId = data.getLong(KEY_INTERNAL_USER_ID, -1)
|
||||||
val conversationToken = data.getString(KEY_ROOM_TOKEN)
|
val conversationToken = data.getString(KEY_CONVERSATION_TOKEN)
|
||||||
val operationUser: UserNgEntity? = usersRepository.getUserWithId(operationUserId)
|
val operationUser: UserNgEntity? = usersRepository.getUserWithId(operationUserId)
|
||||||
operationUser?.let {
|
operationUser?.let {
|
||||||
val credentials = it.getCredentials()
|
val credentials = it.getCredentials()
|
||||||
|
@ -30,7 +30,7 @@ import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
|||||||
import com.nextcloud.talk.newarch.local.models.getCredentials
|
import com.nextcloud.talk.newarch.local.models.getCredentials
|
||||||
import com.nextcloud.talk.utils.ApiUtils
|
import com.nextcloud.talk.utils.ApiUtils
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_TOKEN
|
||||||
import io.reactivex.Observer
|
import io.reactivex.Observer
|
||||||
import io.reactivex.disposables.Disposable
|
import io.reactivex.disposables.Disposable
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
@ -51,7 +51,7 @@ class LeaveConversationWorker(context: Context, workerParams: WorkerParameters)
|
|||||||
override fun doWork(): Result {
|
override fun doWork(): Result {
|
||||||
val data = inputData
|
val data = inputData
|
||||||
val operationUserId = data.getLong(KEY_INTERNAL_USER_ID, -1)
|
val operationUserId = data.getLong(KEY_INTERNAL_USER_ID, -1)
|
||||||
val conversationToken = data.getString(KEY_ROOM_TOKEN)
|
val conversationToken = data.getString(KEY_CONVERSATION_TOKEN)
|
||||||
val operationUser: UserNgEntity? = usersRepository.getUserWithId(operationUserId)
|
val operationUser: UserNgEntity? = usersRepository.getUserWithId(operationUserId)
|
||||||
if (operationUser != null) {
|
if (operationUser != null) {
|
||||||
val credentials = operationUser.getCredentials()
|
val credentials = operationUser.getCredentials()
|
||||||
|
@ -91,7 +91,7 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_SIGNATURE
|
|||||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_SUBJECT
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_SUBJECT
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_ID
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_ID
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_TOKEN
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY
|
||||||
import com.nextcloud.talk.utils.database.arbitrarystorage.ArbitraryStorageUtils
|
import com.nextcloud.talk.utils.database.arbitrarystorage.ArbitraryStorageUtils
|
||||||
import com.nextcloud.talk.utils.preferences.AppPreferences
|
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||||
@ -143,7 +143,7 @@ class NotificationWorker(
|
|||||||
arbitraryStorageEntity = arbitraryStorageUtils.getStorageSetting(
|
arbitraryStorageEntity = arbitraryStorageUtils.getStorageSetting(
|
||||||
userEntity.id!!,
|
userEntity.id!!,
|
||||||
"mute_calls",
|
"mute_calls",
|
||||||
intent.extras!!.getString(KEY_ROOM_TOKEN)
|
intent.extras!!.getString(KEY_CONVERSATION_TOKEN)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (arbitraryStorageEntity != null) {
|
if (arbitraryStorageEntity != null) {
|
||||||
@ -153,7 +153,7 @@ class NotificationWorker(
|
|||||||
arbitraryStorageEntity = arbitraryStorageUtils.getStorageSetting(
|
arbitraryStorageEntity = arbitraryStorageUtils.getStorageSetting(
|
||||||
userEntity.id!!,
|
userEntity.id!!,
|
||||||
"important_conversation",
|
"important_conversation",
|
||||||
intent.extras!!.getString(KEY_ROOM_TOKEN)
|
intent.extras!!.getString(KEY_CONVERSATION_TOKEN)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (arbitraryStorageEntity != null) {
|
if (arbitraryStorageEntity != null) {
|
||||||
@ -171,7 +171,7 @@ class NotificationWorker(
|
|||||||
ncApi!!.getRoom(
|
ncApi!!.getRoom(
|
||||||
credentials, ApiUtils.getRoom(
|
credentials, ApiUtils.getRoom(
|
||||||
userEntity.baseUrl,
|
userEntity.baseUrl,
|
||||||
intent.extras!!.getString(KEY_ROOM_TOKEN)
|
intent.extras!!.getString(KEY_CONVERSATION_TOKEN)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.blockingSubscribe(object : Observer<RoomOverall?> {
|
.blockingSubscribe(object : Observer<RoomOverall?> {
|
||||||
@ -358,7 +358,7 @@ class NotificationWorker(
|
|||||||
// could be an ID or a TOKEN
|
// could be an ID or a TOKEN
|
||||||
|
|
||||||
notificationInfo.putString(
|
notificationInfo.putString(
|
||||||
KEY_ROOM_TOKEN,
|
KEY_CONVERSATION_TOKEN,
|
||||||
decryptedPushMessage!!.id
|
decryptedPushMessage!!.id
|
||||||
)
|
)
|
||||||
notificationInfo.putLong(
|
notificationInfo.putLong(
|
||||||
@ -681,7 +681,7 @@ class NotificationWorker(
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
bundle.putString(
|
bundle.putString(
|
||||||
KEY_ROOM_TOKEN,
|
KEY_CONVERSATION_TOKEN,
|
||||||
decryptedPushMessage!!.id
|
decryptedPushMessage!!.id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -696,7 +696,7 @@ class NotificationWorker(
|
|||||||
intent.putExtras(bundle)
|
intent.putExtras(bundle)
|
||||||
when (decryptedPushMessage!!.type) {
|
when (decryptedPushMessage!!.type) {
|
||||||
"call" -> if (!bundle.containsKey(
|
"call" -> if (!bundle.containsKey(
|
||||||
KEY_ROOM_TOKEN
|
KEY_CONVERSATION_TOKEN
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
context!!.startActivity(intent)
|
context!!.startActivity(intent)
|
||||||
@ -704,7 +704,7 @@ class NotificationWorker(
|
|||||||
showNotificationForCallWithNoPing(intent)
|
showNotificationForCallWithNoPing(intent)
|
||||||
}
|
}
|
||||||
"room" -> if (bundle.containsKey(
|
"room" -> if (bundle.containsKey(
|
||||||
KEY_ROOM_TOKEN
|
KEY_CONVERSATION_TOKEN
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
showNotificationWithObjectData(intent)
|
showNotificationWithObjectData(intent)
|
||||||
|
@ -29,7 +29,7 @@ import com.nextcloud.talk.newarch.local.models.getCredentials
|
|||||||
import com.nextcloud.talk.utils.ApiUtils
|
import com.nextcloud.talk.utils.ApiUtils
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FILE_PATHS
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FILE_PATHS
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_TOKEN
|
||||||
import io.reactivex.Observer
|
import io.reactivex.Observer
|
||||||
import io.reactivex.disposables.Disposable
|
import io.reactivex.disposables.Disposable
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
@ -74,7 +74,7 @@ class ShareOperationWorker(
|
|||||||
init {
|
init {
|
||||||
val data = workerParams.inputData
|
val data = workerParams.inputData
|
||||||
userId = data.getLong(KEY_INTERNAL_USER_ID, 0)
|
userId = data.getLong(KEY_INTERNAL_USER_ID, 0)
|
||||||
roomToken = data.getString(KEY_ROOM_TOKEN)
|
roomToken = data.getString(KEY_CONVERSATION_TOKEN)
|
||||||
|
|
||||||
Collections.addAll(
|
Collections.addAll(
|
||||||
filesArray, *data.getStringArray(KEY_FILE_PATHS)
|
filesArray, *data.getStringArray(KEY_FILE_PATHS)
|
||||||
|
@ -21,12 +21,16 @@
|
|||||||
package com.nextcloud.talk.models.json.participants;
|
package com.nextcloud.talk.models.json.participants;
|
||||||
|
|
||||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||||
|
import com.bluelinelabs.logansquare.annotation.JsonIgnore;
|
||||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||||
import com.nextcloud.talk.models.json.converters.EnumParticipantFlagsConverter;
|
import com.nextcloud.talk.models.json.converters.EnumParticipantFlagsConverter;
|
||||||
import com.nextcloud.talk.models.json.converters.EnumParticipantTypeConverter;
|
import com.nextcloud.talk.models.json.converters.EnumParticipantTypeConverter;
|
||||||
|
|
||||||
import org.parceler.Parcel;
|
import org.parceler.Parcel;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@ -64,6 +68,7 @@ public class Participant {
|
|||||||
@JsonField(name = "source")
|
@JsonField(name = "source")
|
||||||
public String source;
|
public String source;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
public boolean selected;
|
public boolean selected;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -20,10 +20,12 @@
|
|||||||
|
|
||||||
package com.nextcloud.talk.newarch.data.repository.online
|
package com.nextcloud.talk.newarch.data.repository.online
|
||||||
|
|
||||||
|
import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall
|
||||||
import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall
|
import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall
|
||||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||||
|
import com.nextcloud.talk.models.json.participants.Participant
|
||||||
import com.nextcloud.talk.models.json.push.PushRegistrationOverall
|
import com.nextcloud.talk.models.json.push.PushRegistrationOverall
|
||||||
import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall
|
import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall
|
||||||
import com.nextcloud.talk.models.json.userprofile.UserProfileOverall
|
import com.nextcloud.talk.models.json.userprofile.UserProfileOverall
|
||||||
@ -89,6 +91,16 @@ class NextcloudTalkRepositoryImpl(private val apiService: ApiService) : Nextclou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getContactsForUser(user: UserNgEntity, searchQuery: String?, conversationToken: String?): List<Participant> {
|
||||||
|
return apiService.getContacts(authorization = user.getCredentials(), url = ApiUtils.getUrlForContactsSearch(user.baseUrl), shareTypes = ApiUtils.getShareTypesForContactsSearch(), options = ApiUtils.getQueryMapForContactsSearch(searchQuery, conversationToken)).ocs.data.map {
|
||||||
|
val participant = Participant()
|
||||||
|
participant.userId = it.id
|
||||||
|
participant.displayName = it.label
|
||||||
|
participant.source = it.source
|
||||||
|
participant
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun registerPushWithServerForUser(user: UserNgEntity, options: Map<String, String>): PushRegistrationOverall {
|
override suspend fun registerPushWithServerForUser(user: UserNgEntity, options: Map<String, String>): PushRegistrationOverall {
|
||||||
return apiService.registerForPushWithServer(user.getCredentials(), ApiUtils.getUrlNextcloudPush(user.baseUrl), options)
|
return apiService.registerForPushWithServer(user.getCredentials(), ApiUtils.getUrlNextcloudPush(user.baseUrl), options)
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
package com.nextcloud.talk.newarch.data.source.remote
|
package com.nextcloud.talk.newarch.data.source.remote
|
||||||
|
|
||||||
|
import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall
|
||||||
import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall
|
import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall
|
||||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||||
import com.nextcloud.talk.models.json.conversations.RoomsOverall
|
import com.nextcloud.talk.models.json.conversations.RoomsOverall
|
||||||
@ -31,6 +32,9 @@ import retrofit2.http.*
|
|||||||
|
|
||||||
interface ApiService {
|
interface ApiService {
|
||||||
|
|
||||||
|
@GET
|
||||||
|
suspend fun getContacts(@Header("Authorization") authorization: String, @Url url: String, @Query("limit") limit: Int = 50, @Query("shareTypes[]") shareTypes: List<String>, @QueryMap options: Map<String, String>): AutocompleteOverall
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
suspend fun getCapabilities(@Url url: String): CapabilitiesOverall
|
suspend fun getCapabilities(@Url url: String): CapabilitiesOverall
|
||||||
|
|
||||||
|
@ -47,9 +47,16 @@ val UseCasesModule = module {
|
|||||||
single { createRegisterPushWithServerUseCase(get(), get()) }
|
single { createRegisterPushWithServerUseCase(get(), get()) }
|
||||||
single { createUnregisterPushWithProxyUseCase(get(), get()) }
|
single { createUnregisterPushWithProxyUseCase(get(), get()) }
|
||||||
single { createUnregisterPushWithServerUseCase(get(), get()) }
|
single { createUnregisterPushWithServerUseCase(get(), get()) }
|
||||||
|
single { createGetContactsUseCase(get(), get()) }
|
||||||
factory { createChatViewModelFactory(get(), get(), get(), get(), get(), get()) }
|
factory { createChatViewModelFactory(get(), get(), get(), get(), get(), get()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createGetContactsUseCase(nextcloudTalkRepository: NextcloudTalkRepository,
|
||||||
|
apiErrorHandler: ApiErrorHandler
|
||||||
|
): GetContactsUseCase {
|
||||||
|
return GetContactsUseCase(nextcloudTalkRepository, apiErrorHandler)
|
||||||
|
}
|
||||||
|
|
||||||
fun createUnregisterPushWithServerUseCase(nextcloudTalkRepository: NextcloudTalkRepository,
|
fun createUnregisterPushWithServerUseCase(nextcloudTalkRepository: NextcloudTalkRepository,
|
||||||
apiErrorHandler: ApiErrorHandler
|
apiErrorHandler: ApiErrorHandler
|
||||||
): UnregisterPushWithServerUseCase {
|
): UnregisterPushWithServerUseCase {
|
||||||
|
@ -20,16 +20,19 @@
|
|||||||
|
|
||||||
package com.nextcloud.talk.newarch.domain.repository.online
|
package com.nextcloud.talk.newarch.domain.repository.online
|
||||||
|
|
||||||
|
import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall
|
||||||
import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall
|
import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall
|
||||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||||
|
import com.nextcloud.talk.models.json.participants.Participant
|
||||||
import com.nextcloud.talk.models.json.push.PushRegistrationOverall
|
import com.nextcloud.talk.models.json.push.PushRegistrationOverall
|
||||||
import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall
|
import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall
|
||||||
import com.nextcloud.talk.models.json.userprofile.UserProfileOverall
|
import com.nextcloud.talk.models.json.userprofile.UserProfileOverall
|
||||||
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
||||||
|
|
||||||
interface NextcloudTalkRepository {
|
interface NextcloudTalkRepository {
|
||||||
|
suspend fun getContactsForUser(user: UserNgEntity, searchQuery: String?, conversationToken: String?): List<Participant>
|
||||||
suspend fun registerPushWithServerForUser(user: UserNgEntity, options: Map<String, String>): PushRegistrationOverall
|
suspend fun registerPushWithServerForUser(user: UserNgEntity, options: Map<String, String>): PushRegistrationOverall
|
||||||
suspend fun unregisterPushWithServerForUser(user: UserNgEntity): GenericOverall
|
suspend fun unregisterPushWithServerForUser(user: UserNgEntity): GenericOverall
|
||||||
suspend fun registerPushWithProxyForUser(user: UserNgEntity, options: Map<String, String>): Any
|
suspend fun registerPushWithProxyForUser(user: UserNgEntity, options: Map<String, String>): Any
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* * Nextcloud Talk application
|
||||||
|
* *
|
||||||
|
* * @author Mario Danic
|
||||||
|
* * Copyright (C) 2017-2020 Mario Danic <mario@lovelyhq.com>
|
||||||
|
* *
|
||||||
|
* * 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.newarch.domain.usecases
|
||||||
|
|
||||||
|
import com.nextcloud.talk.models.json.participants.Participant
|
||||||
|
import com.nextcloud.talk.newarch.data.source.remote.ApiErrorHandler
|
||||||
|
import com.nextcloud.talk.newarch.domain.repository.online.NextcloudTalkRepository
|
||||||
|
import com.nextcloud.talk.newarch.domain.usecases.base.UseCase
|
||||||
|
import org.koin.core.parameter.DefinitionParameters
|
||||||
|
|
||||||
|
class GetContactsUseCase constructor(
|
||||||
|
private val nextcloudTalkRepository: NextcloudTalkRepository,
|
||||||
|
apiErrorHandler: ApiErrorHandler?
|
||||||
|
) : UseCase<List<Participant>, Any?>(apiErrorHandler) {
|
||||||
|
override suspend fun run(params: Any?): List<Participant> {
|
||||||
|
val definitionParameters = params as DefinitionParameters
|
||||||
|
return nextcloudTalkRepository.getContactsForUser(definitionParameters[0], definitionParameters[1], definitionParameters[2])
|
||||||
|
}
|
||||||
|
}
|
@ -42,6 +42,7 @@ abstract class UseCase<Type, in Params>(private val apiErrorHandler: ApiErrorHan
|
|||||||
onResult.onSuccess(it)
|
onResult.onSuccess(it)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|
||||||
onResult.onError(apiErrorHandler?.traceErrorException(e))
|
onResult.onError(apiErrorHandler?.traceErrorException(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ class ChatView : BaseView(), MessageHolders.ContentChecker<IMessage>, MessagesLi
|
|||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
actionBar?.show()
|
actionBar?.show()
|
||||||
viewModel = viewModelProvider(factory).get(ChatViewModel::class.java)
|
viewModel = viewModelProvider(factory).get(ChatViewModel::class.java)
|
||||||
viewModel.init(args.getParcelable(BundleKeys.KEY_USER_ENTITY)!!, args.getString(BundleKeys.KEY_ROOM_TOKEN)!!, args.getString(KEY_CONVERSATION_PASSWORD))
|
viewModel.init(args.getParcelable(BundleKeys.KEY_USER_ENTITY)!!, args.getString(BundleKeys.KEY_CONVERSATION_TOKEN)!!, args.getString(KEY_CONVERSATION_PASSWORD))
|
||||||
|
|
||||||
viewModel.apply {
|
viewModel.apply {
|
||||||
conversation.observe(this@ChatView) { conversation ->
|
conversation.observe(this@ChatView) { conversation ->
|
||||||
@ -344,7 +344,7 @@ class ChatView : BaseView(), MessageHolders.ContentChecker<IMessage>, MessagesLi
|
|||||||
BundleKeys.KEY_BROWSER_TYPE, Parcels.wrap<BrowserController.BrowserType>(browserType)
|
BundleKeys.KEY_BROWSER_TYPE, Parcels.wrap<BrowserController.BrowserType>(browserType)
|
||||||
)
|
)
|
||||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap<UserNgEntity>(viewModel.user))
|
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap<UserNgEntity>(viewModel.user))
|
||||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, it.token)
|
bundle.putString(BundleKeys.KEY_CONVERSATION_TOKEN, it.token)
|
||||||
router.pushController(
|
router.pushController(
|
||||||
RouterTransaction.with(BrowserController(bundle))
|
RouterTransaction.with(BrowserController(bundle))
|
||||||
.pushChangeHandler(VerticalChangeHandler())
|
.pushChangeHandler(VerticalChangeHandler())
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
package com.nextcloud.talk.newarch.features.contactsflow
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import coil.api.load
|
||||||
|
import com.nextcloud.talk.R
|
||||||
|
import com.nextcloud.talk.models.json.participants.Participant
|
||||||
|
import com.nextcloud.talk.newarch.local.models.getCredentials
|
||||||
|
import com.nextcloud.talk.newarch.services.GlobalService
|
||||||
|
import com.nextcloud.talk.newarch.utils.ElementPayload
|
||||||
|
import com.nextcloud.talk.newarch.utils.Images
|
||||||
|
import com.nextcloud.talk.utils.ApiUtils
|
||||||
|
import com.otaliastudios.elements.Element
|
||||||
|
import com.otaliastudios.elements.Page
|
||||||
|
import com.otaliastudios.elements.Presenter
|
||||||
|
import kotlinx.android.synthetic.main.rv_item_contact.view.*
|
||||||
|
import org.koin.core.KoinComponent
|
||||||
|
import org.koin.core.inject
|
||||||
|
|
||||||
|
open class ContactPresenter(context: Context, onElementClick: ((Page, Holder, Element<Participant>) -> Unit)?) : Presenter<Participant>(context, onElementClick), KoinComponent {
|
||||||
|
private val globalService: GlobalService by inject()
|
||||||
|
|
||||||
|
override val elementTypes: Collection<Int>
|
||||||
|
get() = listOf(0)
|
||||||
|
|
||||||
|
override fun onCreate(parent: ViewGroup, elementType: Int): Holder {
|
||||||
|
return Holder(getLayoutInflater().inflate(R.layout.rv_item_contact, parent, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBind(page: Page, holder: Holder, element: Element<Participant>, payloads: List<Any>) {
|
||||||
|
super.onBind(page, holder, element, payloads)
|
||||||
|
|
||||||
|
val participant = element.data
|
||||||
|
val user = globalService.currentUserLiveData.value
|
||||||
|
|
||||||
|
holder.itemView.checkedImageView.isVisible = element.data?.selected == true
|
||||||
|
|
||||||
|
if (!payloads.contains(ElementPayload.SELECTION_TOGGLE)) {
|
||||||
|
participant?.displayName?.let {
|
||||||
|
holder.itemView.name_text.text = it
|
||||||
|
} ?: run {
|
||||||
|
holder.itemView.name_text.text = context.getString(R.string.nc_guest)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (participant?.source) {
|
||||||
|
"users" -> {
|
||||||
|
when (participant.type) {
|
||||||
|
Participant.ParticipantType.GUEST, Participant.ParticipantType.GUEST_AS_MODERATOR, Participant.ParticipantType.USER_FOLLOWING_LINK -> {
|
||||||
|
holder.itemView.avatarImageView.load(ApiUtils.getUrlForAvatarWithNameForGuests(user?.baseUrl, participant.userId, R.dimen.avatar_size)) {
|
||||||
|
user?.getCredentials()?.let { addHeader("Authorization", it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
holder.itemView.avatarImageView.load(ApiUtils.getUrlForAvatarWithName(user?.baseUrl, participant.userId, R.dimen.avatar_size)) {
|
||||||
|
user?.getCredentials()?.let { addHeader("Authorization", it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"groups", "circles" -> {
|
||||||
|
holder.itemView.avatarImageView.load(Images().getImageWithBackground(context, R.drawable.ic_people_group_white_24px))
|
||||||
|
}
|
||||||
|
"emails" -> {
|
||||||
|
holder.itemView.avatarImageView.load(Images().getImageWithBackground(context, R.drawable.ic_baseline_email_24))
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package com.nextcloud.talk.newarch.features.contactsflow
|
||||||
|
|
||||||
|
enum class ParticipantElementType {
|
||||||
|
PARTICIPANT,
|
||||||
|
PARTICIPANT_HEADER
|
||||||
|
}
|
@ -1,9 +1,79 @@
|
|||||||
package com.nextcloud.talk.newarch.features.contactsflow
|
package com.nextcloud.talk.newarch.features.contactsflow
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.bluelinelabs.conductor.autodispose.ControllerScopeProvider
|
||||||
|
import com.nextcloud.talk.R
|
||||||
|
import com.nextcloud.talk.models.json.participants.Participant
|
||||||
import com.nextcloud.talk.newarch.conversationsList.mvp.BaseView
|
import com.nextcloud.talk.newarch.conversationsList.mvp.BaseView
|
||||||
|
import com.nextcloud.talk.newarch.mvvm.ext.initRecyclerView
|
||||||
|
import com.nextcloud.talk.newarch.utils.ElementPayload
|
||||||
|
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||||
|
import com.otaliastudios.elements.Adapter
|
||||||
|
import com.otaliastudios.elements.Element
|
||||||
|
import com.otaliastudios.elements.Page
|
||||||
|
import com.otaliastudios.elements.Presenter
|
||||||
|
import com.uber.autodispose.lifecycle.LifecycleScopeProvider
|
||||||
|
import kotlinx.android.synthetic.main.conversations_list_view.view.*
|
||||||
|
import kotlinx.android.synthetic.main.message_state.view.*
|
||||||
|
import org.koin.android.ext.android.inject
|
||||||
|
|
||||||
class ContactsView : BaseView() {
|
class ContactsView(private val bundle: Bundle? = null) : BaseView() {
|
||||||
|
override val scopeProvider: LifecycleScopeProvider<*> = ControllerScopeProvider.from(this)
|
||||||
|
|
||||||
|
private lateinit var viewModel: ContactsViewModel
|
||||||
|
val factory: ContactsViewModelFactory by inject()
|
||||||
|
lateinit var adapter: Adapter
|
||||||
override fun getLayoutId(): Int {
|
override fun getLayoutId(): Int {
|
||||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
return R.layout.contacts_list_view
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup
|
||||||
|
): View {
|
||||||
|
actionBar?.show()
|
||||||
|
setHasOptionsMenu(true)
|
||||||
|
|
||||||
|
viewModel = viewModelProvider(factory).get(ContactsViewModel::class.java)
|
||||||
|
viewModel.conversationToken = bundle?.getString(BundleKeys.KEY_CONVERSATION_TOKEN)
|
||||||
|
val view = super.onCreateView(inflater, container)
|
||||||
|
|
||||||
|
// todo - change empty state magic
|
||||||
|
adapter = Adapter.builder(this)
|
||||||
|
.addSource(ContactsViewSource(viewModel.contactsLiveData, ParticipantElementType.PARTICIPANT.ordinal))
|
||||||
|
.addSource(ContactsHeaderSource(activity as Context, ParticipantElementType.PARTICIPANT_HEADER.ordinal))
|
||||||
|
.addPresenter(ContactPresenter(activity as Context, ::onElementClick))
|
||||||
|
.addPresenter(Presenter.forLoadingIndicator(activity as Context, R.layout.loading_state))
|
||||||
|
.addPresenter(Presenter.forEmptyIndicator(activity as Context, R.layout.message_state))
|
||||||
|
.addPresenter(Presenter.forErrorIndicator(activity as Context, R.layout.message_state) { view, throwable ->
|
||||||
|
view.messageStateTextView.setText(R.string.nc_oops)
|
||||||
|
view.messageStateImageView.setImageDrawable((activity as Context).getDrawable(R.drawable.ic_announcement_white_24dp))
|
||||||
|
})
|
||||||
|
.setAutoScrollMode(Adapter.AUTOSCROLL_POSITION_0, true)
|
||||||
|
.into(view.recyclerView)
|
||||||
|
|
||||||
|
view.apply {
|
||||||
|
recyclerView.initRecyclerView(LinearLayoutManager(activity), adapter, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.loadContacts()
|
||||||
|
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onElementClick(page: Page, holder: Presenter.Holder, element: Element<Participant>) {
|
||||||
|
val isElementSelected = element.data?.selected == true
|
||||||
|
element.data?.selected = !isElementSelected
|
||||||
|
adapter.notifyItemChanged(holder.adapterPosition, ElementPayload.SELECTION_TOGGLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTitle(): String? {
|
||||||
|
return resources?.getString(R.string.nc_select_contacts)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package com.nextcloud.talk.newarch.features.contactsflow
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.nextcloud.talk.R
|
||||||
|
import com.nextcloud.talk.models.json.participants.Participant
|
||||||
|
import com.otaliastudios.elements.Page
|
||||||
|
import com.otaliastudios.elements.Source
|
||||||
|
import com.otaliastudios.elements.extensions.HeaderSource
|
||||||
|
|
||||||
|
class ContactsHeaderSource(private val context: Context, private val elementType: Int): HeaderSource<Participant, String>() {
|
||||||
|
|
||||||
|
// Store the last header that was added, even if it belongs to a previous page.
|
||||||
|
private var lastHeader: String = ""
|
||||||
|
|
||||||
|
override fun dependsOn(source: Source<*>) = source is ContactsViewSource
|
||||||
|
|
||||||
|
override fun computeHeaders(page: Page, list: List<Participant>): List<Data<Participant, String>> {
|
||||||
|
val results = arrayListOf<Data<Participant, String>>()
|
||||||
|
for (participant in list) {
|
||||||
|
val header = when (participant.source) {
|
||||||
|
"users" -> {
|
||||||
|
context.getString(R.string.nc_contacts)
|
||||||
|
}
|
||||||
|
"groups" -> {
|
||||||
|
context.getString(R.string.nc_groups)
|
||||||
|
}
|
||||||
|
"emails" -> {
|
||||||
|
context.getString(R.string.nc_emails)
|
||||||
|
}
|
||||||
|
"circles" -> {
|
||||||
|
context.getString(R.string.nc_circles)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
context.getString(R.string.nc_others)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header != lastHeader) {
|
||||||
|
results.add(Data(participant, header))
|
||||||
|
lastHeader = header
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getElementType(data: Data<Participant, String>): Int {
|
||||||
|
return elementType
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areItemsTheSame(first: Data<Participant, String>, second: Data<Participant, String>): Boolean {
|
||||||
|
return first == second
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,41 @@
|
|||||||
package com.nextcloud.talk.newarch.features.contactsflow
|
package com.nextcloud.talk.newarch.features.contactsflow
|
||||||
|
|
||||||
class ContactsViewModel {
|
import android.app.Application
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.nextcloud.talk.models.json.participants.Participant
|
||||||
|
import com.nextcloud.talk.newarch.conversationsList.mvp.BaseViewModel
|
||||||
|
import com.nextcloud.talk.newarch.data.model.ErrorModel
|
||||||
|
import com.nextcloud.talk.newarch.domain.usecases.GetContactsUseCase
|
||||||
|
import com.nextcloud.talk.newarch.domain.usecases.base.UseCaseResponse
|
||||||
|
import com.nextcloud.talk.newarch.features.conversationslist.ConversationsListView
|
||||||
|
import com.nextcloud.talk.newarch.services.GlobalService
|
||||||
|
import org.koin.core.parameter.parametersOf
|
||||||
|
|
||||||
|
class ContactsViewModel constructor(
|
||||||
|
application: Application,
|
||||||
|
private val getContactsUseCase: GetContactsUseCase,
|
||||||
|
val globalService: GlobalService
|
||||||
|
) : BaseViewModel<ConversationsListView>(application) {
|
||||||
|
val contactsLiveData = MutableLiveData<List<Participant>>()
|
||||||
|
val searchQuery = MutableLiveData<String?>(null)
|
||||||
|
var conversationToken: String? = null
|
||||||
|
|
||||||
|
fun loadContacts() {
|
||||||
|
getContactsUseCase.invoke(viewModelScope, parametersOf(globalService.currentUserLiveData.value, searchQuery.value, conversationToken), object :
|
||||||
|
UseCaseResponse<List<Participant>> {
|
||||||
|
override suspend fun onSuccess(result: List<Participant>) {
|
||||||
|
val sortPriority = mapOf("users" to 3, "groups" to 2, "emails" to 1, "circles" to 0)
|
||||||
|
contactsLiveData.postValue(result.sortedWith(Comparator { o1, o2 ->
|
||||||
|
sortPriority[o2.source]?.let { sortPriority[o1.source]?.compareTo(it) }
|
||||||
|
0
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun onError(errorModel: ErrorModel?) {
|
||||||
|
// handle errors here
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,5 +1,20 @@
|
|||||||
package com.nextcloud.talk.newarch.features.contactsflow
|
package com.nextcloud.talk.newarch.features.contactsflow
|
||||||
|
|
||||||
class ContactsViewModelFactory {
|
import android.app.Application
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import com.nextcloud.talk.newarch.domain.usecases.GetContactsUseCase
|
||||||
|
import com.nextcloud.talk.newarch.services.GlobalService
|
||||||
|
|
||||||
|
class ContactsViewModelFactory constructor(
|
||||||
|
private val application: Application,
|
||||||
|
private val getContactsUseCase: GetContactsUseCase,
|
||||||
|
private val globalService: GlobalService
|
||||||
|
) : ViewModelProvider.Factory {
|
||||||
|
|
||||||
|
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||||
|
return ContactsViewModel(
|
||||||
|
application, getContactsUseCase, globalService
|
||||||
|
) as T
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* * Nextcloud Talk application
|
||||||
|
* *
|
||||||
|
* * @author Mario Danic
|
||||||
|
* * Copyright (C) 2017-2020 Mario Danic <mario@lovelyhq.com>
|
||||||
|
* *
|
||||||
|
* * 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.talk.newarch.features.contactsflow
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||||
|
import com.nextcloud.talk.models.json.participants.Participant
|
||||||
|
import com.otaliastudios.elements.Element
|
||||||
|
import com.otaliastudios.elements.Page
|
||||||
|
import com.otaliastudios.elements.Source
|
||||||
|
import com.otaliastudios.elements.extensions.MainSource
|
||||||
|
|
||||||
|
class ContactsViewSource<T : Participant>(private val data: LiveData<List<T>>, private val elementType: Int = 0, loadingIndicatorsEnabled: Boolean = true, errorIndicatorEnabled: Boolean = true, emptyIndicatorEnabled: Boolean = true) : MainSource<T>(loadingIndicatorsEnabled, errorIndicatorEnabled, emptyIndicatorEnabled) {
|
||||||
|
|
||||||
|
override fun onPageOpened(page: Page, dependencies: List<Element<*>>) {
|
||||||
|
super.onPageOpened(page, dependencies)
|
||||||
|
if (page.previous() == null) {
|
||||||
|
postResult(page, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun dependsOn(source: Source<*>): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areContentsTheSame(first: T, second: T): Boolean {
|
||||||
|
return first == second
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areItemsTheSame(first: T, second: T): Boolean {
|
||||||
|
return first.userId == second.userId
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.nextcloud.talk.newarch.features.contactsflow.di.module
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import com.nextcloud.talk.newarch.domain.usecases.GetContactsUseCase
|
||||||
|
import com.nextcloud.talk.newarch.features.contactsflow.ContactsViewModelFactory
|
||||||
|
import com.nextcloud.talk.newarch.services.GlobalService
|
||||||
|
import org.koin.android.ext.koin.androidApplication
|
||||||
|
import org.koin.dsl.module
|
||||||
|
|
||||||
|
val ContactsFlowModule = module {
|
||||||
|
factory {
|
||||||
|
createContactsViewModelFactory(
|
||||||
|
androidApplication(), get(), get()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createContactsViewModelFactory(
|
||||||
|
application: Application,
|
||||||
|
getContactsUseCase: GetContactsUseCase,
|
||||||
|
globalService: GlobalService
|
||||||
|
): ContactsViewModelFactory {
|
||||||
|
return ContactsViewModelFactory(
|
||||||
|
application, getContactsUseCase, globalService
|
||||||
|
)
|
||||||
|
}
|
@ -45,7 +45,7 @@ import kotlinx.android.synthetic.main.rv_item_conversation_with_last_message.vie
|
|||||||
import org.koin.core.KoinComponent
|
import org.koin.core.KoinComponent
|
||||||
import org.koin.core.inject
|
import org.koin.core.inject
|
||||||
|
|
||||||
open class ConversationsPresenter(context: Context, onElementClick: ((Page, Holder, Element<Conversation>) -> Unit)?, private val onElementLongClick: ((Page, Holder, Element<Conversation>) -> Unit)?) : Presenter<Conversation>(context, onElementClick), KoinComponent {
|
open class ConversationPresenter(context: Context, onElementClick: ((Page, Holder, Element<Conversation>) -> Unit)?, private val onElementLongClick: ((Page, Holder, Element<Conversation>) -> Unit)?) : Presenter<Conversation>(context, onElementClick), KoinComponent {
|
||||||
private val globalService: GlobalService by inject()
|
private val globalService: GlobalService by inject()
|
||||||
|
|
||||||
override val elementTypes: Collection<Int>
|
override val elementTypes: Collection<Int>
|
||||||
@ -159,4 +159,6 @@ open class ConversationsPresenter(context: Context, onElementClick: ((Page, Hold
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -27,6 +27,7 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.observe
|
import androidx.lifecycle.observe
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.afollestad.materialdialogs.LayoutMode
|
import com.afollestad.materialdialogs.LayoutMode
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
import com.afollestad.materialdialogs.bottomsheets.BottomSheet
|
import com.afollestad.materialdialogs.bottomsheets.BottomSheet
|
||||||
@ -57,7 +58,6 @@ import com.otaliastudios.elements.Element
|
|||||||
import com.otaliastudios.elements.Page
|
import com.otaliastudios.elements.Page
|
||||||
import com.otaliastudios.elements.Presenter
|
import com.otaliastudios.elements.Presenter
|
||||||
import com.uber.autodispose.lifecycle.LifecycleScopeProvider
|
import com.uber.autodispose.lifecycle.LifecycleScopeProvider
|
||||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
|
||||||
import kotlinx.android.synthetic.main.conversations_list_view.view.*
|
import kotlinx.android.synthetic.main.conversations_list_view.view.*
|
||||||
import kotlinx.android.synthetic.main.message_state.view.*
|
import kotlinx.android.synthetic.main.message_state.view.*
|
||||||
import kotlinx.android.synthetic.main.search_layout.*
|
import kotlinx.android.synthetic.main.search_layout.*
|
||||||
@ -82,7 +82,7 @@ class ConversationsListView : BaseView() {
|
|||||||
|
|
||||||
val adapter = Adapter.builder(this)
|
val adapter = Adapter.builder(this)
|
||||||
.addSource(ConversationsListSource(viewModel.conversationsLiveData))
|
.addSource(ConversationsListSource(viewModel.conversationsLiveData))
|
||||||
.addPresenter(ConversationsPresenter(activity as Context, ::onElementClick, ::onElementLongClick))
|
.addPresenter(ConversationPresenter(activity as Context, ::onElementClick, ::onElementLongClick))
|
||||||
.addPresenter(Presenter.forLoadingIndicator(activity as Context, R.layout.loading_state))
|
.addPresenter(Presenter.forLoadingIndicator(activity as Context, R.layout.loading_state))
|
||||||
.addPresenter(AdvancedEmptyPresenter(activity as Context, R.layout.message_state, ::openNewConversationScreen))
|
.addPresenter(AdvancedEmptyPresenter(activity as Context, R.layout.message_state, ::openNewConversationScreen))
|
||||||
.addPresenter(Presenter.forErrorIndicator(activity as Context, R.layout.message_state) { view, throwable ->
|
.addPresenter(Presenter.forErrorIndicator(activity as Context, R.layout.message_state) { view, throwable ->
|
||||||
@ -94,7 +94,7 @@ class ConversationsListView : BaseView() {
|
|||||||
|
|
||||||
|
|
||||||
view.apply {
|
view.apply {
|
||||||
recyclerView.initRecyclerView(SmoothScrollLinearLayoutManager(activity), adapter, false)
|
recyclerView.initRecyclerView(LinearLayoutManager(activity), adapter, true)
|
||||||
swipeRefreshLayoutView.setOnRefreshListener {
|
swipeRefreshLayoutView.setOnRefreshListener {
|
||||||
view.swipeRefreshLayoutView.isRefreshing = false
|
view.swipeRefreshLayoutView.isRefreshing = false
|
||||||
viewModel.loadConversations()
|
viewModel.loadConversations()
|
||||||
@ -156,7 +156,7 @@ class ConversationsListView : BaseView() {
|
|||||||
conversation?.let { conversation ->
|
conversation?.let { conversation ->
|
||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, user)
|
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, user)
|
||||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, conversation.token)
|
bundle.putString(BundleKeys.KEY_CONVERSATION_TOKEN, conversation.token)
|
||||||
bundle.putString(BundleKeys.KEY_ROOM_ID, conversation.conversationId)
|
bundle.putString(BundleKeys.KEY_ROOM_ID, conversation.conversationId)
|
||||||
bundle.putParcelable(BundleKeys.KEY_ACTIVE_CONVERSATION, Parcels.wrap(conversation))
|
bundle.putParcelable(BundleKeys.KEY_ACTIVE_CONVERSATION, Parcels.wrap(conversation))
|
||||||
ConductorRemapping.remapChatController(
|
ConductorRemapping.remapChatController(
|
||||||
|
@ -98,7 +98,7 @@ class ShortcutService constructor(private var context: Context,
|
|||||||
val intent = Intent(context, MainActivity::class.java)
|
val intent = Intent(context, MainActivity::class.java)
|
||||||
intent.action = BundleKeys.KEY_OPEN_CONVERSATION
|
intent.action = BundleKeys.KEY_OPEN_CONVERSATION
|
||||||
intent.putExtra(BundleKeys.KEY_INTERNAL_USER_ID, user.id)
|
intent.putExtra(BundleKeys.KEY_INTERNAL_USER_ID, user.id)
|
||||||
intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, conversation.token)
|
intent.putExtra(BundleKeys.KEY_CONVERSATION_TOKEN, conversation.token)
|
||||||
|
|
||||||
val persons = mutableListOf<Person>()
|
val persons = mutableListOf<Person>()
|
||||||
conversation.participants?.forEach {
|
conversation.participants?.forEach {
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.nextcloud.talk.newarch.utils
|
||||||
|
|
||||||
|
enum class ElementPayload {
|
||||||
|
SELECTION_TOGGLE
|
||||||
|
}
|
@ -29,7 +29,9 @@ import com.nextcloud.talk.R;
|
|||||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||||
import com.nextcloud.talk.models.RetrofitBucket;
|
import com.nextcloud.talk.models.RetrofitBucket;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -61,23 +63,41 @@ public class ApiUtils {
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RetrofitBucket getRetrofitBucketForContactsSearch(String baseUrl,
|
public static String getUrlForContactsSearch(String baseUrl) {
|
||||||
@Nullable String searchQuery) {
|
return baseUrl + ocsApiVersion + "/core/autocomplete/get";
|
||||||
RetrofitBucket retrofitBucket = new RetrofitBucket();
|
}
|
||||||
retrofitBucket.setUrl(baseUrl + ocsApiVersion + "/apps/files_sharing/api/v1/sharees");
|
|
||||||
|
|
||||||
|
public static List<String> getShareTypesForContactsSearch() {
|
||||||
|
List<String> shareTypesList = new ArrayList<>();
|
||||||
|
// user
|
||||||
|
shareTypesList.add("0");
|
||||||
|
// group
|
||||||
|
shareTypesList.add("1");
|
||||||
|
// group
|
||||||
|
shareTypesList.add("4");
|
||||||
|
// remote/circles
|
||||||
|
shareTypesList.add("7");
|
||||||
|
|
||||||
|
return shareTypesList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, String> getQueryMapForContactsSearch(@Nullable String searchQuery, @Nullable String conversationToken) {
|
||||||
Map<String, String> queryMap = new HashMap<>();
|
Map<String, String> queryMap = new HashMap<>();
|
||||||
|
if (searchQuery != null) {
|
||||||
if (searchQuery == null) {
|
queryMap.put("search", searchQuery);
|
||||||
searchQuery = "";
|
} else {
|
||||||
|
queryMap.put("search", "");
|
||||||
}
|
}
|
||||||
queryMap.put("format", "json");
|
|
||||||
queryMap.put("search", searchQuery);
|
|
||||||
queryMap.put("itemType", "call");
|
queryMap.put("itemType", "call");
|
||||||
|
|
||||||
retrofitBucket.setQueryMap(queryMap);
|
if (conversationToken != null) {
|
||||||
|
queryMap.put("itemId", conversationToken);
|
||||||
|
} else {
|
||||||
|
queryMap.put("itemId", "new");
|
||||||
|
}
|
||||||
|
|
||||||
return retrofitBucket;
|
return queryMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getUrlForFilePreviewWithRemotePath(String baseUrl, String remotePath,
|
public static String getUrlForFilePreviewWithRemotePath(String baseUrl, String remotePath,
|
||||||
@ -96,13 +116,6 @@ public class ApiUtils {
|
|||||||
return baseUrl + ocsApiVersion + "/apps/files_sharing/api/v1/shares";
|
return baseUrl + ocsApiVersion + "/apps/files_sharing/api/v1/shares";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RetrofitBucket getRetrofitBucketForContactsSearchFor14(String baseUrl,
|
|
||||||
@Nullable String searchQuery) {
|
|
||||||
RetrofitBucket retrofitBucket = getRetrofitBucketForContactsSearch(baseUrl, searchQuery);
|
|
||||||
retrofitBucket.setUrl(baseUrl + ocsApiVersion + "/core/autocomplete/get");
|
|
||||||
retrofitBucket.getQueryMap().put("itemId", "new");
|
|
||||||
return retrofitBucket;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getUrlForSettingNotificationlevel(String baseUrl, String token) {
|
public static String getUrlForSettingNotificationlevel(String baseUrl, String token) {
|
||||||
return getRoom(baseUrl, token) + "/notify";
|
return getRoom(baseUrl, token) + "/notify";
|
||||||
|
@ -163,7 +163,7 @@ object NotificationUtils {
|
|||||||
if (conversationUser.id == notification.extras.getLong(
|
if (conversationUser.id == notification.extras.getLong(
|
||||||
BundleKeys.KEY_INTERNAL_USER_ID
|
BundleKeys.KEY_INTERNAL_USER_ID
|
||||||
) && roomTokenOrId == statusBarNotification.notification.extras.getString(
|
) && roomTokenOrId == statusBarNotification.notification.extras.getString(
|
||||||
BundleKeys.KEY_ROOM_TOKEN
|
BundleKeys.KEY_CONVERSATION_TOKEN
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return statusBarNotification
|
return statusBarNotification
|
||||||
@ -196,7 +196,7 @@ object NotificationUtils {
|
|||||||
if (conversationUser.id == notification.extras.getLong(
|
if (conversationUser.id == notification.extras.getLong(
|
||||||
BundleKeys.KEY_INTERNAL_USER_ID
|
BundleKeys.KEY_INTERNAL_USER_ID
|
||||||
) && roomTokenOrId == statusBarNotification.notification.extras.getString(
|
) && roomTokenOrId == statusBarNotification.notification.extras.getString(
|
||||||
BundleKeys.KEY_ROOM_TOKEN
|
BundleKeys.KEY_CONVERSATION_TOKEN
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
notificationManager.cancel(statusBarNotification.id)
|
notificationManager.cancel(statusBarNotification.id)
|
||||||
|
@ -35,7 +35,7 @@ object BundleKeys {
|
|||||||
val KEY_APP_ITEM_PACKAGE_NAME = "KEY_APP_ITEM_PACKAGE_NAME"
|
val KEY_APP_ITEM_PACKAGE_NAME = "KEY_APP_ITEM_PACKAGE_NAME"
|
||||||
val KEY_APP_ITEM_NAME = "KEY_APP_ITEM_NAME"
|
val KEY_APP_ITEM_NAME = "KEY_APP_ITEM_NAME"
|
||||||
val KEY_CONVERSATION_PASSWORD = "KEY_CONVERSATION_PASSWORD"
|
val KEY_CONVERSATION_PASSWORD = "KEY_CONVERSATION_PASSWORD"
|
||||||
val KEY_ROOM_TOKEN = "KEY_ROOM_TOKEN"
|
val KEY_CONVERSATION_TOKEN = "KEY_CONVERSATION_TOKEN"
|
||||||
val KEY_USER_ENTITY = "KEY_USER_ENTITY"
|
val KEY_USER_ENTITY = "KEY_USER_ENTITY"
|
||||||
val KEY_NEW_CONVERSATION = "KEY_NEW_CONVERSATION"
|
val KEY_NEW_CONVERSATION = "KEY_NEW_CONVERSATION"
|
||||||
val KEY_ADD_PARTICIPANTS = "KEY_ADD_PARTICIPANTS"
|
val KEY_ADD_PARTICIPANTS = "KEY_ADD_PARTICIPANTS"
|
||||||
|
@ -35,7 +35,7 @@ import com.nextcloud.talk.newarch.local.models.UserNgEntity
|
|||||||
import com.nextcloud.talk.utils.LoggingUtils.writeLogEntryToFile
|
import com.nextcloud.talk.utils.LoggingUtils.writeLogEntryToFile
|
||||||
import com.nextcloud.talk.utils.MagicMap
|
import com.nextcloud.talk.utils.MagicMap
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_TOKEN
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request.Builder
|
import okhttp3.Request.Builder
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
@ -240,7 +240,7 @@ class MagicWebSocketInstance internal constructor(
|
|||||||
if (shouldRefreshChat) {
|
if (shouldRefreshChat) {
|
||||||
val refreshChatHashMap =
|
val refreshChatHashMap =
|
||||||
HashMap<String, String?>()
|
HashMap<String, String?>()
|
||||||
refreshChatHashMap[KEY_ROOM_TOKEN] = messageHashMap["roomid"] as String?
|
refreshChatHashMap[KEY_CONVERSATION_TOKEN] = messageHashMap["roomid"] as String?
|
||||||
refreshChatHashMap[KEY_INTERNAL_USER_ID] =
|
refreshChatHashMap[KEY_INTERNAL_USER_ID] =
|
||||||
java.lang.Long.toString(conversationUser.id!!)
|
java.lang.Long.toString(conversationUser.id!!)
|
||||||
eventBus.post(
|
eventBus.post(
|
||||||
|
13
app/src/main/res/layout/contacts_list_view.xml
Normal file
13
app/src/main/res/layout/contacts_list_view.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:listitem="@layout/rv_item_contact" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
@ -28,15 +28,15 @@
|
|||||||
android:layout_margin="@dimen/margin_between_elements"
|
android:layout_margin="@dimen/margin_between_elements"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<ImageView
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
android:id="@+id/checkedImageView"
|
android:id="@+id/avatarImageView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="@dimen/small_item_height"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="@dimen/small_item_height"
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:src="@drawable/ic_check_black_24dp"
|
android:layout_marginStart="@dimen/double_margin_between_elements"
|
||||||
android:tint="@color/colorPrimary"
|
android:layout_marginEnd="@dimen/margin_between_elements"
|
||||||
android:visibility="gone" />
|
app:shapeAppearanceOverlay="@style/circleImageView"
|
||||||
|
tools:srcCompat="@tools:sample/avatars"/>
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
<androidx.emoji.widget.EmojiTextView
|
||||||
android:id="@+id/name_text"
|
android:id="@+id/name_text"
|
||||||
@ -48,13 +48,14 @@
|
|||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
tools:text="Contact item text" />
|
tools:text="Contact item text" />
|
||||||
|
|
||||||
<com.google.android.material.imageview.ShapeableImageView
|
<ImageView
|
||||||
android:id="@+id/avatarImageView"
|
android:id="@+id/checkedImageView"
|
||||||
app:shapeAppearanceOverlay="@style/circleImageView"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="@dimen/small_item_height"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="@dimen/small_item_height"
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:layout_marginStart="@dimen/double_margin_between_elements"
|
android:src="@drawable/ic_check_black_24dp"
|
||||||
android:layout_marginEnd="@dimen/margin_between_elements" />
|
android:tint="@color/colorPrimary"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
@ -180,7 +180,7 @@
|
|||||||
conversations list</string>
|
conversations list</string>
|
||||||
|
|
||||||
<!-- Contacts -->
|
<!-- Contacts -->
|
||||||
<string name="nc_select_contacts">Select contacts</string>
|
<string name="nc_select_contacts">Find participants</string>
|
||||||
<string name="nc_contacts_done">Done</string>
|
<string name="nc_contacts_done">Done</string>
|
||||||
|
|
||||||
<!-- Permissions -->
|
<!-- Permissions -->
|
||||||
@ -289,6 +289,7 @@
|
|||||||
<string name="nc_circles">Circles</string>
|
<string name="nc_circles">Circles</string>
|
||||||
<string name="nc_emails">Emails</string>
|
<string name="nc_emails">Emails</string>
|
||||||
<string name="nc_participants">Participants</string>
|
<string name="nc_participants">Participants</string>
|
||||||
|
<string name="nc_others">Others</string>
|
||||||
|
|
||||||
<string name="nc_owner">Owner</string>
|
<string name="nc_owner">Owner</string>
|
||||||
<string name="nc_moderator">Moderator</string>
|
<string name="nc_moderator">Moderator</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user