diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 984deedb..3b25121b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -82,6 +82,12 @@ android:label="@string/speed_dial" android:parentActivityName=".activities.SettingsActivity" /> + + 1) { accounts.forEachIndexed { index, account -> - if (account == CallManager.call?.details?.accountHandle) { + if (account == CallManager.getPrimaryCall()?.details?.accountHandle) { call_sim_id.text = "${index + 1}" call_sim_id.beVisible() call_sim_image.beVisible() @@ -395,8 +406,9 @@ class CallActivity : SimpleActivity() { } } - private fun updateCallState(call: Call?) { - val state = call?.getStateCompat() + private fun updateCallState(call: Call) { + val state = call.getStateCompat() + Log.d(TAG, "updateCallState: $state") when (state) { Call.STATE_RINGING -> callRinging() Call.STATE_ACTIVE -> callStarted() @@ -407,17 +419,22 @@ class CallActivity : SimpleActivity() { val statusTextId = when (state) { Call.STATE_RINGING -> R.string.is_calling - Call.STATE_DIALING -> R.string.dialing + Call.STATE_CONNECTING, Call.STATE_DIALING -> R.string.dialing else -> 0 } if (statusTextId != 0) { call_status_label.text = getString(statusTextId) } + + call_manage.beVisibleIf(call.hasCapability(Call.Details.CAPABILITY_MANAGE_CONFERENCE)) + setActionButtonEnabled(call_swap, state == Call.STATE_ACTIVE) + setActionButtonEnabled(call_merge, state == Call.STATE_ACTIVE) } private fun updateState() { val phoneState = CallManager.getPhoneState() + Log.d(TAG, "updateState: $phoneState") if (phoneState is SingleCall) { updateCallState(phoneState.call) updateCallOnHoldState(null) @@ -426,7 +443,6 @@ class CallActivity : SimpleActivity() { || state == Call.STATE_DISCONNECTING || state == Call.STATE_HOLDING) setActionButtonEnabled(call_toggle_hold, isSingleCallActionsEnabled) setActionButtonEnabled(call_add, isSingleCallActionsEnabled) - call_manage.beVisibleIf(phoneState.call.isConference()) } else if (phoneState is TwoCalls) { updateCallState(phoneState.active) updateCallOnHoldState(phoneState.onHold) @@ -436,7 +452,7 @@ class CallActivity : SimpleActivity() { private fun updateCallOnHoldState(call: Call?) { val hasCallOnHold = call != null if (hasCallOnHold) { - CallManager.getCallContact(applicationContext, call) { contact -> + getCallContact(applicationContext, call) { contact -> runOnUiThread { on_hold_caller_name.text = getContactNameOrNumber(contact) } @@ -448,18 +464,15 @@ class CallActivity : SimpleActivity() { } private fun updateCallContactInfo(call: Call?) { - if (call.isConference()) { - caller_avatar.setImageDrawable(null) - caller_number.text = null - caller_name_label.text = getString(R.string.conference) - } else { - CallManager.getCallContact(applicationContext, call) { contact -> - callContact = contact - val avatar = callContactAvatarHelper.getCallContactAvatar(contact) - runOnUiThread { - updateOtherPersonsInfo(avatar) - checkCalledSIMCard() - } + getCallContact(applicationContext, call) { contact -> + if (call != CallManager.getPrimaryCall()) { + return@getCallContact + } + callContact = contact + val avatar = if (!call.isConference()) callContactAvatarHelper.getCallContactAvatar(contact) else null + runOnUiThread { + updateOtherPersonsInfo(avatar) + checkCalledSIMCard() } } } @@ -489,7 +502,7 @@ class CallActivity : SimpleActivity() { private fun showPhoneAccountPicker() { if (callContact != null) { getHandleToUse(intent, callContact!!.number) { handle -> - CallManager.call?.phoneAccountSelected(handle, false) + CallManager.getPrimaryCall()?.phoneAccountSelected(handle, false) } } } @@ -523,11 +536,13 @@ class CallActivity : SimpleActivity() { } private val callCallback = object : CallManagerListener { - override fun onStateChanged(call: Call, state: Int) { + override fun onStateChanged() { updateState() } override fun onPrimaryCallChanged(call: Call) { + Log.d(TAG, "onPrimaryCallChanged: $call") + callDurationHandler.removeCallbacks(updateCallDurationTask) updateCallContactInfo(call) updateState() } @@ -535,7 +550,7 @@ class CallActivity : SimpleActivity() { private val updateCallDurationTask = object : Runnable { override fun run() { - callDuration = CallManager.getCallDuration() + callDuration = CallManager.getPrimaryCall().getCallDuration() if (!isCallEnded) { call_status_label.text = callDuration.getFormattedDuration() callDurationHandler.postDelayed(this, 1000) diff --git a/app/src/main/kotlin/com/simplemobiletools/dialer/activities/ConferenceActivity.kt b/app/src/main/kotlin/com/simplemobiletools/dialer/activities/ConferenceActivity.kt new file mode 100644 index 00000000..6ad3c2d1 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/dialer/activities/ConferenceActivity.kt @@ -0,0 +1,18 @@ +package com.simplemobiletools.dialer.activities + +import android.os.Bundle +import com.simplemobiletools.dialer.R +import com.simplemobiletools.dialer.adapters.ConferenceCallsAdapter +import com.simplemobiletools.dialer.helpers.CallManager +import kotlinx.android.synthetic.main.activity_conference.* + +class ConferenceActivity : SimpleActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_conference) + supportActionBar + + conference_calls_list.adapter = ConferenceCallsAdapter(this, conference_calls_list, ArrayList(CallManager.getConferenceCalls())) {} + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/dialer/adapters/ConferenceCallsAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/dialer/adapters/ConferenceCallsAdapter.kt new file mode 100644 index 00000000..2d9b339d --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/dialer/adapters/ConferenceCallsAdapter.kt @@ -0,0 +1,93 @@ +package com.simplemobiletools.dialer.adapters + +import android.telecom.Call +import android.view.Menu +import android.view.ViewGroup +import com.bumptech.glide.Glide +import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter +import com.simplemobiletools.commons.helpers.LOWER_ALPHA +import com.simplemobiletools.commons.helpers.SimpleContactsHelper +import com.simplemobiletools.commons.views.MyRecyclerView +import com.simplemobiletools.dialer.R +import com.simplemobiletools.dialer.activities.SimpleActivity +import com.simplemobiletools.dialer.extensions.hasCapability +import com.simplemobiletools.dialer.helpers.getCallContact +import kotlinx.android.synthetic.main.item_conference_call.view.* + +class ConferenceCallsAdapter( + activity: SimpleActivity, recyclerView: MyRecyclerView, val data: ArrayList, itemClick: (Any) -> Unit +) : MyRecyclerViewAdapter(activity, recyclerView, itemClick) { + + override fun actionItemPressed(id: Int) { + } + + override fun getActionMenuId(): Int = 0 + + override fun getIsItemSelectable(position: Int): Boolean = false + + override fun getItemCount(): Int = data.size + + override fun getItemKeyPosition(key: Int): Int = -1 + + override fun getItemSelectionKey(position: Int): Int? = null + + override fun getSelectableItemCount(): Int = data.size + + override fun onActionModeCreated() { + } + + override fun onActionModeDestroyed() { + } + + override fun prepareActionMode(menu: Menu) { + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = createViewHolder(R.layout.item_conference_call, parent) + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val call = data[position] + holder.bindView(call, allowSingleClick = false, allowLongClick = false) { itemView, _ -> + getCallContact(itemView.context, call) { callContact -> + itemView.post { + itemView.item_conference_call_name.text = callContact.name.ifEmpty { itemView.context.getString(R.string.unknown_caller) } + SimpleContactsHelper(activity).loadContactImage( + callContact.photoUri, + itemView.item_conference_call_image, + callContact.name, + activity.getDrawable(R.drawable.ic_person_vector) + ) + } + } + val canSeparate = call.hasCapability(Call.Details.CAPABILITY_SEPARATE_FROM_CONFERENCE) + val canDisconnect = call.hasCapability(Call.Details.CAPABILITY_DISCONNECT_FROM_CONFERENCE) + itemView.item_conference_call_split.isEnabled = canSeparate + itemView.item_conference_call_split.alpha = if (canSeparate) 1.0f else LOWER_ALPHA + itemView.item_conference_call_split.setOnClickListener { + call.splitFromConference() + data.removeAt(position) + notifyItemRemoved(position) + if (data.size == 1) { + activity.finish() + } + } + itemView.item_conference_call_end.isEnabled = canDisconnect + itemView.item_conference_call_end.alpha = if (canDisconnect) 1.0f else LOWER_ALPHA + itemView.item_conference_call_end.setOnClickListener { + call.disconnect() + data.removeAt(position) + notifyItemRemoved(position) + if (data.size == 1) { + activity.finish() + } + } + } + bindViewHolder(holder) + } + + override fun onViewRecycled(holder: ViewHolder) { + super.onViewRecycled(holder) + if (!activity.isDestroyed && !activity.isFinishing) { + Glide.with(activity).clear(holder.itemView.item_conference_call_image) + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/dialer/extensions/Call.kt b/app/src/main/kotlin/com/simplemobiletools/dialer/extensions/Call.kt index e66f1b16..80f1173f 100644 --- a/app/src/main/kotlin/com/simplemobiletools/dialer/extensions/Call.kt +++ b/app/src/main/kotlin/com/simplemobiletools/dialer/extensions/Call.kt @@ -19,12 +19,22 @@ fun Call?.getStateCompat(): Int { } } +fun Call?.getCallDuration(): Int { + return if (this != null) { + val connectTimeMillis = details.connectTimeMillis + if (connectTimeMillis == 0L) { + return 0 + } + ((System.currentTimeMillis() - connectTimeMillis) / 1000).toInt() + } else { + 0 + } +} + fun Call.isOutgoing(): Boolean { return OUTGOING_CALL_STATES.contains(getStateCompat()) } -fun Call.hasCapability(capability: Int): Boolean = details.callCapabilities and capability != 0 - -fun Call.hasProperty(property: Int): Boolean = details.hasProperty(property) +fun Call.hasCapability(capability: Int): Boolean = (details.callCapabilities and capability) != 0 fun Call?.isConference(): Boolean = this?.details?.hasProperty(Call.Details.PROPERTY_CONFERENCE) == true diff --git a/app/src/main/kotlin/com/simplemobiletools/dialer/helpers/CallContactHelper.kt b/app/src/main/kotlin/com/simplemobiletools/dialer/helpers/CallContactHelper.kt new file mode 100644 index 00000000..3f234793 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/dialer/helpers/CallContactHelper.kt @@ -0,0 +1,71 @@ +package com.simplemobiletools.dialer.helpers + +import android.content.Context +import android.net.Uri +import android.telecom.Call +import com.simplemobiletools.commons.extensions.getMyContactsCursor +import com.simplemobiletools.commons.extensions.getPhoneNumberTypeText +import com.simplemobiletools.commons.helpers.MyContactsContentProvider +import com.simplemobiletools.commons.helpers.SimpleContactsHelper +import com.simplemobiletools.commons.helpers.ensureBackgroundThread +import com.simplemobiletools.dialer.R +import com.simplemobiletools.dialer.extensions.isConference +import com.simplemobiletools.dialer.models.CallContact + +fun getCallContact(context: Context, call: Call?, callback: (CallContact) -> Unit) { + if (call.isConference()) { + callback(CallContact(context.getString(R.string.conference), "", "", "")) + return + } + val privateCursor = context.getMyContactsCursor(false, true) + ensureBackgroundThread { + val callContact = CallContact("", "", "", "") + val handle = try { + call?.details?.handle?.toString() + } catch (e: NullPointerException) { + null + } + + if (handle == null) { + callback(callContact) + return@ensureBackgroundThread + } + + val uri = Uri.decode(handle) + if (uri.startsWith("tel:")) { + val number = uri.substringAfter("tel:") + SimpleContactsHelper(context).getAvailableContacts(false) { contacts -> + val privateContacts = MyContactsContentProvider.getSimpleContacts(context, privateCursor) + if (privateContacts.isNotEmpty()) { + contacts.addAll(privateContacts) + } + + val contactsWithMultipleNumbers = contacts.filter { it.phoneNumbers.size > 1 } + val numbersToContactIDMap = HashMap() + contactsWithMultipleNumbers.forEach { contact -> + contact.phoneNumbers.forEach { phoneNumber -> + numbersToContactIDMap[phoneNumber.value] = contact.contactId + numbersToContactIDMap[phoneNumber.normalizedNumber] = contact.contactId + } + } + + callContact.number = number + val contact = contacts.firstOrNull { it.doesHavePhoneNumber(number) } + if (contact != null) { + callContact.name = contact.name + callContact.photoUri = contact.photoUri + + if (contact.phoneNumbers.size > 1) { + val specificPhoneNumber = contact.phoneNumbers.firstOrNull { it.value == number } + if (specificPhoneNumber != null) { + callContact.numberLabel = context.getPhoneNumberTypeText(specificPhoneNumber.type, specificPhoneNumber.label) + } + } + } else { + callContact.name = number + } + callback(callContact) + } + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/dialer/helpers/CallManager.kt b/app/src/main/kotlin/com/simplemobiletools/dialer/helpers/CallManager.kt index e8f7c996..ea3d1b40 100644 --- a/app/src/main/kotlin/com/simplemobiletools/dialer/helpers/CallManager.kt +++ b/app/src/main/kotlin/com/simplemobiletools/dialer/helpers/CallManager.kt @@ -1,35 +1,31 @@ package com.simplemobiletools.dialer.helpers -import android.content.Context -import android.net.Uri +import android.annotation.SuppressLint import android.telecom.Call import android.telecom.InCallService import android.telecom.VideoProfile import android.util.Log -import com.simplemobiletools.commons.extensions.getMyContactsCursor -import com.simplemobiletools.commons.extensions.getPhoneNumberTypeText -import com.simplemobiletools.commons.helpers.MyContactsContentProvider -import com.simplemobiletools.commons.helpers.SimpleContactsHelper -import com.simplemobiletools.commons.helpers.ensureBackgroundThread import com.simplemobiletools.dialer.extensions.getStateCompat import com.simplemobiletools.dialer.extensions.hasCapability import com.simplemobiletools.dialer.extensions.isConference -import com.simplemobiletools.dialer.models.CallContact import java.util.concurrent.CopyOnWriteArraySet const val TAG = "SimpleDialer:CallManager" +const val TAG2 = TAG /*"SimpleDialer:CallState"*/ // inspired by https://github.com/Chooloo/call_manage class CallManager { companion object { - var call: Call? = null + @SuppressLint("StaticFieldLeak") var inCallService: InCallService? = null - val calls = mutableListOf() + private var call: Call? = null + private val calls = mutableListOf() private val listeners = CopyOnWriteArraySet() fun onCallAdded(call: Call) { this.call = call calls.add(call) + Log.d(TAG, "onCallAdded (${calls.size}): $call") for (listener in listeners) { listener.onPrimaryCallChanged(call) } @@ -37,13 +33,15 @@ class CallManager { override fun onStateChanged(call: Call, state: Int) { Log.d(TAG, "onStateChanged: $call") updateState() - for (listener in listeners) { - listener.onStateChanged(call, state) - } + } + + override fun onDetailsChanged(call: Call, details: Call.Details) { + Log.d(TAG, "onDetailsChanged") + updateState() } override fun onConferenceableCallsChanged(call: Call, conferenceableCalls: MutableList) { - Log.d(TAG, "onConferenceableCallsChanged: $call, conferenceableCalls size=${conferenceableCalls.size}") + Log.d(TAG, "onConferenceableCallsChanged (${conferenceableCalls.size}): $call") updateState() } }) @@ -51,34 +49,59 @@ class CallManager { fun onCallRemoved(call: Call) { calls.remove(call) + Log.d(TAG, "onCallRemoved (${calls.size}): $call") updateState() } fun getPhoneState(): PhoneState { return when (calls.size) { 0 -> { + Log.d(TAG2, "No call") NoCall } 1 -> { + Log.d(TAG2, "Single call") SingleCall(calls.first()) } 2 -> { + Log.d(TAG2, "Two calls") val active = calls.find { it.getStateCompat() == Call.STATE_ACTIVE } val newCall = calls.find { it.getStateCompat() == Call.STATE_CONNECTING || it.getStateCompat() == Call.STATE_DIALING } val onHold = calls.find { it.getStateCompat() == Call.STATE_HOLDING } if (active != null && newCall != null) { TwoCalls(newCall, active) - } else if(newCall != null && onHold != null) { + } else if (newCall != null && onHold != null) { TwoCalls(newCall, onHold) - } else if(active != null && onHold != null) { + } else if (active != null && onHold != null) { TwoCalls(active, onHold) } else { TwoCalls(calls[0], calls[1]) } } else -> { - SingleCall(calls.find { it.isConference() }!!) - // TODO handle the call on hold (outside the conference) + val conference = calls.find { it.isConference() }!! + val secondCall = if (conference.children.size + 1 != calls.size) { + calls.filter { !it.isConference() } + .subtract(conference.children.toSet()) + .firstOrNull() + } else { + null + } + Log.d(TAG2, "Conference call (${conference.children.size} children)") + Log.d(TAG2, "secondCall: $secondCall") + if (secondCall == null) { + Log.d(TAG2, "Conference call (single)") + SingleCall(conference) + } else { + val newCallState = secondCall.getStateCompat() + if (newCallState == Call.STATE_ACTIVE || newCallState == Call.STATE_CONNECTING || newCallState == Call.STATE_DIALING) { + Log.d(TAG2, "Conference call and regular call (conference on hold)") + TwoCalls(secondCall, conference) + } else { + Log.d(TAG2, "Conference call and regular call (regular call on hold)") + TwoCalls(conference, secondCall) + } + } } } } @@ -89,6 +112,7 @@ class CallManager { is SingleCall -> phoneState.call is TwoCalls -> phoneState.active } + var notify = true if (primaryCall == null) { call = null } else if (primaryCall != call) { @@ -96,6 +120,12 @@ class CallManager { for (listener in listeners) { listener.onPrimaryCallChanged(primaryCall) } + notify = false + } + if (notify) { + for (listener in listeners) { + listener.onStateChanged() + } } } @@ -103,11 +133,8 @@ class CallManager { return call } - fun getSecondaryCall(): Call? { - if (calls.size == 1) { - return null - } - return calls.find { it.getStateCompat() == Call.STATE_HOLDING } + fun getConferenceCalls(): List { + return calls.find { it.isConference() }?.children ?: emptyList() } fun accept() { @@ -134,11 +161,10 @@ class CallManager { return !isOnHold } - val isConference: Boolean - get() = call?.details?.hasProperty(Call.Details.PROPERTY_CONFERENCE) ?: false - fun swap() { - getSecondaryCall()?.unhold() + if (calls.size > 1) { + calls.find { it.getStateCompat() == Call.STATE_HOLDING }?.unhold() + } } fun merge() { @@ -166,85 +192,15 @@ class CallManager { call?.playDtmfTone(c) call?.stopDtmfTone() } - - fun getCallContact(context: Context, callback: (CallContact?) -> Unit) { - return getCallContact(context, call, callback) - } - - fun getCallContact(context: Context, call: Call?, callback: (CallContact) -> Unit) { - val privateCursor = context.getMyContactsCursor(false, true) - ensureBackgroundThread { - val callContact = CallContact("", "", "", "") - val handle = try { - call?.details?.handle?.toString() - } catch (e: NullPointerException) { - null - } - - if (handle == null) { - callback(callContact) - return@ensureBackgroundThread - } - - val uri = Uri.decode(handle) - if (uri.startsWith("tel:")) { - val number = uri.substringAfter("tel:") - SimpleContactsHelper(context).getAvailableContacts(false) { contacts -> - val privateContacts = MyContactsContentProvider.getSimpleContacts(context, privateCursor) - if (privateContacts.isNotEmpty()) { - contacts.addAll(privateContacts) - } - - val contactsWithMultipleNumbers = contacts.filter { it.phoneNumbers.size > 1 } - val numbersToContactIDMap = HashMap() - contactsWithMultipleNumbers.forEach { contact -> - contact.phoneNumbers.forEach { phoneNumber -> - numbersToContactIDMap[phoneNumber.value] = contact.contactId - numbersToContactIDMap[phoneNumber.normalizedNumber] = contact.contactId - } - } - - callContact.number = number - val contact = contacts.firstOrNull { it.doesHavePhoneNumber(number) } - if (contact != null) { - callContact.name = contact.name - callContact.photoUri = contact.photoUri - - if (contact.phoneNumbers.size > 1) { - val specificPhoneNumber = contact.phoneNumbers.firstOrNull { it.value == number } - if (specificPhoneNumber != null) { - callContact.numberLabel = context.getPhoneNumberTypeText(specificPhoneNumber.type, specificPhoneNumber.label) - } - } - } else { - callContact.name = number - } - callback(callContact) - } - } - } - } - - fun getCallDuration(): Int { - return if (call != null) { - val connectTimeMillis = call!!.details.connectTimeMillis - if (connectTimeMillis == 0L) { - return 0 - } - ((System.currentTimeMillis() - connectTimeMillis) / 1000).toInt() - } else { - 0 - } - } } } interface CallManagerListener { - fun onStateChanged(call: Call, state: Int) + fun onStateChanged() fun onPrimaryCallChanged(call: Call) } sealed class PhoneState object NoCall : PhoneState() -class SingleCall(val call: Call?) : PhoneState() +class SingleCall(val call: Call) : PhoneState() class TwoCalls(val active: Call, val onHold: Call) : PhoneState() diff --git a/app/src/main/kotlin/com/simplemobiletools/dialer/helpers/CallNotificationManager.kt b/app/src/main/kotlin/com/simplemobiletools/dialer/helpers/CallNotificationManager.kt index 324123b4..ecd286c6 100644 --- a/app/src/main/kotlin/com/simplemobiletools/dialer/helpers/CallNotificationManager.kt +++ b/app/src/main/kotlin/com/simplemobiletools/dialer/helpers/CallNotificationManager.kt @@ -28,7 +28,7 @@ class CallNotificationManager(private val context: Context) { @SuppressLint("NewApi") fun setupNotification() { - CallManager.getCallContact(context.applicationContext) { callContact -> + getCallContact(context.applicationContext, CallManager.getPrimaryCall()) { callContact -> val callContactAvatar = callContactAvatarHelper.getCallContactAvatar(callContact) val callState = CallManager.getState() val isHighPriority = context.powerManager.isInteractive && callState == Call.STATE_RINGING diff --git a/app/src/main/kotlin/com/simplemobiletools/dialer/services/CallService.kt b/app/src/main/kotlin/com/simplemobiletools/dialer/services/CallService.kt index b6ba9283..43315238 100644 --- a/app/src/main/kotlin/com/simplemobiletools/dialer/services/CallService.kt +++ b/app/src/main/kotlin/com/simplemobiletools/dialer/services/CallService.kt @@ -4,15 +4,13 @@ import android.app.KeyguardManager import android.content.Context import android.telecom.Call import android.telecom.InCallService -import android.util.Log import com.simplemobiletools.dialer.activities.CallActivity import com.simplemobiletools.dialer.extensions.getStateCompat import com.simplemobiletools.dialer.extensions.isOutgoing import com.simplemobiletools.dialer.extensions.powerManager import com.simplemobiletools.dialer.helpers.CallManager import com.simplemobiletools.dialer.helpers.CallNotificationManager - -const val TAG = "SimpleDialer:CallService" +import com.simplemobiletools.dialer.helpers.NoCall class CallService : InCallService() { private val callNotificationManager by lazy { CallNotificationManager(this) } @@ -45,18 +43,18 @@ class CallService : InCallService() { override fun onCallRemoved(call: Call) { super.onCallRemoved(call) - Log.d(TAG, "onCallRemoved: $call") call.unregisterCallback(callListener) + val wasPrimaryCall = call == CallManager.getPrimaryCall() CallManager.onCallRemoved(call) - if (CallManager.calls.isEmpty()) { - CallManager.call = null + if (CallManager.getPhoneState() == NoCall) { CallManager.inCallService = null callNotificationManager.cancelNotification() } else { callNotificationManager.setupNotification() - startActivity(CallActivity.getStartIntent(this)) + if (wasPrimaryCall) { + startActivity(CallActivity.getStartIntent(this)) + } } - Log.d(TAG, "onCallRemoved: calls=${CallManager.calls.size}") } override fun onDestroy() { diff --git a/app/src/main/res/drawable/ic_call_split_vector.xml b/app/src/main/res/drawable/ic_call_split_vector.xml new file mode 100644 index 00000000..5afba98d --- /dev/null +++ b/app/src/main/res/drawable/ic_call_split_vector.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_call.xml b/app/src/main/res/layout/activity_call.xml index bf927509..52e3ae73 100644 --- a/app/src/main/res/layout/activity_call.xml +++ b/app/src/main/res/layout/activity_call.xml @@ -207,9 +207,9 @@ android:contentDescription="@string/hold_call" android:padding="@dimen/medium_margin" android:src="@drawable/ic_pause_vector" - app:layout_constraintEnd_toStartOf="@id/call_add" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/call_toggle_speaker" /> + app:layout_constraintEnd_toEndOf="@+id/call_toggle_microphone" + app:layout_constraintStart_toStartOf="@+id/call_toggle_microphone" + app:layout_constraintTop_toBottomOf="@+id/call_toggle_microphone" /> + app:layout_constraintEnd_toEndOf="@+id/call_dialpad" + app:layout_constraintStart_toStartOf="@+id/call_dialpad" + app:layout_constraintTop_toBottomOf="@+id/call_dialpad" + tools:visibility="visible" /> + app:layout_constraintStart_toStartOf="@+id/call_toggle_microphone" + app:layout_constraintTop_toBottomOf="@+id/call_toggle_microphone" /> + + + + + diff --git a/app/src/main/res/layout/item_conference_call.xml b/app/src/main/res/layout/item_conference_call.xml new file mode 100644 index 00000000..0833746b --- /dev/null +++ b/app/src/main/res/layout/item_conference_call.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + +