Add Group Call peeking in the Conversation view.

This commit is contained in:
Cody Henthorne
2020-12-02 13:20:38 -05:00
committed by Greyson Parrelli
parent 2729eb9f5f
commit 01f143667f
18 changed files with 309 additions and 30 deletions

View File

@@ -25,6 +25,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ShortcutManager;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Color;
@@ -82,6 +83,7 @@ import com.annimon.stream.Stream;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition;
import com.google.android.material.button.MaterialButton;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
@@ -135,6 +137,7 @@ import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher;
import org.thoughtcrime.securesms.conversation.ConversationGroupViewModel.GroupActiveState;
import org.thoughtcrime.securesms.conversation.ConversationMessage.ConversationMessageFactory;
import org.thoughtcrime.securesms.conversation.ui.error.SafetyNumberChangeDialog;
import org.thoughtcrime.securesms.conversation.ui.groupcall.GroupCallViewModel;
import org.thoughtcrime.securesms.conversation.ui.mentions.MentionsPickerViewModel;
import org.thoughtcrime.securesms.conversationlist.model.MessageResult;
import org.thoughtcrime.securesms.crypto.SecurityEvent;
@@ -159,6 +162,7 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.database.model.ReactionRecord;
import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.events.GroupCallPeekEvent;
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
import org.thoughtcrime.securesms.groups.GroupChangeException;
@@ -369,6 +373,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
private View requestingMemberBanner;
private View cancelJoinRequest;
private Stub<View> mentionsSuggestions;
private MaterialButton joinGroupCallButton;
private LinkPreviewViewModel linkPreviewViewModel;
private ConversationSearchViewModel searchViewModel;
@@ -377,6 +382,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
private InviteReminderModel inviteReminderModel;
private ConversationGroupViewModel groupViewModel;
private MentionsPickerViewModel mentionsViewModel;
private GroupCallViewModel groupCallViewModel;
private LiveRecipient recipient;
private long threadId;
@@ -426,6 +432,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
initializeViewModel(args);
initializeGroupViewModel();
initializeMentionsViewModel();
initializeGroupCallViewModel();
initializeEnabledCheck();
initializePendingRequestsBanner();
initializeGroupV1MigrationsBanners();
@@ -532,6 +539,10 @@ public class ConversationActivity extends PassphraseRequiredActivity
.enqueue();
}
if (groupCallViewModel != null) {
groupCallViewModel.peekGroupCall(this);
}
setVisibleThread(threadId);
ConversationUtil.refreshRecipientShortcuts();
}
@@ -816,6 +827,9 @@ public class ConversationActivity extends PassphraseRequiredActivity
} else if (isGroupConversation()) {
if (isActiveV2Group && FeatureFlags.groupCalling()) {
inflater.inflate(R.menu.conversation_callable_groupv2, menu);
if (groupCallViewModel != null && Boolean.TRUE.equals(groupCallViewModel.hasActiveGroupCall().getValue())) {
hideMenuItem(menu, R.id.menu_video_secure);
}
}
inflater.inflate(R.menu.conversation_group_options, menu);
@@ -1895,6 +1909,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
noLongerMemberBanner = findViewById(R.id.conversation_no_longer_member_banner);
requestingMemberBanner = findViewById(R.id.conversation_requesting_banner);
cancelJoinRequest = findViewById(R.id.conversation_cancel_request);
joinGroupCallButton = findViewById(R.id.conversation_group_cal_join);
container.addOnKeyboardShownListener(this);
inputPanel.setListener(this);
@@ -1949,6 +1964,8 @@ public class ConversationActivity extends PassphraseRequiredActivity
inlineAttachmentButton.setOnClickListener(v -> handleAddAttachment());
reactionOverlay.setOnReactionSelectedListener(this);
joinGroupCallButton.setOnClickListener(v -> handleVideo(getRecipient()));
}
protected void initializeActionBar() {
@@ -2105,6 +2122,21 @@ public class ConversationActivity extends PassphraseRequiredActivity
});
}
public void initializeGroupCallViewModel() {
groupCallViewModel = ViewModelProviders.of(this, new GroupCallViewModel.Factory()).get(GroupCallViewModel.class);
recipient.observe(this, r -> {
groupCallViewModel.onRecipientChange(this, r);
});
groupCallViewModel.hasActiveGroupCall().observe(this, hasActiveCall -> {
invalidateOptionsMenu();
joinGroupCallButton.setVisibility(hasActiveCall ? View.VISIBLE : View.GONE);
});
groupCallViewModel.canJoinGroupCall().observe(this, canJoin -> joinGroupCallButton.setText(canJoin ? R.string.ConversationActivity_join : R.string.ConversationActivity_full));
}
private void showStickerIntroductionTooltip() {
TextSecurePreferences.setMediaKeyboardMode(this, MediaKeyboardMode.STICKER);
inputPanel.setMediaKeyboardToggleMode(true);
@@ -2218,6 +2250,10 @@ public class ConversationActivity extends PassphraseRequiredActivity
if (mentionsViewModel != null) {
mentionsViewModel.onRecipientChange(recipient);
}
if (groupCallViewModel != null) {
groupCallViewModel.onRecipientChange(this, recipient);
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
@@ -2239,6 +2275,13 @@ public class ConversationActivity extends PassphraseRequiredActivity
}
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onGroupCallPeekEvent(@NonNull GroupCallPeekEvent event) {
if (groupCallViewModel != null) {
groupCallViewModel.onGroupCallPeekEvent(event);
}
}
private void initializeReceivers() {
securityUpdateReceiver = new BroadcastReceiver() {
@Override
@@ -2396,8 +2439,13 @@ public class ConversationActivity extends PassphraseRequiredActivity
private void setActionBarColor(MaterialColor color) {
ActionBar supportActionBar = getSupportActionBar();
if (supportActionBar == null) throw new AssertionError();
supportActionBar.setBackgroundDrawable(new ColorDrawable(color.toActionBarColor(this)));
int actionBarColor = color.toActionBarColor(this);
supportActionBar.setBackgroundDrawable(new ColorDrawable(actionBarColor));
WindowUtil.setStatusBarColor(getWindow(), color.toStatusBarColor(this));
joinGroupCallButton.setTextColor(actionBarColor);
joinGroupCallButton.setIconTint(ColorStateList.valueOf(actionBarColor));
joinGroupCallButton.setRippleColor(ColorStateList.valueOf(actionBarColor));
}
private void setBlockedUserState(Recipient recipient, boolean isSecureText, boolean isDefaultSms) {

View File

@@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.BindableConversationItem;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.VerifyIdentityActivity;
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
import org.thoughtcrime.securesms.database.model.GroupCallUpdateDetailsUtil;
import org.thoughtcrime.securesms.database.model.LiveUpdateMessage;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.UpdateDescription;
@@ -182,13 +183,16 @@ public final class ConversationUpdateItem extends LinearLayout
}
});
} else if (conversationMessage.getMessageRecord().isGroupCall()) {
UpdateDescription updateDescription = MessageRecord.getGroupCallUpdateDescription(getContext(), conversationMessage.getMessageRecord().getBody(), true);
Collection<UUID> uuids = updateDescription.getMentioned();
int text = 0;
if (Util.hasItems(uuids)) {
text = uuids.contains(TextSecurePreferences.getLocalUuid(getContext())) ? R.string.ConversationUpdateItem_return_to_call : R.string.ConversationUpdateItem_join_call;
if (GroupCallUpdateDetailsUtil.parse(conversationMessage.getMessageRecord().getBody()).getIsCallFull()) {
text = R.string.ConversationUpdateItem_call_is_full;
} else {
text = uuids.contains(TextSecurePreferences.getLocalUuid(getContext())) ? R.string.ConversationUpdateItem_return_to_call : R.string.ConversationUpdateItem_join_call;
}
}
if (text != 0) {

View File

@@ -0,0 +1,90 @@
package org.thoughtcrime.securesms.conversation.ui.groupcall;
import android.content.Context;
import android.content.Intent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import org.thoughtcrime.securesms.events.GroupCallPeekEvent;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.ringrtc.RemotePeer;
import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.util.FeatureFlags;
import java.util.Objects;
public class GroupCallViewModel extends ViewModel {
private static final String TAG = Log.tag(GroupCallViewModel.class);
private final MutableLiveData<Boolean> activeGroupCall;
private final MutableLiveData<Boolean> canJoin;
private @Nullable Recipient currentRecipient;
GroupCallViewModel() {
this.activeGroupCall = new MutableLiveData<>(false);
this.canJoin = new MutableLiveData<>(false);
}
public @NonNull LiveData<Boolean> hasActiveGroupCall() {
return activeGroupCall;
}
public @NonNull LiveData<Boolean> canJoinGroupCall() {
return canJoin;
}
public void onRecipientChange(@NonNull Context context, @Nullable Recipient recipient) {
if (Objects.equals(currentRecipient, recipient)) {
return;
}
activeGroupCall.postValue(false);
canJoin.postValue(false);
currentRecipient = recipient;
peekGroupCall(context);
}
public void peekGroupCall(@NonNull Context context) {
if (isGroupCallCapable(currentRecipient)) {
Log.i(TAG, "peek call for " + currentRecipient.getId());
Intent intent = new Intent(context, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_GROUP_CALL_PEEK)
.putExtra(WebRtcCallService.EXTRA_REMOTE_PEER, new RemotePeer(currentRecipient.getId()));
context.startService(intent);
}
}
public void onGroupCallPeekEvent(@NonNull GroupCallPeekEvent groupCallPeekEvent) {
if (isGroupCallCapable(currentRecipient) && groupCallPeekEvent.getGroupRecipientId().equals(currentRecipient.getId())) {
Log.i(TAG, "update UI with call event: active call: " + groupCallPeekEvent.hasActiveCall() + " canJoin: " + groupCallPeekEvent.canJoinCall());
activeGroupCall.postValue(groupCallPeekEvent.hasActiveCall());
canJoin.postValue(groupCallPeekEvent.canJoinCall());
} else {
Log.i(TAG, "Ignore call event for different recipient.");
}
}
private static boolean isGroupCallCapable(@Nullable Recipient recipient) {
return recipient != null && recipient.isActiveGroup() && recipient.isPushV2Group() && FeatureFlags.groupCalling();
}
public static final class Factory implements ViewModelProvider.Factory {
@Override
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection ConstantConditions
return modelClass.cast(new GroupCallViewModel());
}
}
}