Manage recipient activity.

master
Alan Evans 2020-06-16 17:42:54 -03:00 committed by Greyson Parrelli
parent d9641128a8
commit b53827f32b
32 changed files with 1869 additions and 102 deletions

View File

@ -267,6 +267,10 @@
android:windowSoftInputMode="stateAlwaysHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".recipients.ui.managerecipient.ManageRecipientActivity"
android:windowSoftInputMode="stateAlwaysHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".DatabaseMigrationActivity"
android:theme="@style/NoAnimation.Theme.AppCompat.Light.DarkActionBar"
android:launchMode="singleTask"

View File

@ -272,12 +272,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
private class ProfileClickListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
Intent intent = new Intent(preference.getContext(), EditProfileActivity.class);
intent.putExtra(EditProfileActivity.EXCLUDE_SYSTEM, true);
intent.putExtra(EditProfileActivity.DISPLAY_USERNAME, true);
intent.putExtra(EditProfileActivity.NEXT_BUTTON_TEXT, R.string.save);
requireActivity().startActivity(intent);
requireActivity().startActivity(EditProfileActivity.getIntentForUserProfileEdit(preference.getContext()));
return true;
}
}

View File

@ -73,6 +73,7 @@ import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.recipients.ui.managerecipient.ManageRecipientActivity;
import org.thoughtcrime.securesms.util.CommunicationActions;
import org.thoughtcrime.securesms.util.DynamicDarkToolbarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
@ -117,6 +118,15 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
private CollapsingToolbarLayout toolbarLayout;
public static @NonNull Intent getLaunchIntent(@NonNull Context context, @NonNull RecipientId id) {
if (FeatureFlags.newGroupUI()) {
return ManageRecipientActivity.newIntent(context, id);
}
return getOldLaunchIntent(context, id);
}
@Deprecated
public static Intent getOldLaunchIntent(@NonNull Context context, @NonNull RecipientId id) {
Intent intent = new Intent(context, RecipientPreferenceActivity.class);
intent.putExtra(RecipientPreferenceActivity.RECIPIENT_ID, id);

View File

@ -1,6 +1,8 @@
package org.thoughtcrime.securesms.color;
import android.content.Context;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -52,7 +54,7 @@ public class MaterialColors {
return null;
}
public int[] asConversationColorArray(@NonNull Context context) {
public @ColorInt int[] asConversationColorArray(@NonNull Context context) {
int[] results = new int[colors.size()];
int index = 0;

View File

@ -2,13 +2,11 @@ package org.thoughtcrime.securesms.contacts.avatars;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.InsetDrawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Build;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.DrawableCompat;
import org.thoughtcrime.securesms.R;
@ -18,7 +16,16 @@ import org.thoughtcrime.securesms.util.ViewUtil;
import java.util.Objects;
public final class GroupFallbackPhoto80 implements FallbackContactPhoto {
public final class FallbackPhoto80dp implements FallbackContactPhoto {
@DrawableRes private final int drawable80dp;
private final MaterialColor backgroundColor;
public FallbackPhoto80dp(@DrawableRes int drawable80dp, @NonNull MaterialColor backgroundColor) {
this.drawable80dp = drawable80dp;
this.backgroundColor = backgroundColor;
}
@Override
public Drawable asDrawable(Context context, int color) {
return buildDrawable(context);
@ -41,12 +48,12 @@ public final class GroupFallbackPhoto80 implements FallbackContactPhoto {
private @NonNull Drawable buildDrawable(@NonNull Context context) {
Drawable background = DrawableCompat.wrap(Objects.requireNonNull(AppCompatResources.getDrawable(context, R.drawable.circle_tintable)));
Drawable foreground = AppCompatResources.getDrawable(context, R.drawable.ic_group_80);
Drawable foreground = AppCompatResources.getDrawable(context, drawable80dp);
Drawable gradient = ThemeUtil.getThemedDrawable(context, R.attr.resource_placeholder_gradient);
LayerDrawable drawable = new LayerDrawable(new Drawable[]{background, foreground, gradient});
int foregroundInset = ViewUtil.dpToPx(24);
DrawableCompat.setTint(background, MaterialColor.ULTRAMARINE.toAvatarColor(context));
DrawableCompat.setTint(background, backgroundColor.toAvatarColor(context));
drawable.setLayerInset(1, foregroundInset, foregroundInset, foregroundInset, foregroundInset);

View File

@ -91,7 +91,7 @@ public class AddGroupDetailsFragment extends Fragment {
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
create = view.findViewById(R.id.create);
name = view.findViewById(R.id.group_name);
name = view.findViewById(R.id.name);
toolbar = view.findViewById(R.id.toolbar);
setCreateEnabled(false, false);

View File

@ -27,10 +27,11 @@ import org.thoughtcrime.securesms.MediaPreviewActivity;
import org.thoughtcrime.securesms.MuteDialog;
import org.thoughtcrime.securesms.PushContactSelectionActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.ThreadPhotoRailView;
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.GroupFallbackPhoto80;
import org.thoughtcrime.securesms.contacts.avatars.FallbackPhoto80dp;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.groups.ui.GroupMemberListView;
import org.thoughtcrime.securesms.groups.ui.LeaveGroupDialog;
@ -97,7 +98,7 @@ public class ManageGroupFragment extends Fragment {
private final Recipient.FallbackPhotoProvider fallbackPhotoProvider = new Recipient.FallbackPhotoProvider() {
@Override
public @NonNull FallbackContactPhoto getPhotoForGroup() {
return new GroupFallbackPhoto80();
return new FallbackPhoto80dp(R.drawable.ic_group_80, MaterialColor.ULTRAMARINE);
}
};
@ -120,7 +121,7 @@ public class ManageGroupFragment extends Fragment {
avatar = view.findViewById(R.id.group_avatar);
toolbar = view.findViewById(R.id.toolbar);
groupName = view.findViewById(R.id.group_name);
groupName = view.findViewById(R.id.name);
memberCountUnderAvatar = view.findViewById(R.id.member_count);
memberCountAboveList = view.findViewById(R.id.member_count_2);
groupMemberList = view.findViewById(R.id.group_members);

View File

@ -13,7 +13,6 @@ import androidx.navigation.Navigation;
import org.thoughtcrime.securesms.BaseActionBarActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.DynamicRegistrationTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
@ -35,6 +34,14 @@ public class EditProfileActivity extends BaseActionBarActivity implements EditPr
return intent;
}
public static @NonNull Intent getIntentForUserProfileEdit(@NonNull Context context) {
Intent intent = new Intent(context, EditProfileActivity.class);
intent.putExtra(EditProfileActivity.EXCLUDE_SYSTEM, true);
intent.putExtra(EditProfileActivity.DISPLAY_USERNAME, true);
intent.putExtra(EditProfileActivity.NEXT_BUTTON_TEXT, R.string.save);
return intent;
}
public static @NonNull Intent getIntentForGroupProfile(@NonNull Context context, @NonNull GroupId.Push groupId) {
Intent intent = new Intent(context, EditProfileActivity.class);
intent.putExtra(EditProfileActivity.SHOW_TOOLBAR, true);

View File

@ -1,4 +1,4 @@
package org.thoughtcrime.securesms.groups.ui.creategroup;
package org.thoughtcrime.securesms.recipients.ui;
import android.content.Context;
import android.graphics.Rect;
@ -19,17 +19,17 @@ import org.thoughtcrime.securesms.R;
import java.lang.ref.WeakReference;
public final class GroupSettingsCoordinatorLayoutBehavior extends CoordinatorLayout.Behavior<View> {
public final class RecipientSettingsCoordinatorLayoutBehavior extends CoordinatorLayout.Behavior<View> {
private static final Interpolator INTERPOLATOR = new DecelerateInterpolator();
private final ViewRef avatarTargetRef = new ViewRef(R.id.avatar_target);
private final ViewRef groupNameRef = new ViewRef(R.id.group_name);
private final ViewRef groupNameTargetRef = new ViewRef(R.id.group_name_target);
private final Rect targetRect = new Rect();
private final Rect childRect = new Rect();
private final ViewReference avatarTargetRef = new ViewReference(R.id.avatar_target);
private final ViewReference nameRef = new ViewReference(R.id.name);
private final ViewReference nameTargetRef = new ViewReference(R.id.name_target);
private final Rect targetRect = new Rect();
private final Rect childRect = new Rect();
public GroupSettingsCoordinatorLayoutBehavior(@NonNull Context context, @Nullable AttributeSet attrs) {
public RecipientSettingsCoordinatorLayoutBehavior(@NonNull Context context, @Nullable AttributeSet attrs) {
}
@Override
@ -71,8 +71,8 @@ public final class GroupSettingsCoordinatorLayoutBehavior extends CoordinatorLay
}
private void updateNamePosition(@NonNull CoordinatorLayout parent, float factor) {
TextView child = (TextView) groupNameRef.require(parent);
View target = groupNameTargetRef.require(parent);
TextView child = (TextView) nameRef.require(parent);
View target = nameTargetRef.require(parent);
targetRect.set(target.getLeft(), target.getTop(), target.getRight(), target.getBottom());
childRect.set(child.getLeft(), child.getTop(), child.getRight(), child.getBottom());
@ -95,13 +95,13 @@ public final class GroupSettingsCoordinatorLayoutBehavior extends CoordinatorLay
return parent.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR ? rect.left : rect.right;
}
private static final class ViewRef {
private static final class ViewReference {
private WeakReference<View> ref = new WeakReference<>(null);
private final @IdRes int idRes;
private ViewRef(@IdRes int idRes) {
private ViewReference(@IdRes int idRes) {
this.idRes = idRes;
}

View File

@ -0,0 +1,60 @@
package org.thoughtcrime.securesms.recipients.ui.managerecipient;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityOptionsCompat;
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
public class ManageRecipientActivity extends PassphraseRequiredActionBarActivity {
private static final String RECIPIENT_ID = "RECIPIENT_ID";
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
public static Intent newIntent(@NonNull Context context, @NonNull RecipientId recipientId) {
Intent intent = new Intent(context, ManageRecipientActivity.class);
intent.putExtra(RECIPIENT_ID, recipientId);
return intent;
}
public static @Nullable Bundle createTransitionBundle(@NonNull Context activityContext, @NonNull View from) {
if (activityContext instanceof Activity) {
return ActivityOptionsCompat.makeSceneTransitionAnimation((Activity) activityContext, from, "avatar").toBundle();
} else {
return null;
}
}
@Override
protected void onPreCreate() {
dynamicTheme.onCreate(this);
}
@Override
protected void onCreate(Bundle savedInstanceState, boolean ready) {
super.onCreate(savedInstanceState, ready);
setContentView(R.layout.recipient_manage_activity);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, ManageRecipientFragment.newInstance(getIntent().getParcelableExtra(RECIPIENT_ID)))
.commitNow();
}
}
@Override
public void onResume() {
super.onResume();
dynamicTheme.onResume(this);
}
}

View File

@ -0,0 +1,349 @@
package org.thoughtcrime.securesms.recipients.ui.managerecipient;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.SwitchCompat;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.core.view.ViewCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.ViewModelProviders;
import com.takisoft.colorpicker.ColorPickerDialog;
import com.takisoft.colorpicker.ColorStateDrawable;
import org.thoughtcrime.securesms.AvatarPreviewActivity;
import org.thoughtcrime.securesms.MediaPreviewActivity;
import org.thoughtcrime.securesms.MuteDialog;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.color.MaterialColors;
import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.ThreadPhotoRailView;
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.FallbackPhoto80dp;
import org.thoughtcrime.securesms.groups.ui.GroupMemberListView;
import org.thoughtcrime.securesms.mediaoverview.MediaOverviewActivity;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.profiles.edit.EditProfileActivity;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.ui.notifications.CustomNotificationsDialogFragment;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.LifecycleCursorWrapper;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.Util;
import java.util.Locale;
import java.util.Objects;
public class ManageRecipientFragment extends Fragment {
private static final String RECIPIENT_ID = "RECIPIENT_ID";
private static final int RETURN_FROM_MEDIA = 405;
private ManageRecipientViewModel viewModel;
private GroupMemberListView sharedGroupList;
private Toolbar toolbar;
private TextView name;
private TextView usernameNumber;
private AvatarImageView avatar;
private ThreadPhotoRailView threadPhotoRailView;
private View mediaCard;
private ManageRecipientViewModel.CursorFactory cursorFactory;
private View sharedMediaRow;
private View disappearingMessagesCard;
private View disappearingMessagesRow;
private TextView disappearingMessages;
private View colorRow;
private ImageView colorChip;
private TextView block;
private TextView unblock;
private TextView addToAGroup;
private SwitchCompat muteNotificationsSwitch;
private View muteNotificationsRow;
private TextView muteNotificationsUntilLabel;
private TextView customNotificationsButton;
private View customNotificationsRow;
private View toggleAllGroups;
private View viewSafetyNumber;
private TextView groupsInCommonCount;
private View messageButton;
private View secureCallButton;
private View secureVideoCallButton;
static ManageRecipientFragment newInstance(@NonNull RecipientId recipientId) {
ManageRecipientFragment fragment = new ManageRecipientFragment();
Bundle args = new Bundle();
args.putParcelable(RECIPIENT_ID, recipientId);
fragment.setArguments(args);
return fragment;
}
@Override
public @Nullable View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.recipient_manage_fragment, container, false);
avatar = view.findViewById(R.id.recipient_avatar);
toolbar = view.findViewById(R.id.toolbar);
name = view.findViewById(R.id.name);
usernameNumber = view.findViewById(R.id.username_number);
sharedGroupList = view.findViewById(R.id.shared_group_list);
groupsInCommonCount = view.findViewById(R.id.groups_in_common_count);
threadPhotoRailView = view.findViewById(R.id.recent_photos);
mediaCard = view.findViewById(R.id.recipient_media_card);
sharedMediaRow = view.findViewById(R.id.shared_media_row);
disappearingMessagesCard = view.findViewById(R.id.recipient_disappearing_messages_card);
disappearingMessagesRow = view.findViewById(R.id.disappearing_messages_row);
disappearingMessages = view.findViewById(R.id.disappearing_messages);
colorRow = view.findViewById(R.id.color_row);
colorChip = view.findViewById(R.id.color_chip);
block = view.findViewById(R.id.block);
unblock = view.findViewById(R.id.unblock);
viewSafetyNumber = view.findViewById(R.id.view_safety_number);
addToAGroup = view.findViewById(R.id.add_to_a_group);
muteNotificationsUntilLabel = view.findViewById(R.id.recipient_mute_notifications_until);
muteNotificationsSwitch = view.findViewById(R.id.recipient_mute_notifications_switch);
muteNotificationsRow = view.findViewById(R.id.recipient_mute_notifications_row);
customNotificationsButton = view.findViewById(R.id.recipient_custom_notifications_button);
customNotificationsRow = view.findViewById(R.id.recipient_custom_notifications_row);
toggleAllGroups = view.findViewById(R.id.toggle_all_groups);
messageButton = view.findViewById(R.id.recipient_message);
secureCallButton = view.findViewById(R.id.recipient_voice_call);
secureVideoCallButton = view.findViewById(R.id.recipient_video_call);
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
RecipientId recipientId = Objects.requireNonNull(requireArguments().getParcelable(RECIPIENT_ID));
ManageRecipientViewModel.Factory factory = new ManageRecipientViewModel.Factory(recipientId);
viewModel = ViewModelProviders.of(requireActivity(), factory).get(ManageRecipientViewModel.class);
viewModel.getVisibleSharedGroups().observe(getViewLifecycleOwner(), members -> sharedGroupList.setMembers(members));
viewModel.getSharedGroupsCountSummary().observe(getViewLifecycleOwner(), members -> groupsInCommonCount.setText(members));
viewModel.getCanCollapseMemberList().observe(getViewLifecycleOwner(), canCollapseMemberList -> {
if (canCollapseMemberList) {
toggleAllGroups.setVisibility(View.VISIBLE);
toggleAllGroups.setOnClickListener(v -> viewModel.revealCollapsedMembers());
} else {
toggleAllGroups.setVisibility(View.GONE);
}
});
viewModel.getIdentity().observe(getViewLifecycleOwner(), identityRecord -> {
viewSafetyNumber.setVisibility(identityRecord != null ? View.VISIBLE : View.GONE);
if (identityRecord != null) {
viewSafetyNumber.setOnClickListener(view -> viewModel.onViewSafetyNumberClicked(requireActivity(), identityRecord));
}
});
toolbar.setNavigationOnClickListener(v -> requireActivity().onBackPressed());
toolbar.setOnMenuItemClickListener(this::onMenuItemSelected);
toolbar.inflateMenu(R.menu.manage_recipient_fragment);
if (recipientId.equals(Recipient.self().getId())) {
toolbar.getMenu().findItem(R.id.action_edit).setVisible(true);
}
viewModel.getName().observe(getViewLifecycleOwner(), name::setText);
viewModel.getDisappearingMessageTimer().observe(getViewLifecycleOwner(), string -> disappearingMessages.setText(string));
viewModel.getRecipient().observe(getViewLifecycleOwner(), this::presentRecipient);
viewModel.getMediaCursor().observe(getViewLifecycleOwner(), this::presentMediaCursor);
viewModel.getMuteState().observe(getViewLifecycleOwner(), this::presentMuteState);
disappearingMessagesRow.setOnClickListener(v -> viewModel.handleExpirationSelection(requireContext()));
block.setOnClickListener(v -> viewModel.onBlockClicked(requireActivity()));
unblock.setOnClickListener(v -> viewModel.onUnblockClicked(requireActivity()));
addToAGroup.setOnClickListener(v -> viewModel.onAddToGroupButton(requireActivity()));
sharedGroupList.setRecipientClickListener(recipient -> viewModel.onGroupClicked(requireActivity(), recipient));
sharedGroupList.setOverScrollMode(View.OVER_SCROLL_NEVER);
muteNotificationsRow.setOnClickListener(v -> {
if (muteNotificationsSwitch.isEnabled()) {
muteNotificationsSwitch.toggle();
}
});
customNotificationsRow.setVisibility(View.VISIBLE);
customNotificationsRow.setOnClickListener(v -> CustomNotificationsDialogFragment.create(recipientId)
.show(requireFragmentManager(), "CUSTOM_NOTIFICATIONS"));
//noinspection CodeBlock2Expr
if (NotificationChannels.supported()) {
viewModel.hasCustomNotifications().observe(getViewLifecycleOwner(), hasCustomNotifications -> {
customNotificationsButton.setText(hasCustomNotifications ? R.string.ManageRecipientActivity_on
: R.string.ManageRecipientActivity_off);
});
}
viewModel.getCanBlock().observe(getViewLifecycleOwner(), canBlock -> {
block.setVisibility(canBlock ? View.VISIBLE : View.GONE);
unblock.setVisibility(canBlock ? View.GONE : View.VISIBLE);
});
messageButton.setOnClickListener(v -> viewModel.onMessage(requireActivity()));
secureCallButton.setOnClickListener(v -> viewModel.onSecureCall(requireActivity()));
secureVideoCallButton.setOnClickListener(v -> viewModel.onSecureVideoCall(requireActivity()));
}
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RETURN_FROM_MEDIA) {
applyMediaCursorFactory();
}
}
private void presentRecipient(@NonNull Recipient recipient) {
disappearingMessagesCard.setVisibility(recipient.isRegistered() ? View.VISIBLE : View.GONE);
addToAGroup.setVisibility(recipient.isRegistered() ? View.VISIBLE : View.GONE);
MaterialColor recipientColor = recipient.getColor();
avatar.setFallbackPhotoProvider(new Recipient.FallbackPhotoProvider() {
@Override
public @NonNull FallbackContactPhoto getPhotoForRecipientWithoutName() {
return new FallbackPhoto80dp(R.drawable.ic_profile_80, recipientColor);
}
});
avatar.setRecipient(recipient);
avatar.setOnClickListener(v -> {
FragmentActivity activity = requireActivity();
activity.startActivity(AvatarPreviewActivity.intentFromRecipientId(activity, recipient.getId()),
AvatarPreviewActivity.createTransitionBundle(activity, avatar));
});
@ColorInt int color = recipientColor.toActionBarColor(requireContext());
Drawable[] colorDrawable = new Drawable[]{ContextCompat.getDrawable(requireContext(), R.drawable.colorpickerpreference_pref_swatch)};
colorChip.setImageDrawable(new ColorStateDrawable(colorDrawable, color));
colorRow.setOnClickListener(v -> handleColorSelection(color));
String usernameNumberString = String.format("%s %s", recipient.getUsername().or(""), recipient.getSmsAddress().or(""))
.trim();
usernameNumber.setText(usernameNumberString);
usernameNumber.setVisibility(TextUtils.isEmpty(usernameNumberString) ? View.GONE : View.VISIBLE);
usernameNumber.setOnLongClickListener(v -> {
Util.copyToClipboard(v.getContext(), usernameNumber.getText().toString());
ServiceUtil.getVibrator(v.getContext()).vibrate(250);
Toast.makeText(v.getContext(), R.string.RecipientBottomSheet_copied_to_clipboard, Toast.LENGTH_SHORT).show();
return true;
});
secureCallButton.setVisibility(recipient.isRegistered() ? View.VISIBLE : View.GONE);
secureVideoCallButton.setVisibility(recipient.isRegistered() ? View.VISIBLE : View.GONE);
}
private void presentMediaCursor(ManageRecipientViewModel.MediaCursor mediaCursor) {
if (mediaCursor == null) return;
sharedMediaRow.setOnClickListener(v -> startActivity(MediaOverviewActivity.forThread(requireContext(), mediaCursor.getThreadId())));
setMediaCursorFactory(mediaCursor.getMediaCursorFactory());
threadPhotoRailView.setListener(mediaRecord ->
startActivityForResult(MediaPreviewActivity.intentFromMediaRecord(requireContext(),
mediaRecord,
ViewCompat.getLayoutDirection(threadPhotoRailView) == ViewCompat.LAYOUT_DIRECTION_LTR),
RETURN_FROM_MEDIA));
}
private void presentMuteState(@NonNull ManageRecipientViewModel.MuteState muteState) {
if (muteNotificationsSwitch.isChecked() != muteState.isMuted()) {
muteNotificationsSwitch.setOnCheckedChangeListener(null);
muteNotificationsSwitch.setChecked(muteState.isMuted());
}
muteNotificationsSwitch.setEnabled(true);
muteNotificationsSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (isChecked) {
MuteDialog.show(requireContext(), viewModel::setMuteUntil, () -> muteNotificationsSwitch.setChecked(false));
} else {
viewModel.clearMuteUntil();
}
});
muteNotificationsUntilLabel.setVisibility(muteState.isMuted() ? View.VISIBLE : View.GONE);
if (muteState.isMuted()) {
muteNotificationsUntilLabel.setText(getString(R.string.ManageRecipientActivity_until_s,
DateUtils.getTimeString(requireContext(),
Locale.getDefault(),
muteState.getMutedUntil())));
}
}
private void handleColorSelection(@ColorInt int currentColor) {
@ColorInt int[] colors = MaterialColors.CONVERSATION_PALETTE.asConversationColorArray(requireContext());
ColorPickerDialog.Params params = new ColorPickerDialog.Params.Builder(requireContext())
.setSelectedColor(currentColor)
.setColors(colors)
.setSize(ColorPickerDialog.SIZE_SMALL)
.setSortColors(false)
.setColumns(3)
.build();
ColorPickerDialog dialog = new ColorPickerDialog(requireActivity(), color -> viewModel.onSelectColor(color), params);
dialog.setTitle(R.string.ManageRecipientActivity_chat_color);
dialog.show();
}
public boolean onMenuItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == R.id.action_edit) {
startActivity(EditProfileActivity.getIntentForUserProfileEdit(requireActivity()));
return true;
}
return false;
}
private void setMediaCursorFactory(@Nullable ManageRecipientViewModel.CursorFactory cursorFactory) {
if (this.cursorFactory != cursorFactory) {
this.cursorFactory = cursorFactory;
applyMediaCursorFactory();
}
}
private void applyMediaCursorFactory() {
Context context = getContext();
if (context == null) return;
if (cursorFactory != null) {
Cursor cursor = cursorFactory.create();
getViewLifecycleOwner().getLifecycle().addObserver(new LifecycleCursorWrapper(cursor));
threadPhotoRailView.setCursor(GlideApp.with(context), cursor);
mediaCard.setVisibility(cursor.getCount() > 0 ? View.VISIBLE : View.GONE);
} else {
threadPhotoRailView.setCursor(GlideApp.with(context), null);
mediaCard.setVisibility(View.GONE);
}
}
}

View File

@ -0,0 +1,108 @@
package org.thoughtcrime.securesms.recipients.ui.managerecipient;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import androidx.core.util.Consumer;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.color.MaterialColors;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import java.util.ArrayList;
import java.util.List;
final class ManageRecipientRepository {
private final Context context;
private final RecipientId recipientId;
ManageRecipientRepository(@NonNull Context context, @NonNull RecipientId recipientId) {
this.context = context;
this.recipientId = recipientId;
}
public RecipientId getRecipientId() {
return recipientId;
}
void getThreadId(@NonNull Consumer<Long> onGetThreadId) {
SignalExecutors.BOUNDED.execute(() -> onGetThreadId.accept(getThreadId()));
}
@WorkerThread
private long getThreadId() {
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
Recipient groupRecipient = Recipient.resolved(recipientId);
return threadDatabase.getThreadIdFor(groupRecipient);
}
void getIdentity(@NonNull Consumer<IdentityDatabase.IdentityRecord> callback) {
SignalExecutors.BOUNDED.execute(() -> callback.accept(DatabaseFactory.getIdentityDatabase(context)
.getIdentity(recipientId)
.orNull()));
}
void setExpiration(int newExpirationTime) {
SignalExecutors.BOUNDED.execute(() -> {
DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipientId, newExpirationTime);
OutgoingExpirationUpdateMessage outgoingMessage = new OutgoingExpirationUpdateMessage(Recipient.resolved(recipientId), System.currentTimeMillis(), newExpirationTime * 1000L);
MessageSender.send(context, outgoingMessage, getThreadId(), false, null);
});
}
void getGroupMembership(@NonNull Consumer<List<RecipientId>> onComplete) {
SignalExecutors.BOUNDED.execute(() -> {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
List<GroupDatabase.GroupRecord> groupRecords = groupDatabase.getPushGroupsContainingMember(recipientId);
ArrayList<RecipientId> groupRecipients = new ArrayList<>(groupRecords.size());
for (GroupDatabase.GroupRecord groupRecord : groupRecords) {
groupRecipients.add(groupRecord.getRecipientId());
}
onComplete.accept(groupRecipients);
});
}
public void getRecipient(@NonNull Consumer<Recipient> recipientCallback) {
SignalExecutors.BOUNDED.execute(() -> recipientCallback.accept(Recipient.resolved(recipientId)));
}
void setMuteUntil(long until) {
SignalExecutors.BOUNDED.execute(() -> DatabaseFactory.getRecipientDatabase(context).setMuted(recipientId, until));
}
void setColor(int color) {
SignalExecutors.BOUNDED.execute(() -> {
MaterialColor selectedColor = MaterialColors.CONVERSATION_PALETTE.getByColor(context, color);
if (selectedColor != null) {
DatabaseFactory.getRecipientDatabase(context).setColor(recipientId, selectedColor);
}
});
}
@WorkerThread
@NonNull List<Recipient> getSharedGroups(@NonNull RecipientId recipientId) {
return Stream.of(DatabaseFactory.getGroupDatabase(context)
.getPushGroupsContainingMember(recipientId))
.filter(g -> g.getMembers().contains(Recipient.self().getId()))
.map(GroupDatabase.GroupRecord::getRecipientId)
.map(Recipient::resolved)
.sortBy(gr -> gr.getDisplayName(context))
.toList();
}
}

View File

@ -0,0 +1,283 @@
package org.thoughtcrime.securesms.recipients.ui.managerecipient;
import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import androidx.core.util.Consumer;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Transformations;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.BlockUnblockDialog;
import org.thoughtcrime.securesms.ExpirationDialog;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.VerifyIdentityActivity;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.MediaDatabase;
import org.thoughtcrime.securesms.database.loaders.MediaLoader;
import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry;
import org.thoughtcrime.securesms.groups.ui.addtogroup.AddToGroupsActivity;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.CommunicationActions;
import org.thoughtcrime.securesms.util.DefaultValueLiveData;
import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
import java.util.List;
public final class ManageRecipientViewModel extends ViewModel {
private static final int MAX_COLLAPSED_GROUPS = 5;
private final Context context;
private final ManageRecipientRepository manageRecipientRepository;
private final LiveData<String> name;
private final LiveData<String> disappearingMessageTimer;
private final MutableLiveData<IdentityDatabase.IdentityRecord> identity;
private final LiveData<Recipient> recipient;
private final MutableLiveData<MediaCursor> mediaCursor = new MutableLiveData<>(null);
private final LiveData<MuteState> muteState;
private final LiveData<Boolean> hasCustomNotifications;
private final LiveData<Boolean> canCollapseMemberList;
private final DefaultValueLiveData<CollapseState> groupListCollapseState = new DefaultValueLiveData<>(CollapseState.COLLAPSED);
private final LiveData<Boolean> canBlock;
private final LiveData<List<GroupMemberEntry.FullMember>> visibleSharedGroups;
private final LiveData<String> sharedGroupsCountSummary;
private ManageRecipientViewModel(@NonNull Context context, @NonNull ManageRecipientRepository manageRecipientRepository) {
this.context = context;
this.manageRecipientRepository = manageRecipientRepository;
manageRecipientRepository.getThreadId(this::onThreadIdLoaded);
this.recipient = Recipient.live(manageRecipientRepository.getRecipientId()).getLiveData();
this.name = Transformations.map(recipient, r -> r.getDisplayName(context));
this.identity = new MutableLiveData<>();
LiveData<List<Recipient>> allSharedGroups = LiveDataUtil.mapAsync(this.recipient, r -> manageRecipientRepository.getSharedGroups(r.getId()));
this.sharedGroupsCountSummary = Transformations.map(allSharedGroups, list -> {
int size = list.size();
return size == 0 ? context.getString(R.string.ManageRecipientActivity_no_groups_in_common)
: context.getResources().getQuantityString(R.plurals.ManageRecipientActivity_d_groups_in_common, size, size);
});
this.canCollapseMemberList = LiveDataUtil.combineLatest(this.groupListCollapseState,
Transformations.map(allSharedGroups, m -> m.size() > MAX_COLLAPSED_GROUPS),
(state, hasEnoughMembers) -> state != CollapseState.OPEN && hasEnoughMembers);
this.visibleSharedGroups = Transformations.map(LiveDataUtil.combineLatest(allSharedGroups,
this.groupListCollapseState,
ManageRecipientViewModel::filterSharedGroupList),
recipients -> Stream.of(recipients).map(r -> new GroupMemberEntry.FullMember(r, false)).toList());
this.disappearingMessageTimer = Transformations.map(this.recipient, r -> ExpirationUtil.getExpirationDisplayValue(context, r.getExpireMessages()));
this.muteState = Transformations.map(this.recipient, r -> new MuteState(r.getMuteUntil(), r.isMuted()));
this.hasCustomNotifications = Transformations.map(this.recipient, r -> r.getNotificationChannel() != null || !NotificationChannels.supported());
this.canBlock = Transformations.map(this.recipient, r -> !r.isBlocked());
boolean isSelf = manageRecipientRepository.getRecipientId().equals(Recipient.self().getId());
if (!isSelf) {
manageRecipientRepository.getIdentity(identity::postValue);
}
}
@WorkerThread
private void onThreadIdLoaded(long threadId) {
mediaCursor.postValue(new MediaCursor(threadId,
() -> new ThreadMediaLoader(context, threadId, MediaLoader.MediaType.GALLERY, MediaDatabase.Sorting.Newest).getCursor()));
}
public LiveData<String> getName() {
return name;
}
public LiveData<Recipient> getRecipient() {
return recipient;
}
LiveData<MediaCursor> getMediaCursor() {
return mediaCursor;
}
LiveData<MuteState> getMuteState() {
return muteState;
}
LiveData<String> getDisappearingMessageTimer() {
return disappearingMessageTimer;
}
LiveData<Boolean> hasCustomNotifications() {
return hasCustomNotifications;
}
LiveData<Boolean> getCanCollapseMemberList() {
return canCollapseMemberList;
}
LiveData<Boolean> getCanBlock() {
return canBlock;
}
void handleExpirationSelection(@NonNull Context context) {
withRecipient(recipient ->
ExpirationDialog.show(context,
recipient.getExpireMessages(),
manageRecipientRepository::setExpiration));
}
void setMuteUntil(long muteUntil) {
manageRecipientRepository.setMuteUntil(muteUntil);
}
void clearMuteUntil() {
manageRecipientRepository.setMuteUntil(0);
}
void revealCollapsedMembers() {
groupListCollapseState.setValue(CollapseState.OPEN);
}
void onAddToGroupButton(@NonNull Activity activity) {
manageRecipientRepository.getGroupMembership(existingGroups -> Util.runOnMain(() -> activity.startActivity(AddToGroupsActivity.newIntent(activity, manageRecipientRepository.getRecipientId(), existingGroups))));
}
private void withRecipient(@NonNull Consumer<Recipient> mainThreadRecipientCallback) {
manageRecipientRepository.getRecipient(recipient -> Util.runOnMain(() -> mainThreadRecipientCallback.accept(recipient)));
}
private static @NonNull List<Recipient> filterSharedGroupList(@NonNull List<Recipient> groups,
@NonNull CollapseState collapseState)
{
if (collapseState == CollapseState.COLLAPSED && groups.size() > MAX_COLLAPSED_GROUPS) {
return groups.subList(0, MAX_COLLAPSED_GROUPS);
} else {
return groups;
}
}
LiveData<IdentityDatabase.IdentityRecord> getIdentity() {
return identity;
}
void onBlockClicked(@NonNull FragmentActivity activity) {
withRecipient(recipient -> BlockUnblockDialog.showBlockFor(activity, activity.getLifecycle(), recipient, () -> RecipientUtil.block(context, recipient)));
}
void onUnblockClicked(@NonNull FragmentActivity activity) {
withRecipient(recipient -> BlockUnblockDialog.showUnblockFor(activity, activity.getLifecycle(), recipient, () -> RecipientUtil.unblock(context, recipient)));
}
void onViewSafetyNumberClicked(@NonNull Activity activity, @NonNull IdentityDatabase.IdentityRecord identityRecord) {
activity.startActivity(VerifyIdentityActivity.newIntent(activity, identityRecord));
}
LiveData<List<GroupMemberEntry.FullMember>> getVisibleSharedGroups() {
return visibleSharedGroups;
}
LiveData<String> getSharedGroupsCountSummary() {
return sharedGroupsCountSummary;
}
void onSelectColor(int color) {
manageRecipientRepository.setColor(color);
}
void onGroupClicked(@NonNull Activity activity, @NonNull Recipient recipient) {
CommunicationActions.startConversation(activity, recipient, null);
activity.finish();
}
void onMessage(@NonNull FragmentActivity activity) {
withRecipient(r -> {
CommunicationActions.startConversation(activity, r, null);
activity.finish();
});
}
void onSecureCall(@NonNull FragmentActivity activity) {
withRecipient(r -> CommunicationActions.startVoiceCall(activity, r));
}
void onSecureVideoCall(@NonNull FragmentActivity activity) {
withRecipient(r -> CommunicationActions.startVideoCall(activity, r));
}
static final class MediaCursor {
private final long threadId;
@NonNull private final CursorFactory mediaCursorFactory;
private MediaCursor(long threadId,
@NonNull CursorFactory mediaCursorFactory)
{
this.threadId = threadId;
this.mediaCursorFactory = mediaCursorFactory;
}
long getThreadId() {
return threadId;
}
@NonNull CursorFactory getMediaCursorFactory() {
return mediaCursorFactory;
}
}
static final class MuteState {
private final long mutedUntil;
private final boolean isMuted;
MuteState(long mutedUntil, boolean isMuted) {
this.mutedUntil = mutedUntil;
this.isMuted = isMuted;
}
long getMutedUntil() {
return mutedUntil;
}
public boolean isMuted() {
return isMuted;
}
}
private enum CollapseState {
OPEN,
COLLAPSED
}
interface CursorFactory {
Cursor create();
}
public static class Factory implements ViewModelProvider.Factory {
private final Context context;
private final RecipientId recipientId;
public Factory(@NonNull RecipientId recipientId) {
this.context = ApplicationDependencies.getApplication();
this.recipientId = recipientId;
}
@Override
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection unchecked
return (T) new ManageRecipientViewModel(context, new ManageRecipientRepository(context, recipientId));
}
}
}

View File

@ -16,6 +16,7 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SwitchCompat;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.DialogFragment;
@ -25,21 +26,31 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ThemeUtil;
import java.util.Objects;
public class CustomNotificationsDialogFragment extends DialogFragment {
private static final short RINGTONE_PICKER_REQUEST_CODE = 13562;
private static final short MESSAGE_RINGTONE_PICKER_REQUEST_CODE = 13562;
private static final short CALL_RINGTONE_PICKER_REQUEST_CODE = 23621;
private static final String ARG_RECIPIENT_ID = "recipient_id";
private View customNotificationsRow;
private SwitchCompat customNotificationsSwitch;
private View soundRow;
private View soundLabel;
private TextView soundSelector;
private View vibrateRow;
private View vibrateLabel;
private SwitchCompat vibrateSwitch;
private View callHeading;
private View ringtoneRow;
private TextView ringtoneSelector;
private View callVibrateRow;
private TextView callVibrateSelector;
private CustomNotificationsViewModel viewModel;
@ -79,10 +90,13 @@ public class CustomNotificationsDialogFragment extends DialogFragment {
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (requestCode == RINGTONE_PICKER_REQUEST_CODE && resultCode == Activity.RESULT_OK && data != null) {
if (resultCode == Activity.RESULT_OK && data != null) {
Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
viewModel.setMessageSound(uri);
if (requestCode == MESSAGE_RINGTONE_PICKER_REQUEST_CODE) {
viewModel.setMessageSound(uri);
} else if (requestCode == CALL_RINGTONE_PICKER_REQUEST_CODE) {
viewModel.setCallSound(uri);
}
}
}
@ -96,11 +110,19 @@ public class CustomNotificationsDialogFragment extends DialogFragment {
}
private void initializeViews(@NonNull View view) {
customNotificationsRow = view.findViewById(R.id.custom_notifications_row);
customNotificationsSwitch = view.findViewById(R.id.custom_notifications_enable_switch);
soundRow = view.findViewById(R.id.custom_notifications_sound_row);
soundLabel = view.findViewById(R.id.custom_notifications_sound_label);
soundSelector = view.findViewById(R.id.custom_notifications_sound_selection);
vibrateRow = view.findViewById(R.id.custom_notifications_vibrate_row);
vibrateLabel = view.findViewById(R.id.custom_notifications_vibrate_label);
vibrateSwitch = view.findViewById(R.id.custom_notifications_vibrate_switch);
callHeading = view.findViewById(R.id.custom_notifications_call_settings_section_header);
ringtoneRow = view.findViewById(R.id.custom_notifications_ringtone_row);
ringtoneSelector = view.findViewById(R.id.custom_notifications_ringtone_selection);
callVibrateRow = view.findViewById(R.id.custom_notifications_call_vibrate_row);
callVibrateSelector = view.findViewById(R.id.custom_notifications_call_vibrate_selection);
Toolbar toolbar = view.findViewById(R.id.custom_notifications_toolbar);
@ -119,8 +141,11 @@ public class CustomNotificationsDialogFragment extends DialogFragment {
}
customNotificationsSwitch.setOnCheckedChangeListener(onCustomNotificationsSwitchCheckChangedListener);
customNotificationsRow.setOnClickListener(v -> customNotificationsSwitch.toggle());
soundRow.setEnabled(hasCustomNotifications);
soundLabel.setEnabled(hasCustomNotifications);
vibrateRow.setEnabled(hasCustomNotifications);
vibrateLabel.setEnabled(hasCustomNotifications);
soundSelector.setVisibility(hasCustomNotifications ? View.VISIBLE : View.GONE);
vibrateSwitch.setVisibility(hasCustomNotifications ? View.VISIBLE : View.GONE);
@ -145,18 +170,42 @@ public class CustomNotificationsDialogFragment extends DialogFragment {
vibrateSwitch.setOnCheckedChangeListener(onVibrateSwitchCheckChangedListener);
});
vibrateRow.setOnClickListener(v -> vibrateSwitch.toggle());
viewModel.getNotificationSound().observe(getViewLifecycleOwner(), sound -> {
soundSelector.setText(getRingtoneSummary(requireContext(), sound));
soundSelector.setTag(sound);
soundRow.setOnClickListener(v -> launchSoundSelector(sound, false));
});
soundSelector.setOnClickListener(v -> launchSoundSelector(viewModel.getNotificationSound().getValue()));
viewModel.getShowCallingOptions().observe(getViewLifecycleOwner(), showCalling -> {
callHeading.setVisibility(showCalling ? View.VISIBLE : View.GONE);
ringtoneRow.setVisibility(showCalling ? View.VISIBLE : View.GONE);
callVibrateRow.setVisibility(showCalling ? View.VISIBLE : View.GONE);
});
viewModel.getRingtone().observe(getViewLifecycleOwner(), sound -> {
ringtoneSelector.setText(getRingtoneSummary(requireContext(), sound));
ringtoneSelector.setTag(sound);
ringtoneRow.setOnClickListener(v -> launchSoundSelector(sound, true));
});
viewModel.getCallingVibrateState().observe(getViewLifecycleOwner(), vibrateState -> {
String vibrateSummary = getVibrateSummary(requireContext(), vibrateState);
callVibrateSelector.setText(vibrateSummary);
callVibrateRow.setOnClickListener(v -> new AlertDialog.Builder(requireContext())
.setTitle(R.string.CustomNotificationsDialogFragment__vibrate)
.setSingleChoiceItems(R.array.recipient_vibrate_entries, vibrateState.ordinal(), ((dialog, which) -> {
viewModel.setCallingVibrate(RecipientDatabase.VibrateState.fromId(which));
dialog.dismiss();
})).setNegativeButton(android.R.string.cancel, null)
.show());
});
}
private @NonNull String getRingtoneSummary(@NonNull Context context, @Nullable Uri ringtone) {
if (ringtone == null) {
return context.getString(R.string.preferences__default);
return context.getString(R.string.CustomNotificationsDialogFragment__default);
} else if (ringtone.toString().isEmpty()) {
return context.getString(R.string.preferences__silent);
} else {
@ -167,18 +216,38 @@ public class CustomNotificationsDialogFragment extends DialogFragment {
}
}
return context.getString(R.string.preferences__default);
return context.getString(R.string.CustomNotificationsDialogFragment__default);
}
private void launchSoundSelector(@Nullable Uri current) {
private void launchSoundSelector(@Nullable Uri current, boolean calls) {
Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION);
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, Settings.System.DEFAULT_NOTIFICATION_URI);
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, current);
if (current == null) current = calls ? Settings.System.DEFAULT_RINGTONE_URI : Settings.System.DEFAULT_NOTIFICATION_URI;
else if (current.toString().isEmpty()) current = null;
startActivityForResult(intent, RINGTONE_PICKER_REQUEST_CODE);
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, calls ? RingtoneManager.TYPE_RINGTONE : RingtoneManager.TYPE_NOTIFICATION);
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, current);
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, defaultSound(calls));
startActivityForResult(intent, calls ? CALL_RINGTONE_PICKER_REQUEST_CODE : MESSAGE_RINGTONE_PICKER_REQUEST_CODE);
}
private Uri defaultSound(boolean calls) {
Uri defaultValue;
if (calls) defaultValue = TextSecurePreferences.getCallNotificationRingtone(requireContext());
else defaultValue = TextSecurePreferences.getNotificationRingtone(requireContext());
return defaultValue;
}
private static @NonNull String getVibrateSummary(@NonNull Context context, @NonNull RecipientDatabase.VibrateState vibrateState) {
switch (vibrateState) {
case DEFAULT : return context.getString(R.string.CustomNotificationsDialogFragment__default);
case ENABLED : return context.getString(R.string.CustomNotificationsDialogFragment__enabled);
case DISABLED : return context.getString(R.string.CustomNotificationsDialogFragment__disabled);
default : throw new AssertionError();
}
}
}

View File

@ -61,6 +61,10 @@ class CustomNotificationsRepository {
});
}
void setCallingVibrate(final RecipientDatabase.VibrateState vibrateState) {
SignalExecutors.SERIAL.execute(() -> DatabaseFactory.getRecipientDatabase(context).setCallVibrate(recipientId, vibrateState));
}
void setMessageSound(@Nullable Uri sound) {
SignalExecutors.SERIAL.execute(() -> {
Recipient recipient = getRecipient();
@ -76,6 +80,19 @@ class CustomNotificationsRepository {
});
}
void setCallSound(@Nullable Uri sound) {
SignalExecutors.SERIAL.execute(() -> {
Uri defaultValue = TextSecurePreferences.getCallNotificationRingtone(context);
Uri newValue;
if (defaultValue.equals(sound)) newValue = null;
else if (sound == null) newValue = Uri.EMPTY;
else newValue = sound;
DatabaseFactory.getRecipientDatabase(context).setCallRingtone(recipientId, newValue);
});
}
@WorkerThread
private void createCustomNotificationChannel() {
Recipient recipient = getRecipient();

View File

@ -22,14 +22,20 @@ public final class CustomNotificationsViewModel extends ViewModel {
private final LiveData<Uri> notificationSound;
private final CustomNotificationsRepository repository;
private final MutableLiveData<Boolean> isInitialLoadComplete = new MutableLiveData<>();
private final LiveData<Boolean> showCallingOptions;
private final LiveData<Uri> ringtone;
private final LiveData<RecipientDatabase.VibrateState> isCallingVibrateEnabled;
private CustomNotificationsViewModel(@NonNull RecipientId recipientId, @NonNull CustomNotificationsRepository repository) {
LiveData<Recipient> recipient = Recipient.live(recipientId).getLiveData();
this.repository = repository;
this.hasCustomNotifications = Transformations.map(recipient, r -> r.getNotificationChannel() != null || !NotificationChannels.supported());
this.isVibrateEnabled = Transformations.map(recipient, Recipient::getMessageVibrate);
this.notificationSound = Transformations.map(recipient, Recipient::getMessageRingtone);
this.repository = repository;
this.hasCustomNotifications = Transformations.map(recipient, r -> r.getNotificationChannel() != null || !NotificationChannels.supported());
this.isVibrateEnabled = Transformations.map(recipient, Recipient::getMessageVibrate);
this.notificationSound = Transformations.map(recipient, Recipient::getMessageRingtone);
this.showCallingOptions = Transformations.map(recipient, r -> !r.isGroup() && r.isRegistered());
this.ringtone = Transformations.map(recipient, Recipient::getCallRingtone);
this.isCallingVibrateEnabled = Transformations.map(recipient, Recipient::getCallVibrate);
repository.onLoad(() -> isInitialLoadComplete.postValue(true));
}
@ -62,6 +68,26 @@ public final class CustomNotificationsViewModel extends ViewModel {
repository.setMessageSound(sound);
}
public void setCallSound(@Nullable Uri sound) {
repository.setCallSound(sound);
}
public LiveData<Boolean> getShowCallingOptions() {
return showCallingOptions;
}
public LiveData<Uri> getRingtone() {
return ringtone;
}
public LiveData<RecipientDatabase.VibrateState> getCallingVibrateState() {
return isCallingVibrateEnabled;
}
public void setCallingVibrate(@NonNull RecipientDatabase.VibrateState vibrateState) {
repository.setCallingVibrate(vibrateState);
}
public static final class Factory implements ViewModelProvider.Factory {
private final RecipientId recipientId;

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@color/core_ultramarine"
android:pathData="M12,4c4.963,0 9,3.364 9,7.5S16.963,19 12,19a10.536,10.536 0,0 1,-3.19 -0.5l-0.465,-0.148 -0.464,0.155L4.739,19.556l0.394,-2.365 0.118,-0.707 -0.477,-0.536A6.651,6.651 0,0 1,3 11.5C3,7.364 7.037,4 12,4m0,-1.5c-5.8,0 -10.5,4.029 -10.5,9a8.164,8.164 0,0 0,2.153 5.445l-0.639,3.836c-0.073,0.438 0.144,0.719 0.505,0.719a0.9,0.9 0,0 0,0.279 -0.049l4.558,-1.519A12.019,12.019 0,0 0,12 20.5c5.8,0 10.5,-4.029 10.5,-9s-4.773,-9 -10.5,-9Z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@color/core_ultramarine_light"
android:pathData="M12,2.5c-5.8,0 -10.5,4.029 -10.5,9a8.164,8.164 0,0 0,2.153 5.445l-0.639,3.836c-0.09,0.542 0.263,0.844 0.784,0.67l4.558,-1.519A12.019,12.019 0,0 0,12 20.5c5.8,0 10.5,-4.029 10.5,-9S17.727,2.5 12,2.5Z" />
</vector>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@color/core_ultramarine"
android:pathData="M22,6.56a1.06,1.06 0,0 0,-0.42 0.09L18,8.31L18,6.5a3,3 0,0 0,-3 -3L4,3.5a3,3 0,0 0,-3 3v11a3,3 0,0 0,3 3L15,20.5a3,3 0,0 0,3 -3L18,15.69l3.58,1.66a1.06,1.06 0,0 0,0.42 0.09,1 1,0 0,0 1,-1L23,7.56A1,1 0,0 0,22 6.56ZM16.5,17.5A1.5,1.5 0,0 1,15 19L4,19a1.5,1.5 0,0 1,-1.5 -1.5L2.5,6.5A1.5,1.5 0,0 1,4 5L15,5a1.5,1.5 0,0 1,1.5 1.5ZM21.5,8.34L21.5,16l-1,-0.81L18,14L18,10l2.48,-1.15 1,-0.81Z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@color/core_ultramarine_light"
android:pathData="M23,8.563v6.874a1,1 0,0 1,-1.419 0.908L18.5,14.923V9.077l3.081,-1.422A1,1 0,0 1,23 8.563ZM2.535,4.876A2.639,2.639 0,0 0,1.4 5.939,3.854 3.854,0 0,0 1,8.105V15.9a3.854,3.854 0,0 0,0.4 2.166,2.639 2.639,0 0,0 1.134,1.063 4.6,4.6 0,0 0,2.311 0.376h8.308a4.6,4.6 0,0 0,2.311 -0.376A2.639,2.639 0,0 0,16.6 18.061,3.854 3.854,0 0,0 17,15.9V8.105a3.854,3.854 0,0 0,-0.4 -2.166,2.639 2.639,0 0,0 -1.134,-1.063A4.6,4.6 0,0 0,13.154 4.5H4.846A4.6,4.6 0,0 0,2.535 4.876Z"/>
</vector>

View File

@ -25,7 +25,7 @@
app:layout_constraintTop_toBottomOf="@id/toolbar" />
<org.thoughtcrime.securesms.components.emoji.EmojiEditText
android:id="@+id/group_name"
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"

View File

@ -23,73 +23,212 @@
android:layout_marginTop="16dp"
android:text="@string/CustomNotificationsDialogFragment__messages"
android:textAppearance="@style/TextAppearance.Signal.Body2"
android:textColor="@color/ultramarine_text_button"
android:textColor="?attr/colorAccent"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/custom_notifications_toolbar" />
<TextView
android:id="@+id/custom_notifications_enable_label"
style="@style/Widget.Signal.Button.TextButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/CustomNotificationsDialogFragment__use_custom_notifications"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/custom_notifications_message_section_header" />
<TextView
android:id="@+id/custom_notifications_sound_label"
style="@style/Widget.Signal.Button.TextButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:enabled="false"
<LinearLayout
android:id="@+id/custom_notifications_row"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="?selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="horizontal"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:text="@string/CustomNotificationsDialogFragment__notification_sound"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/custom_notifications_enable_label" />
app:layout_constraintTop_toBottomOf="@id/custom_notifications_message_section_header">
<TextView
android:id="@+id/custom_notifications_vibrate_label"
style="@style/Widget.Signal.Button.TextButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:enabled="false"
android:text="@string/CustomNotificationsDialogFragment__vibrate"
<TextView
android:id="@+id/custom_notifications_enable_label"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:gravity="center_vertical|start"
android:text="@string/CustomNotificationsDialogFragment__use_custom_notifications"
android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.Signal.Body2" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/custom_notifications_enable_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="false"
app:layout_constraintBottom_toBottomOf="@id/custom_notifications_enable_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/custom_notifications_enable_label"
app:layout_constraintTop_toTopOf="@id/custom_notifications_enable_label" />
</LinearLayout>
<LinearLayout
android:id="@+id/custom_notifications_sound_row"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="?selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="horizontal"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:text="@string/CustomNotificationsDialogFragment__notification_sound"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/custom_notifications_sound_label" />
app:layout_constraintTop_toBottomOf="@id/custom_notifications_row">
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/custom_notifications_enable_switch"
android:layout_width="0dp"
android:layout_height="0dp"
android:enabled="false"
app:layout_constraintBottom_toBottomOf="@id/custom_notifications_enable_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/custom_notifications_enable_label"
app:layout_constraintTop_toTopOf="@id/custom_notifications_enable_label" />
<TextView
android:id="@+id/custom_notifications_sound_label"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:enabled="false"
android:gravity="center_vertical|start"
android:text="@string/CustomNotificationsDialogFragment__notification_sound"
android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.Signal.Body2" />
<TextView
android:id="@+id/custom_notifications_sound_selection"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="false"
android:textColor="?attr/colorAccent"
android:visibility="gone"
tools:text="Default (Popcorn)"
tools:visibility="visible" />
</LinearLayout>
<LinearLayout
android:id="@+id/custom_notifications_vibrate_row"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="?selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="horizontal"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:text="@string/CustomNotificationsDialogFragment__notification_sound"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/custom_notifications_sound_row">
<TextView
android:id="@+id/custom_notifications_vibrate_label"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_weight="1"
android:enabled="false"
android:gravity="center_vertical|start"
android:text="@string/CustomNotificationsDialogFragment__vibrate"
android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.Signal.Body2" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/custom_notifications_vibrate_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="false" />
</LinearLayout>
<TextView
android:id="@+id/custom_notifications_sound_selection"
style="@style/Widget.Signal.Button.TextButton.Ultramarine"
android:id="@+id/custom_notifications_call_settings_section_header"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="36dp"
android:text="@string/CustomNotificationsDialogFragment__call_settings"
android:textAppearance="@style/TextAppearance.Signal.Body2"
android:textColor="?attr/colorAccent"
android:textStyle="bold"
android:visibility="gone"
android:gravity="end"
app:layout_constraintBottom_toBottomOf="@id/custom_notifications_sound_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/custom_notifications_sound_label"
app:layout_constraintTop_toTopOf="@id/custom_notifications_sound_label"
tools:visibility="visible"
tools:text="Default (Popcorn)" />
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/custom_notifications_vibrate_row"
tools:visibility="visible" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/custom_notifications_vibrate_switch"
android:layout_width="0dp"
android:layout_height="0dp"
<LinearLayout
android:id="@+id/custom_notifications_ringtone_row"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="?selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="horizontal"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:text="@string/CustomNotificationsDialogFragment__notification_sound"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/custom_notifications_vibrate_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/custom_notifications_vibrate_label"
app:layout_constraintTop_toTopOf="@id/custom_notifications_vibrate_label" />
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/custom_notifications_call_settings_section_header"
tools:visibility="visible">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:gravity="center_vertical|start"
android:text="@string/CustomNotificationsDialogFragment__ringtone"
android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.Signal.Body2" />
<TextView
android:id="@+id/custom_notifications_ringtone_selection"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="false"
android:textColor="?attr/colorAccent"
tools:text="Default" />
</LinearLayout>
<LinearLayout
android:id="@+id/custom_notifications_call_vibrate_row"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="?selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="horizontal"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:text="@string/CustomNotificationsDialogFragment__notification_sound"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/custom_notifications_ringtone_row"
tools:visibility="visible">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:gravity="center_vertical|start"
android:text="@string/CustomNotificationsDialogFragment__vibrate"
android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.Signal.Body2" />
<TextView
android:id="@+id/custom_notifications_call_vibrate_selection"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="false"
android:textColor="?attr/colorAccent"
tools:text="Default" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -70,7 +70,7 @@
android:layout_marginEnd="10dp" />
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/group_name_target"
android:id="@+id/name_target"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
@ -93,10 +93,10 @@
android:layout_marginTop="16dp"
android:elevation="8dp"
android:transitionName="avatar"
app:layout_behavior=".groups.ui.creategroup.GroupSettingsCoordinatorLayoutBehavior" />
app:layout_behavior=".recipients.ui.RecipientSettingsCoordinatorLayoutBehavior" />
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/group_name"
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
tools:context=".recipients.ui.managerecipient.ManageRecipientActivity">
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>

View File

@ -0,0 +1,546 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".recipients.ui.managerecipient.ManageRecipientFragment"
tools:theme="@style/TextSecure.LightNoActionBar">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/windowBackground"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
app:contentScrim="?android:attr/windowBackground"
app:expandedTitleGravity="center_horizontal"
app:expandedTitleMarginTop="156dp"
app:expandedTitleTextAppearance="@style/TextAppearance.Signal.Body1.Bold"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:scrimAnimationDuration="200">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="16dp"
android:paddingBottom="16dp">
<Space
android:layout_width="128dp"
android:layout_height="128dp"
android:layout_gravity="center_horizontal" />
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="14dp"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.Signal.Body1.Bold" />
<TextView
android:id="@+id/username_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textAppearance="@style/Signal.Text.Body"
android:textColor="?title_text_color_secondary"
tools:text="\@spidergwen +1 555-654-6657" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="16dp">
<com.google.android.material.button.MaterialButton
android:id="@+id/recipient_message"
style="@style/Widget.Signal.Button.Icon.Circular"
android:contentDescription="@string/ManageRecipientActivity_message_description"
app:backgroundTint="?recipient_contact_button_color"
app:icon="?recipient_message_circle_icon"
app:rippleColor="@color/core_ultramarine" />
<com.google.android.material.button.MaterialButton
android:id="@+id/recipient_voice_call"
style="@style/Widget.Signal.Button.Icon.Circular"
android:layout_marginStart="36dp"
android:contentDescription="@string/ManageRecipientActivity_voice_call_description"
android:visibility="gone"
app:backgroundTint="?recipient_contact_button_color"
app:icon="?recipient_call_circle_icon"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/recipient_video_call"
style="@style/Widget.Signal.Button.Icon.Circular"
android:layout_marginStart="36dp"
android:contentDescription="@string/ManageRecipientActivity_video_call_description"
android:visibility="gone"
app:backgroundTint="?recipient_contact_button_color"
app:icon="?recipient_video_call_circle_icon"
tools:visibility="visible" />
</LinearLayout>
</LinearLayout>
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:contentInsetStartWithNavigation="0dp"
app:layout_collapseMode="pin"
app:navigationIcon="@drawable/ic_arrow_left_24">
<Space
android:id="@+id/avatar_target"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_gravity="center_vertical|start"
android:layout_marginEnd="10dp" />
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/name_target"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:layout_weight="1"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.Signal.Body1.Bold" />
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<org.thoughtcrime.securesms.components.AvatarImageView
android:id="@+id/recipient_avatar"
android:layout_width="128dp"
android:layout_height="128dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="16dp"
android:elevation="8dp"
android:transitionName="avatar"
app:layout_behavior=".recipients.ui.RecipientSettingsCoordinatorLayoutBehavior" />
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="158dp"
android:elevation="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.Signal.Body1.Bold"
tools:text="Rob Aneesh" />
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?pref_divider"
android:fillViewport="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:id="@+id/recipient_disappearing_messages_card"
style="@style/Widget.Signal.CardView.PreferenceRow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/group_manage_fragment_card_vertical_padding"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@+id/disappearing_messages_row"
android:layout_width="match_parent"
android:layout_height="@dimen/group_manage_fragment_row_height"
android:background="?selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:gravity="center_vertical|start"
android:paddingStart="@dimen/group_manage_fragment_row_horizontal_padding"
android:paddingEnd="@dimen/group_manage_fragment_row_horizontal_padding"
android:text="@string/ManageRecipientActivity_disappearing_messages"
android:textAlignment="viewStart"
android:textAppearance="@style/Signal.Text.Body" />
<TextView
android:id="@+id/disappearing_messages"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:minWidth="48dp"
android:paddingStart="@dimen/group_manage_fragment_row_horizontal_padding"
android:paddingEnd="@dimen/group_manage_fragment_row_horizontal_padding"
android:textAppearance="@style/Signal.Text.Body"
android:textColor="@color/ultramarine_text_button"
tools:text="Off" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/recipient_notifications_card"
style="@style/Widget.Signal.CardView.PreferenceRow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/group_manage_fragment_card_vertical_padding"
app:layout_constraintTop_toBottomOf="@id/recipient_disappearing_messages_card"
app:layout_goneMarginTop="0dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/recipient_mute_notifications_row"
android:layout_width="match_parent"
android:layout_height="@dimen/group_manage_fragment_row_height"
android:background="?selectableItemBackground"
android:clickable="true"
android:focusable="true">
<TextView
android:id="@+id/recipient_mute_notifications"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/group_manage_fragment_row_horizontal_padding"
android:gravity="center_vertical|start"
android:text="@string/ManageRecipientActivity_mute_notifications"
android:textAlignment="viewStart"
android:textAppearance="@style/Signal.Text.Body"
app:layout_constraintBottom_toTopOf="@id/recipient_mute_notifications_until"
app:layout_constraintEnd_toStartOf="@id/recipient_mute_notifications_switch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/recipient_mute_notifications_until"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/group_manage_fragment_row_horizontal_padding"
android:textAppearance="@style/TextSecure.SubtitleTextStyle"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/recipient_mute_notifications"
tools:text="Until 12:42 PM"
tools:visibility="visible" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/recipient_mute_notifications_switch"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:clickable="false"
android:enabled="false"
android:minWidth="48dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/recipient_mute_notifications"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:id="@+id/recipient_custom_notifications_row"
android:layout_width="match_parent"
android:layout_height="@dimen/group_manage_fragment_row_height"
android:background="?selectableItemBackground"
android:clickable="true"
android:focusable="true">
<TextView
android:id="@+id/recipient_custom_notifications"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="start|center_vertical"
android:paddingStart="@dimen/group_manage_fragment_row_horizontal_padding"
android:paddingEnd="@dimen/group_manage_fragment_row_horizontal_padding"
android:text="@string/ManageRecipientActivity_custom_notifications"
android:textAlignment="viewStart"
android:textAppearance="@style/Signal.Text.Body"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/recipient_mute_notifications_switch"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/recipient_custom_notifications_button"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical|end"
android:paddingStart="@dimen/group_manage_fragment_row_horizontal_padding"
android:paddingEnd="@dimen/group_manage_fragment_row_horizontal_padding"
android:textAppearance="@style/Signal.Text.Body"
android:textColor="@color/ultramarine_text_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/recipient_custom_notifications"
app:layout_constraintTop_toBottomOf="@id/recipient_mute_notifications_switch"
tools:text="Off" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/recipient_media_card"
style="@style/Widget.Signal.CardView.PreferenceRow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/group_manage_fragment_card_vertical_padding"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/recipient_notifications_card"
tools:visibility="visible">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/shared_media_row"
android:layout_width="match_parent"
android:layout_height="@dimen/group_manage_fragment_row_height"
android:background="?selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:gravity="center_vertical|start"
android:paddingStart="@dimen/group_manage_fragment_row_horizontal_padding"
android:paddingEnd="@dimen/group_manage_fragment_row_horizontal_padding"
android:text="@string/recipient_preference_activity__shared_media"
android:textAlignment="viewStart"
android:textAppearance="@style/Signal.Text.Body" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:minWidth="48dp"
android:paddingStart="@dimen/group_manage_fragment_row_horizontal_padding"
android:paddingEnd="@dimen/group_manage_fragment_row_horizontal_padding"
android:text="@string/ManageRecipientActivity_see_all"
android:textAppearance="@style/Signal.Text.Body"
android:textColor="@color/ultramarine_text_button" />
</LinearLayout>
<org.thoughtcrime.securesms.components.ThreadPhotoRailView
android:id="@+id/recent_photos"
android:layout_width="match_parent"
android:layout_height="90dp"
android:layout_marginBottom="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/shared_media_row" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/recipient_chat_color"
style="@style/Widget.Signal.CardView.PreferenceRow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/group_manage_fragment_card_vertical_padding"
app:layout_constraintTop_toBottomOf="@id/recipient_media_card">
<LinearLayout
android:id="@+id/color_row"
android:layout_width="match_parent"
android:layout_height="@dimen/group_manage_fragment_row_height"
android:background="?selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:gravity="center_vertical|start"
android:paddingStart="@dimen/group_manage_fragment_row_horizontal_padding"
android:paddingEnd="@dimen/group_manage_fragment_row_horizontal_padding"
android:text="@string/ManageRecipientActivity_chat_color"
android:textAlignment="viewStart"
android:textAppearance="@style/Signal.Text.Body" />
<ImageView
android:id="@+id/color_chip"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/group_manage_fragment_row_horizontal_padding"
android:contentDescription="@string/ManageRecipientActivity_chat_color"
android:gravity="center"
tools:src="@drawable/circle_tintable"
tools:tint="@color/core_ultramarine_light" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/recipient_membership_card"
style="@style/Widget.Signal.CardView.PreferenceRow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/group_manage_fragment_card_vertical_padding"
app:layout_constraintTop_toBottomOf="@id/recipient_chat_color">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/groups_in_common_count"
style="@style/TextAppearance.Signal.Subtitle2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="12dp"
android:layout_marginBottom="4dp"
android:textColor="?title_text_color_secondary"
tools:text="8 groups in common" />
<TextView
android:id="@+id/add_to_a_group"
android:layout_width="match_parent"
android:layout_height="64dp"
android:background="?selectableItemBackground"
android:drawableStart="?attr/manage_group_add_members_icon"
android:drawablePadding="8dp"
android:gravity="center_vertical|start"
android:paddingStart="@dimen/group_manage_fragment_row_horizontal_padding"
android:paddingEnd="@dimen/group_manage_fragment_row_horizontal_padding"
android:text="@string/ManageRecipientActivity_add_to_a_group"
android:textAlignment="viewStart"
android:textAppearance="@style/Signal.Text.Body"
android:textColor="@color/ultramarine_text_button" />
<org.thoughtcrime.securesms.groups.ui.GroupMemberListView
android:id="@+id/shared_group_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:listitem="@layout/group_recipient_list_item" />
<TextView
android:id="@+id/toggle_all_groups"
android:layout_width="match_parent"
android:layout_height="64dp"
android:background="?selectableItemBackground"
android:drawableStart="?attr/manage_group_view_all_icon"
android:drawablePadding="8dp"
android:gravity="center_vertical|start"
android:paddingStart="@dimen/group_manage_fragment_row_horizontal_padding"
android:paddingEnd="@dimen/group_manage_fragment_row_horizontal_padding"
android:text="@string/ManageRecipientActivity_view_all_groups"
android:textAlignment="viewStart"
android:textAppearance="@style/Signal.Text.Body"
android:textColor="?title_text_color_secondary"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/recipient_block_and_leave_card"
style="@style/Widget.Signal.CardView.PreferenceRow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/group_manage_fragment_card_vertical_padding"
app:layout_constraintTop_toBottomOf="@id/recipient_membership_card">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/view_safety_number"
android:layout_width="match_parent"
android:layout_height="@dimen/group_manage_fragment_row_height"
android:background="?selectableItemBackground"
android:gravity="center_vertical|start"
android:paddingStart="@dimen/group_manage_fragment_row_horizontal_padding"
android:paddingEnd="@dimen/group_manage_fragment_row_horizontal_padding"
android:text="@string/ManageRecipientActivity_view_safety_number"
android:textAlignment="viewStart"
android:textAppearance="@style/Signal.Text.Body"
android:visibility="gone"
tools:visibility="visible" />
<TextView
android:id="@+id/block"
android:layout_width="match_parent"
android:layout_height="@dimen/group_manage_fragment_row_height"
android:background="?selectableItemBackground"
android:gravity="center_vertical|start"
android:paddingStart="@dimen/group_manage_fragment_row_horizontal_padding"
android:paddingEnd="@dimen/group_manage_fragment_row_horizontal_padding"
android:text="@string/ManageRecipientActivity_block"
android:textAlignment="viewStart"
android:textAppearance="@style/Signal.Text.Body"
android:textColor="@color/core_red" />
<TextView
android:id="@+id/unblock"
android:layout_width="match_parent"
android:layout_height="@dimen/group_manage_fragment_row_height"
android:background="?selectableItemBackground"
android:gravity="center_vertical|start"
android:paddingStart="@dimen/group_manage_fragment_row_horizontal_padding"
android:paddingEnd="@dimen/group_manage_fragment_row_horizontal_padding"
android:text="@string/ManageRecipientActivity_unblock"
android:textAlignment="viewStart"
android:textAppearance="@style/Signal.Text.Body"
android:textColor="@color/core_red"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_edit"
android:icon="?attr/menu_edit_icon"
android:title="@string/ManageGroupActivity_edit_name_and_picture"
android:visible="false"
app:showAsAction="always" />
</menu>

View File

@ -301,6 +301,11 @@
<attr name="recipient_make_admin_icon" format="reference"/>
<attr name="recipient_remove_icon" format="reference"/>
<attr name="recipient_message_circle_icon" format="reference"/>
<attr name="recipient_call_circle_icon" format="reference"/>
<attr name="recipient_video_call_circle_icon" format="reference"/>
<attr name="recipient_contact_button_color" format="reference"/>
<declare-styleable name="ColorPreference">
<attr name="itemLayout" format="reference" />
<attr name="choices" format="reference" />

View File

@ -555,6 +555,31 @@
<string name="GroupManagement_choose_who_can_add_or_invite_new_members">Choose who can add or invite new members</string>
<string name="GroupManagement_choose_who_can_change_the_group_name_and_photo">Choose who can change the group name and photo</string>
<!-- ManageRecipientActivity -->
<string name="ManageRecipientActivity_disappearing_messages">Disappearing messages</string>
<string name="ManageRecipientActivity_chat_color">Chat color</string>
<string name="ManageRecipientActivity_block">Block</string>
<string name="ManageRecipientActivity_unblock">Unblock</string>
<string name="ManageRecipientActivity_view_safety_number">View safety number</string>
<string name="ManageRecipientActivity_mute_notifications">Mute notifications</string>
<string name="ManageRecipientActivity_custom_notifications">Custom notifications</string>
<string name="ManageRecipientActivity_until_s">Until %1$s</string>
<string name="ManageRecipientActivity_off">Off</string>
<string name="ManageRecipientActivity_on">On</string>
<string name="ManageRecipientActivity_add_to_a_group">Add to a group</string>
<string name="ManageRecipientActivity_view_all_groups">View all groups</string>
<string name="ManageRecipientActivity_see_all">See all</string>
<string name="ManageRecipientActivity_no_groups_in_common">No groups in common</string>
<plurals name="ManageRecipientActivity_d_groups_in_common">
<item quantity="one">%d group in common</item>
<item quantity="other">%d groups in common</item>
</plurals>
<string name="ManageRecipientActivity_edit_name_and_picture">Edit name and picture</string>
<string name="ManageRecipientActivity_message_description">Message</string>
<string name="ManageRecipientActivity_voice_call_description">Voice call</string>
<string name="ManageRecipientActivity_video_call_description">Video call</string>
<plurals name="GroupMemberList_invited">
<item quantity="one">%1$s invited 1 person</item>
@ -567,6 +592,11 @@
<string name="CustomNotificationsDialogFragment__use_custom_notifications">Use custom notifications</string>
<string name="CustomNotificationsDialogFragment__notification_sound">Notification sound</string>
<string name="CustomNotificationsDialogFragment__vibrate">Vibrate</string>
<string name="CustomNotificationsDialogFragment__call_settings">Call settings</string>
<string name="CustomNotificationsDialogFragment__ringtone">Ringtone</string>
<string name="CustomNotificationsDialogFragment__enabled">Enabled</string>
<string name="CustomNotificationsDialogFragment__disabled">Disabled</string>
<string name="CustomNotificationsDialogFragment__default">Default</string>
<!-- GV2 Invite cancellation confirmation -->
<string name="GroupManagement_cancel_own_single_invite">Do you want to cancel the invite you sent to %1$s?</string>

View File

@ -448,4 +448,26 @@
<item name="cardElevation">0.5dp</item>
<item name="cardBackgroundColor">?android:attr/windowBackground</item>
</style>
<style name="Widget.Signal.Button.Icon.Circular" parent="Widget.MaterialComponents.Button.Icon">
<item name="android:layout_width">48dp</item>
<item name="android:layout_height">48dp</item>
<item name="iconSize">24dp</item>
<item name="android:insetRight">0dp</item>
<item name="android:insetLeft">0dp</item>
<item name="android:insetTop">0dp</item>
<item name="android:insetBottom">0dp</item>
<item name="android:elevation" tools:ignore="NewApi">0dp</item>
<item name="android:stateListAnimator" tools:ignore="NewApi">@null</item>
<item name="iconTint">@color/white</item>
<item name="iconTintMode">multiply</item>
<item name="iconPadding">0dp</item>
<item name="rippleColor">@color/core_ultramarine</item>
<item name="shapeAppearanceOverlay">@style/ShapeAppearanceOverlay.Signal.Button.Rounded</item>
</style>
<style name="ShapeAppearanceOverlay.Signal.Button.Rounded" parent="">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">50%</item>
</style>
</resources>

View File

@ -437,6 +437,11 @@
<item name="colorControlNormal">@color/core_grey_90</item>
<item name="circular_progress_button_state">@drawable/progress_button_state_light</item>
<item name="recipient_message_circle_icon">@drawable/ic_message_outline_ultramarine_24</item>
<item name="recipient_call_circle_icon">@drawable/ic_phone_right_outline_ultramarine_24</item>
<item name="recipient_video_call_circle_icon">@drawable/ic_video_outline_ultramarine_24</item>
<item name="recipient_contact_button_color">@color/core_grey_02</item>
</style>
<style name="TextSecure.DarkTheme" parent="@style/TextSecure.BaseDarkTheme">
@ -727,6 +732,11 @@
<item name="colorControlNormal">@color/core_white</item>
<item name="circular_progress_button_state">@drawable/progress_button_state_dark</item>
<item name="recipient_message_circle_icon">@drawable/ic_message_solid_ultramarine_light_24</item>
<item name="recipient_call_circle_icon">@drawable/ic_phone_right_solid_ultramarine_light_24</item>
<item name="recipient_video_call_circle_icon">@drawable/ic_video_solid_ultramarine_light_24</item>
<item name="recipient_contact_button_color">@color/core_grey_75</item>
</style>
<style name="Theme.Signal.AlertDialog.Light.Cornered" parent="Theme.AppCompat.Light.Dialog.Alert">