Skip to content
Snippets Groups Projects
Commit e406016d authored by Adrien Béraud's avatar Adrien Béraud
Browse files

add long-press actions for conversation requests

Change-Id: I49a66bd73a598baab5a87024b93f42b7e4bb6248
parent 7e1fe6a8
No related branches found
No related tags found
No related merge requests found
Showing
with 256 additions and 298 deletions
......@@ -21,26 +21,26 @@
*/
package cx.ring.client
import cx.ring.utils.ConversationPath.Companion.fromIntent
import dagger.hilt.android.AndroidEntryPoint
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Build
import android.view.WindowManager
import cx.ring.R
import android.media.AudioManager
import android.os.Looper
import android.content.Intent
import android.content.res.Configuration
import android.media.AudioManager
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.KeyEvent
import android.view.View
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import cx.ring.BuildConfig
import cx.ring.R
import cx.ring.application.JamiApplication
import cx.ring.fragments.CallFragment
import net.jami.services.NotificationService
import cx.ring.utils.ConversationPath.Companion.fromIntent
import cx.ring.utils.KeyboardVisibilityManager
import cx.ring.utils.MediaButtonsHelper
import dagger.hilt.android.AndroidEntryPoint
import net.jami.services.NotificationService
@AndroidEntryPoint
class CallActivity : AppCompatActivity() {
......@@ -68,11 +68,10 @@ class CallActivity : AppCompatActivity() {
mMainView = findViewById<View>(R.id.main_call_layout)?.apply {
setOnClickListener {
dimmed = !dimmed
if (dimmed) {
if (dimmed)
hideSystemUI()
} else {
else
showSystemUI()
}
}
}
intent?.let { handleNewIntent(it) }
......@@ -81,13 +80,12 @@ class CallActivity : AppCompatActivity() {
override fun onResume() {
super.onResume()
restartNoInteractionTimer()
volumeControlStream = AudioManager.STREAM_VOICE_CALL
}
override fun onStop() {
super.onStop()
if (handler != null) {
handler!!.removeCallbacks(onNoInteraction)
}
handler?.removeCallbacks(onNoInteraction)
}
public override fun onNewIntent(intent: Intent) {
......@@ -128,9 +126,9 @@ class CallActivity : AppCompatActivity() {
}
private fun restartNoInteractionTimer() {
if (handler != null) {
handler!!.removeCallbacks(onNoInteraction)
handler!!.postDelayed(onNoInteraction, (4 * 1000).toLong())
handler?.let { handler ->
handler.removeCallbacks(onNoInteraction)
handler.postDelayed(onNoInteraction, (4 * 1000).toLong())
}
}
......@@ -155,8 +153,8 @@ class CallActivity : AppCompatActivity() {
private fun hideSystemUI() {
KeyboardVisibilityManager.hideKeyboard(this)
if (mMainView != null) {
mMainView!!.systemUiVisibility =
mMainView?.let { mainView ->
mainView.systemUiVisibility =
(View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LOW_PROFILE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
......@@ -167,29 +165,27 @@ class CallActivity : AppCompatActivity() {
if (callFragment != null && !callFragment.isChoosePluginMode) {
callFragment.toggleVideoPluginsCarousel(false)
}
if (handler != null) handler!!.removeCallbacks(onNoInteraction)
handler?.removeCallbacks(onNoInteraction)
}
}
fun showSystemUI() {
if (mMainView != null) {
mMainView!!.systemUiVisibility =
mMainView?.let { mainView ->
mainView.systemUiVisibility =
(View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LOW_PROFILE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_IMMERSIVE)
val callFragment = callFragment
callFragment?.toggleVideoPluginsCarousel(true)
restartNoInteractionTimer()
}
}
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
val callFragment = callFragment
return if (callFragment != null) {
(MediaButtonsHelper.handleMediaKeyCode(keyCode, callFragment)
|| super.onKeyDown(keyCode, event))
} else super.onKeyDown(keyCode, event)
val fragment = callFragment
if (fragment != null && MediaButtonsHelper.handleMediaKeyCode(keyCode, fragment))
return true
return super.onKeyDown(keyCode, event)
}
// Get the call Fragment
......
......@@ -178,7 +178,7 @@ class HomeActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListen
val action = intent.action
if (ACTION_PRESENT_TRUST_REQUEST_FRAGMENT == action) {
//mAccountWithPendingrequests = extra.getString(ContactRequestsFragment.ACCOUNT_ID);
presentTrustRequestFragment(extra?.getString(ContactRequestsFragment.ACCOUNT_ID) ?: return)
presentTrustRequestFragment(extra?.getString(AccountEditionFragment.ACCOUNT_ID_KEY) ?: return)
} else if (Intent.ACTION_SEND == action || Intent.ACTION_SEND_MULTIPLE == action) {
val path = ConversationPath.fromBundle(extra)
if (path != null) {
......@@ -380,14 +380,14 @@ class HomeActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListen
.commit()
}
private fun presentTrustRequestFragment(accountID: String) {
mNotificationService.cancelTrustRequestNotification(accountID)
private fun presentTrustRequestFragment(accountId: String) {
mNotificationService.cancelTrustRequestNotification(accountId)
if (fContent is ContactRequestsFragment) {
(fContent as ContactRequestsFragment).presentForAccount(accountID)
(fContent as ContactRequestsFragment).presentForAccount(accountId)
return
}
val content = ContactRequestsFragment().apply {
arguments = Bundle().apply { putString(ContactRequestsFragment.ACCOUNT_ID, accountID) }
arguments = Bundle().apply { putString(AccountEditionFragment.ACCOUNT_ID_KEY, accountId) }
}
fContent = content
mBinding!!.navigationView.menu.getItem(NAVIGATION_CONTACT_REQUESTS).isChecked = true
......
......@@ -22,10 +22,16 @@ import android.os.Bundle
import android.view.*
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import cx.ring.R
import cx.ring.account.AccountEditionFragment
import cx.ring.adapters.SmartListAdapter
import cx.ring.client.HomeActivity
import cx.ring.databinding.FragPendingContactRequestsBinding
import cx.ring.mvp.BaseSupportFragment
import cx.ring.utils.ActionHelper
import cx.ring.utils.ClipboardHelper
import cx.ring.viewholders.SmartListViewHolder.SmartListListeners
import dagger.hilt.android.AndroidEntryPoint
import io.reactivex.rxjava3.disposables.CompositeDisposable
......@@ -54,13 +60,14 @@ class ContactRequestsFragment :
}
fun presentForAccount(accountId: String?) {
arguments?.putString(ACCOUNT_ID, accountId)
if (accountId != null)
arguments?.putString(AccountEditionFragment.ACCOUNT_ID_KEY, accountId)
presenter.updateAccount(accountId)
}
override fun onStart() {
super.onStart()
presenter.updateAccount(arguments?.getString(ACCOUNT_ID))
presenter.updateAccount(arguments?.getString(AccountEditionFragment.ACCOUNT_ID_KEY))
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
......@@ -72,28 +79,23 @@ class ContactRequestsFragment :
}
override fun updateView(list: MutableList<SmartListViewModel>, disposable: CompositeDisposable) {
if (binding == null) {
return
}
val binding = binding ?: return
if (list.isNotEmpty()) {
binding!!.paneRingID.visibility = View.GONE
binding.paneRingID.visibility = View.GONE
}
binding!!.placeholder.visibility = if (list.isEmpty()) View.VISIBLE else View.GONE
if (binding!!.requestsList.adapter != null) {
mAdapter!!.update(list)
binding.placeholder.visibility = if (list.isEmpty()) View.VISIBLE else View.GONE
val adapter = mAdapter
if (adapter != null) {
adapter.update(list)
} else {
mAdapter = SmartListAdapter(list, this@ContactRequestsFragment, disposable)
binding!!.requestsList.adapter = mAdapter
val mLayoutManager = LinearLayoutManager(activity)
binding!!.requestsList.layoutManager = mLayoutManager
binding.requestsList.layoutManager = LinearLayoutManager(activity)
mAdapter = SmartListAdapter(list, this@ContactRequestsFragment, disposable).apply {
binding.requestsList.adapter = this
}
}
binding!!.requestsList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
binding.requestsList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
(requireActivity() as HomeActivity).setToolbarElevation(
recyclerView.canScrollVertically(
SCROLL_DIRECTION_UP
)
)
(activity as HomeActivity?)?.setToolbarElevation(recyclerView.canScrollVertically(SCROLL_DIRECTION_UP))
}
})
}
......@@ -106,15 +108,34 @@ class ContactRequestsFragment :
(requireActivity() as HomeActivity).startConversation(accountId, contactId)
}
override fun copyNumber(uri: Uri) {
val number = uri.toString()
ClipboardHelper.copyToClipboard(requireContext(), number)
val snackbarText = getString(
R.string.conversation_action_copied_peer_number_clipboard,
ActionHelper.getShortenedNumber(number)
)
Snackbar.make(binding!!.root, snackbarText, Snackbar.LENGTH_LONG).show()
}
override fun onItemClick(item: SmartListViewModel) {
presenter.contactRequestClicked(item.accountId, item.uri)
}
override fun onItemLongClick(item: SmartListViewModel) {}
override fun onItemLongClick(item: SmartListViewModel) {
MaterialAlertDialogBuilder(requireContext())
.setItems(R.array.swarm_actions) { dialog, which ->
when (which) {
0 -> presenter.copyNumber(item)
1 -> presenter.removeConversation(item)
2 -> presenter.banContact(item)
}
}
.show()
}
companion object {
private val TAG = ContactRequestsFragment::class.java.simpleName
val ACCOUNT_ID = TAG + "accountID"
private val TAG = ContactRequestsFragment::class.simpleName!!
private const val SCROLL_DIRECTION_UP = -1
}
}
\ No newline at end of file
......@@ -105,8 +105,8 @@ class SmartListFragment : BaseSupportFragment<SmartListPresenter, SmartListView>
)
searchView.imeOptions = EditorInfo.IME_ACTION_GO
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val editText = searchView.findViewById<EditText>(R.id.search_src_text)
editText?.setAutofillHints(View.AUTOFILL_HINT_USERNAME)
searchView.findViewById<EditText>(R.id.search_src_text)
?.setAutofillHints(View.AUTOFILL_HINT_USERNAME)
}
mSearchMenuItem = searchMenuItem
mDialpadMenuItem = dialpadMenuItem
......@@ -119,16 +119,17 @@ class SmartListFragment : BaseSupportFragment<SmartListPresenter, SmartListView>
}
fun handleIntent(intent: Intent) {
if (mSearchView != null && intent.action != null) {
val searchView = mSearchView ?: return
if (intent.action != null) {
when (intent.action) {
Intent.ACTION_VIEW, Intent.ACTION_CALL -> mSearchView!!.setQuery(intent.dataString, true)
Intent.ACTION_VIEW, Intent.ACTION_CALL -> searchView.setQuery(intent.dataString, true)
Intent.ACTION_DIAL -> {
mSearchMenuItem?.expandActionView()
mSearchView?.setQuery(intent.dataString, false)
searchView.setQuery(intent.dataString, false)
}
Intent.ACTION_SEARCH -> {
mSearchMenuItem?.expandActionView()
mSearchView?.setQuery(intent.getStringExtra(SearchManager.QUERY), true)
searchView.setQuery(intent.getStringExtra(SearchManager.QUERY), true)
}
else -> {}
}
......@@ -294,15 +295,15 @@ class SmartListFragment : BaseSupportFragment<SmartListPresenter, SmartListView>
}
override fun displayClearDialog(conversationUri: Uri) {
ActionHelper.launchClearAction(activity, conversationUri, this@SmartListFragment)
ActionHelper.launchClearAction(requireContext(), conversationUri, this@SmartListFragment)
}
override fun displayDeleteDialog(conversationUri: Uri) {
ActionHelper.launchDeleteAction(activity, conversationUri, this@SmartListFragment)
ActionHelper.launchDeleteAction(requireContext(), conversationUri, this@SmartListFragment)
}
override fun copyNumber(uri: Uri) {
ActionHelper.launchCopyNumberToClipboardFromContact(activity, uri, this)
copyContactNumberToClipboard(uri.toString())
}
override fun displayMenuItem() {
......
......@@ -45,6 +45,7 @@ import androidx.core.graphics.drawable.IconCompat
import androidx.core.util.Pair
import com.bumptech.glide.Glide
import cx.ring.R
import cx.ring.account.AccountEditionFragment
import cx.ring.client.CallActivity
import cx.ring.client.ConversationActivity
import cx.ring.client.HomeActivity
......@@ -495,7 +496,7 @@ class NotificationServiceImpl(
.setContentTitle(mContext.getString(R.string.contact_request_title))
val intentOpenTrustRequestFragment = Intent(HomeActivity.ACTION_PRESENT_TRUST_REQUEST_FRAGMENT)
.setClass(mContext, HomeActivity::class.java)
.putExtra(ContactRequestsFragment.ACCOUNT_ID, accountId)
.putExtra(AccountEditionFragment.ACCOUNT_ID_KEY, accountId)
builder.setContentIntent(PendingIntent.getActivity(mContext, random.nextInt(), intentOpenTrustRequestFragment, PendingIntent.FLAG_ONE_SHOT))
builder.color = ResourcesCompat.getColor(mContext.resources, R.color.color_primary_dark, null)
return builder
......
/*
* Copyright (C) 2004-2021 Savoir-faire Linux Inc.
*
* Author: Alexandre Lision <alexandre.lision@savoirfairelinux.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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package cx.ring.utils;
import android.content.ActivityNotFoundException;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.provider.ContactsContract;
import android.util.Log;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.ArrayList;
import cx.ring.R;
import net.jami.model.Contact;
import net.jami.model.Conversation;
import net.jami.model.Uri;
public class ActionHelper {
public static final String TAG = ActionHelper.class.getSimpleName();
public static final int ACTION_COPY = 0;
public static final int ACTION_CLEAR = 1;
public static final int ACTION_DELETE = 2;
public static final int ACTION_BLOCK = 3;
private ActionHelper() {
}
public static void launchClearAction(final Context context,
final Uri uri,
final Conversation.ConversationActionCallback callback) {
if (context == null) {
Log.d(TAG, "launchClearAction: activity is null");
return;
}
if (uri == null) {
Log.d(TAG, "launchClearAction: conversation is null");
return;
}
new MaterialAlertDialogBuilder(context)
.setTitle(R.string.conversation_action_history_clear_title)
.setMessage(R.string.conversation_action_history_clear_message)
.setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
if (callback != null) {
callback.clearConversation(uri);
}
})
.setNegativeButton(android.R.string.cancel, (dialog, whichButton) -> {
/* Terminate with no action */
})
.show();
}
public static void launchDeleteAction(final Context context,
final Uri uri,
final Conversation.ConversationActionCallback callback) {
if (context == null) {
Log.d(TAG, "launchDeleteAction: activity is null");
return;
}
if (uri == null) {
Log.d(TAG, "launchDeleteAction: conversation is null");
return;
}
new MaterialAlertDialogBuilder(context)
.setTitle(R.string.conversation_action_remove_this_title)
.setMessage(R.string.conversation_action_remove_this_message)
.setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
if (callback != null) {
callback.removeConversation(uri);
}
})
.setNegativeButton(android.R.string.cancel, (dialog, whichButton) -> {
/* Terminate with no action */
})
.show();
}
public static void launchCopyNumberToClipboardFromContact(final Context context,
final Uri uri,
final Conversation.ConversationActionCallback callback) {
if (uri == null) {
Log.d(TAG, "launchCopyNumberToClipboardFromContact: uri is null");
return;
}
if (context == null) {
Log.d(TAG, "launchCopyNumberToClipboardFromContact: activity is null");
return;
}
if (callback != null) {
callback.copyContactNumberToClipboard(uri.toString());
}
}
public static Intent getAddNumberIntentForContact(Contact contact) {
final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
ArrayList<ContentValues> data = new ArrayList<>();
ContentValues values = new ContentValues();
Uri number = contact.getUri();
if (number.isHexId()) {
values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE);
values.put(ContactsContract.CommonDataKinds.Im.DATA, number.getRawUriString());
values.put(ContactsContract.CommonDataKinds.Im.PROTOCOL, ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM);
values.put(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL, "Ring");
} else {
values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE);
values.put(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS, number.getRawUriString());
}
data.add(values);
intent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, data);
return intent;
}
public static void displayContact(Context context, Contact contact) {
if (context == null) {
Log.d(TAG, "displayContact: context is null");
return;
}
if (contact.getId() != Contact.UNKNOWN_ID) {
Log.d(TAG, "displayContact: contact is known, displaying...");
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
android.net.Uri uri = android.net.Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI,
String.valueOf(contact.getId()));
intent.setData(uri);
context.startActivity(intent);
} catch (ActivityNotFoundException exc) {
Log.e(TAG, "Error displaying contact", exc);
}
}
}
public static String getShortenedNumber(String number) {
if (number != null && !number.isEmpty() && number.length() > 18) {
int size = number.length();
return number.substring(0, 9).concat("\u2026").concat(number.substring(size - 9, size));
}
return number;
}
}
/*
* Copyright (C) 2004-2021 Savoir-faire Linux Inc.
*
* Author: Alexandre Lision <alexandre.lision@savoirfairelinux.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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package cx.ring.utils
import android.content.*
import net.jami.model.Conversation.ConversationActionCallback
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import cx.ring.R
import net.jami.model.Contact
import android.provider.ContactsContract
import android.util.Log
import net.jami.model.Uri
import java.util.ArrayList
object ActionHelper {
val TAG = ActionHelper::class.simpleName!!
const val ACTION_COPY = 0
const val ACTION_CLEAR = 1
const val ACTION_DELETE = 2
const val ACTION_BLOCK = 3
fun launchClearAction(context: Context, uri: Uri, callback: ConversationActionCallback) {
MaterialAlertDialogBuilder(context)
.setTitle(R.string.conversation_action_history_clear_title)
.setMessage(R.string.conversation_action_history_clear_message)
.setPositiveButton(android.R.string.ok) { dialog: DialogInterface?, whichButton: Int ->
callback.clearConversation(uri)
}
.setNegativeButton(android.R.string.cancel) { dialog: DialogInterface?, whichButton: Int -> }
.show()
}
fun launchDeleteAction(context: Context, uri: Uri, callback: ConversationActionCallback) {
MaterialAlertDialogBuilder(context)
.setTitle(R.string.conversation_action_remove_this_title)
.setMessage(R.string.conversation_action_remove_this_message)
.setPositiveButton(android.R.string.ok) { dialog: DialogInterface?, whichButton: Int ->
callback.removeConversation(uri)
}
.setNegativeButton(android.R.string.cancel) { dialog: DialogInterface?, whichButton: Int -> }
.show()
}
fun getAddNumberIntentForContact(contact: Contact): Intent {
val intent = Intent(Intent.ACTION_INSERT_OR_EDIT)
intent.type = ContactsContract.Contacts.CONTENT_ITEM_TYPE
val data = ArrayList<ContentValues>()
val values = ContentValues()
val number = contact.uri
if (number.isHexId) {
values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE)
values.put(ContactsContract.CommonDataKinds.Im.DATA, number.rawUriString)
values.put(
ContactsContract.CommonDataKinds.Im.PROTOCOL,
ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM
)
values.put(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL, "Ring")
} else {
values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE)
values.put(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS, number.rawUriString)
}
data.add(values)
intent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, data)
return intent
}
fun displayContact(context: Context, contact: Contact) {
if (contact.id != Contact.UNKNOWN_ID) {
Log.d(TAG, "displayContact: contact is known, displaying...")
try {
val intent = Intent(Intent.ACTION_VIEW)
val uri = android.net.Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, contact.id.toString())
intent.data = uri
context.startActivity(intent)
} catch (exc: ActivityNotFoundException) {
Log.e(TAG, "Error displaying contact", exc)
}
}
}
fun getShortenedNumber(number: String?): String? {
if (number != null && number.isNotEmpty() && number.length > 18) {
val size = number.length
return number.substring(0, 9) + "\u2026" + number.substring(size - 9, size)
}
return number
}
}
\ No newline at end of file
......@@ -44,11 +44,11 @@ class ContactRequestsPresenter @Inject internal constructor(
mCompositeDisposable.add(mConversationFacade.getPendingList(mAccount)
.switchMap { viewModels: List<Observable<SmartListViewModel>> ->
if (viewModels.isEmpty()) SmartListViewModel.EMPTY_RESULTS
else Observable.combineLatest(viewModels) { obs: Array<Any> -> obs.mapTo(ArrayList(obs.size))
else Observable.combineLatest(viewModels) { obs -> obs.mapTo(ArrayList(obs.size))
{ ob -> ob as SmartListViewModel } }
}
.observeOn(mUiScheduler)
.subscribe({ viewModels -> view?.updateView(viewModels, mCompositeDisposable) })
.subscribe({ viewModels -> this.view?.updateView(viewModels, mCompositeDisposable) })
{ e: Throwable -> Log.d(TAG, "updateList subscribe onError", e) })
}
......@@ -71,6 +71,24 @@ class ContactRequestsPresenter @Inject internal constructor(
view?.goToConversation(accountId, uri)
}
fun removeConversation(item: SmartListViewModel) {
mConversationFacade.discardRequest(item.accountId, item.uri)
}
fun banContact(item: SmartListViewModel) {
mConversationFacade.discardRequest(item.accountId, item.uri)
mAccountService.removeContact(item.accountId, item.uri.host, true)
}
fun copyNumber(item: SmartListViewModel) {
val contact = item.getContact()
if (contact != null) {
view?.copyNumber(contact.uri)
} else {
view?.copyNumber(item.uri)
}
}
companion object {
private val TAG = ContactRequestsPresenter::class.simpleName!!
}
......
......@@ -26,4 +26,5 @@ interface ContactRequestsView {
fun updateView(list: MutableList<SmartListViewModel>, disposable: CompositeDisposable)
fun updateItem(item: SmartListViewModel)
fun goToConversation(accountId: String, contactId: Uri)
fun copyNumber(uri: Uri)
}
\ No newline at end of file
......@@ -56,8 +56,8 @@ class ConversationPresenter @Inject constructor(
private val mConversationSubject: Subject<Conversation> = BehaviorSubject.create()
fun init(conversationUri: Uri, accountId: String) {
Log.w(TAG, "init $conversationUri $accountId")
if (conversationUri == mConversationUri) return
Log.w(TAG, "init $conversationUri $accountId")
val settings = mPreferencesService.settings
view?.setSettings(settings.enableReadIndicator, settings.enableLinkPreviews)
mConversationUri = conversationUri
......
......@@ -110,11 +110,9 @@ class Contact private constructor(
val ids: ArrayList<String>
get() {
val ret = ArrayList<String>(phones.size + if (id == UNKNOWN_ID.toLong()) 0 else 1)
if (id != UNKNOWN_ID.toLong()) ret.add(
"c:" + java.lang.Long.toHexString(
id
)
val ret = ArrayList<String>(phones.size + if (id == UNKNOWN_ID) 0 else 1)
if (id != UNKNOWN_ID) ret.add(
"c:" + java.lang.Long.toHexString(id)
)
for (p in phones) ret.add(p.number.rawUriString)
return ret
......@@ -215,8 +213,8 @@ class Contact private constructor(
companion object {
private val TAG = Contact::class.simpleName!!
const val UNKNOWN_ID = -1
const val DEFAULT_ID = 0
const val UNKNOWN_ID = -1L
const val DEFAULT_ID = 0L
const val PREFIX_RING = Uri.RING_URI_SCHEME
fun buildSIP(to: Uri): Contact {
......
......@@ -41,7 +41,6 @@ class SmartListPresenter @Inject constructor(
private val mConversationFacade: ConversationFacade,
@param:Named("UiScheduler") private val mUiScheduler: Scheduler
) : RootPresenter<SmartListView>() {
//private final CompositeDisposable mConversationDisposable = new CompositeDisposable();
private var mQueryDisposable: Disposable? = null
private val mCurrentQuery: Subject<String> = BehaviorSubject.createDefault("")
private val mQuery: Subject<String> = PublishSubject.create()
......@@ -53,15 +52,32 @@ class SmartListPresenter @Inject constructor(
override fun bindView(view: SmartListView) {
super.bindView(view)
//mCompositeDisposable.clear();
//mCompositeDisposable.add(mConversationDisposable);
showConversations(mConversationFacade.getFullList(accountSubject, mCurrentQuery, true))
view.setLoading(true)
mCompositeDisposable.add(mConversationFacade.getFullList(accountSubject, mCurrentQuery, true)
.switchMap { viewModels ->
if (viewModels.isEmpty()) SmartListViewModel.EMPTY_RESULTS
else Observable.combineLatest(viewModels) { obs -> obs.mapTo(ArrayList(obs.size), {ob -> ob as SmartListViewModel}) }
.throttleLatest(150, TimeUnit.MILLISECONDS, mUiScheduler)
}
.observeOn(mUiScheduler)
.subscribe({ viewModels: MutableList<SmartListViewModel> ->
val v = this.view ?: return@subscribe
v.setLoading(false)
if (viewModels.isEmpty()) {
v.hideList()
v.displayNoConversationMessage()
} else {
v.hideNoConversationMessage()
v.updateList(viewModels, mCompositeDisposable)
}
}) { e: Throwable -> Log.w(TAG, "showConversations error ", e) })
}
fun queryTextChanged(query: String) {
if (query.isEmpty()) {
if (mQueryDisposable != null) {
mQueryDisposable!!.dispose()
mQueryDisposable?.let { disposable ->
disposable.dispose()
mQueryDisposable = null
}
mCurrentQuery.onNext(query)
......@@ -78,11 +94,11 @@ class SmartListPresenter @Inject constructor(
}
fun conversationLongClicked(smartListViewModel: SmartListViewModel) {
view!!.displayConversationDialog(smartListViewModel)
view?.displayConversationDialog(smartListViewModel)
}
fun fabButtonClicked() {
view!!.displayMenuItem()
view?.displayMenuItem()
}
private fun startConversation(accountId: String, conversationUri: Uri?) {
......@@ -94,40 +110,35 @@ class SmartListPresenter @Inject constructor(
}
fun startConversation(uri: Uri) {
view!!.goToConversation(mAccount!!.accountId, uri)
view?.goToConversation(mAccount!!.accountId, uri)
}
fun copyNumber(smartListViewModel: SmartListViewModel) {
if (smartListViewModel.isSwarm) {
// Copy first contact's URI for a swarm
// TODO other modes
val contact = smartListViewModel.contacts[0]
view!!.copyNumber(contact.uri)
val contact = smartListViewModel.getContact()
if (contact != null) {
view?.copyNumber(contact.uri)
} else {
view!!.copyNumber(smartListViewModel.uri)
view?.copyNumber(smartListViewModel.uri)
}
}
fun clearConversation(smartListViewModel: SmartListViewModel) {
view!!.displayClearDialog(smartListViewModel.uri)
view?.displayClearDialog(smartListViewModel.uri)
}
fun clearConversation(uri: Uri?) {
mCompositeDisposable.add(
mConversationFacade
.clearHistory(mAccount!!.accountId, uri!!)
.subscribeOn(Schedulers.computation()).subscribe()
)
mCompositeDisposable.add(mConversationFacade
.clearHistory(mAccount!!.accountId, uri!!)
.subscribeOn(Schedulers.computation()).subscribe())
}
fun removeConversation(smartListViewModel: SmartListViewModel) {
view!!.displayDeleteDialog(smartListViewModel.uri)
view?.displayDeleteDialog(smartListViewModel.uri)
}
fun removeConversation(uri: Uri) {
mCompositeDisposable.add(
mConversationFacade.removeConversation(mAccount!!.accountId, uri)
.subscribe())
mCompositeDisposable.add(mConversationFacade.removeConversation(mAccount!!.accountId, uri)
.subscribe())
}
fun banContact(smartListViewModel: SmartListViewModel) {
......@@ -135,37 +146,13 @@ class SmartListPresenter @Inject constructor(
}
fun clickQRSearch() {
view!!.goToQRFragment()
view?.goToQRFragment()
}
private fun showConversations(conversations: Observable<List<Observable<SmartListViewModel>>>) {
//mConversationDisposable.clear();
view!!.setLoading(true)
mCompositeDisposable.add(conversations
.switchMap { viewModels: List<Observable<SmartListViewModel>> ->
if (viewModels.isEmpty())
SmartListViewModel.EMPTY_RESULTS
else Observable.combineLatest(viewModels) { obs: Array<Any> -> //obs.map { it as SmartListViewModel }
val vms: MutableList<SmartListViewModel> = ArrayList(obs.size)
for (ob in obs) vms.add(ob as SmartListViewModel)
vms
}.throttleLatest(150, TimeUnit.MILLISECONDS, mUiScheduler)
}
.observeOn(mUiScheduler)
.subscribe({ viewModels: MutableList<SmartListViewModel> ->
val view = view
view!!.setLoading(false)
if (viewModels.isEmpty()) {
view.hideList()
view.displayNoConversationMessage()
return@subscribe
}
view.hideNoConversationMessage()
view.updateList(viewModels, mCompositeDisposable)
}) { e: Throwable -> Log.w(TAG, "showConversations error ", e) })
}
companion object {
private val TAG = SmartListPresenter::class.java.simpleName
private val TAG = SmartListPresenter::class.simpleName!!
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment