From 17c5b858b5933e71a4fcc8fce6963edeed11fa6b Mon Sep 17 00:00:00 2001 From: Alan Evans Date: Wed, 22 Apr 2020 16:25:28 -0300 Subject: [PATCH] Recipient bottom sheet. --- .../securesms/GroupMembersDialog.java | 37 ++-- .../RecipientBottomSheetDialogFragment.java | 158 ++++++++++++++++++ .../RecipientDialogRepository.java | 82 +++++++++ .../bottomsheet/RecipientDialogViewModel.java | 158 ++++++++++++++++++ .../main/res/drawable/ic_block_tinted_24.xml | 9 + .../drawable/ic_info_outline_tinted_24.xml | 9 + .../res/drawable/ic_info_solid_tinted_24.xml | 9 + .../main/res/drawable/ic_leave_tinted_24.xml | 10 ++ .../drawable/ic_message_outline_tinted_24.xml | 13 +- .../ic_message_outline_tinted_bitmap_24.xml | 4 + .../drawable/ic_message_solid_tinted_24.xml | 13 +- .../ic_message_solid_tinted_bitmap_24.xml | 4 + .../ic_phone_right_outline_tinted_24.xml | 9 + .../ic_phone_right_solid_tinted_24.xml | 9 + app/src/main/res/drawable/rounded_dialog.xml | 11 ++ .../main/res/drawable/rounded_dialog_dark.xml | 11 ++ .../res/layout/recipient_bottom_sheet.xml | 138 +++++++++++++++ app/src/main/res/values/attrs.xml | 7 + app/src/main/res/values/strings.xml | 10 ++ app/src/main/res/values/styles.xml | 12 +- app/src/main/res/values/text_styles.xml | 12 +- app/src/main/res/values/themes.xml | 51 +++++- 22 files changed, 747 insertions(+), 29 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientBottomSheetDialogFragment.java create mode 100644 app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogRepository.java create mode 100644 app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogViewModel.java create mode 100644 app/src/main/res/drawable/ic_block_tinted_24.xml create mode 100644 app/src/main/res/drawable/ic_info_outline_tinted_24.xml create mode 100644 app/src/main/res/drawable/ic_info_solid_tinted_24.xml create mode 100644 app/src/main/res/drawable/ic_leave_tinted_24.xml create mode 100644 app/src/main/res/drawable/ic_message_outline_tinted_bitmap_24.xml create mode 100644 app/src/main/res/drawable/ic_message_solid_tinted_bitmap_24.xml create mode 100644 app/src/main/res/drawable/ic_phone_right_outline_tinted_24.xml create mode 100644 app/src/main/res/drawable/ic_phone_right_solid_tinted_24.xml create mode 100644 app/src/main/res/drawable/rounded_dialog.xml create mode 100644 app/src/main/res/drawable/rounded_dialog_dark.xml create mode 100644 app/src/main/res/layout/recipient_bottom_sheet.xml diff --git a/app/src/main/java/org/thoughtcrime/securesms/GroupMembersDialog.java b/app/src/main/java/org/thoughtcrime/securesms/GroupMembersDialog.java index a7676558d7..c390f31508 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/GroupMembersDialog.java +++ b/app/src/main/java/org/thoughtcrime/securesms/GroupMembersDialog.java @@ -1,43 +1,45 @@ package org.thoughtcrime.securesms; -import android.content.Context; import android.content.Intent; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.Lifecycle; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.GroupDatabase; +import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry; import org.thoughtcrime.securesms.groups.ui.GroupMemberListView; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientExporter; +import org.thoughtcrime.securesms.recipients.ui.bottomsheet.RecipientBottomSheetDialogFragment; import org.thoughtcrime.securesms.util.concurrent.SimpleTask; import java.util.ArrayList; public final class GroupMembersDialog { - private final Context context; - private final Recipient groupRecipient; - private final Lifecycle lifecycle; + private final FragmentActivity fragmentActivity; + private final Recipient groupRecipient; + private final Lifecycle lifecycle; - public GroupMembersDialog(@NonNull Context context, + public GroupMembersDialog(@NonNull FragmentActivity activity, @NonNull Recipient groupRecipient, @NonNull Lifecycle lifecycle) { - this.context = context; - this.groupRecipient = groupRecipient; - this.lifecycle = lifecycle; + this.fragmentActivity = activity; + this.groupRecipient = groupRecipient; + this.lifecycle = lifecycle; } public void display() { SimpleTask.run( lifecycle, - () -> DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupRecipient.requireGroupId(), GroupDatabase.MemberSet.FULL_MEMBERS_INCLUDING_SELF), + () -> DatabaseFactory.getGroupDatabase(fragmentActivity).getGroupMembers(groupRecipient.requireGroupId(), GroupDatabase.MemberSet.FULL_MEMBERS_INCLUDING_SELF), members -> { - AlertDialog dialog = new AlertDialog.Builder(context) + AlertDialog dialog = new AlertDialog.Builder(fragmentActivity) .setTitle(R.string.ConversationActivity_group_members) .setIconAttribute(R.attr.group_members_dialog_icon) .setCancelable(true) @@ -70,13 +72,18 @@ public final class GroupMembersDialog { } private void contactClick(@NonNull Recipient recipient) { - if (recipient.getContactUri() != null) { - Intent intent = new Intent(context, RecipientPreferenceActivity.class); - intent.putExtra(RecipientPreferenceActivity.RECIPIENT_ID, recipient.getId()); + GroupId groupId = groupRecipient.requireGroupId(); - context.startActivity(intent); + if (groupId.isV2()) { + RecipientBottomSheetDialogFragment.create(recipient.getId(), groupId) + .show(fragmentActivity.getSupportFragmentManager(), "BOTTOM"); + } else if (recipient.getContactUri() != null) { + Intent intent = new Intent(fragmentActivity, RecipientPreferenceActivity.class); + intent.putExtra(RecipientPreferenceActivity.RECIPIENT_ID, recipient.getId()); + + fragmentActivity.startActivity(intent); } else { - context.startActivity(RecipientExporter.export(recipient).asAddContactIntent()); + fragmentActivity.startActivity(RecipientExporter.export(recipient).asAddContactIntent()); } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientBottomSheetDialogFragment.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientBottomSheetDialogFragment.java new file mode 100644 index 0000000000..2eba810983 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientBottomSheetDialogFragment.java @@ -0,0 +1,158 @@ +package org.thoughtcrime.securesms.recipients.ui.bottomsheet; + +import android.os.Bundle; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProviders; + +import com.google.android.material.bottomsheet.BottomSheetDialogFragment; + +import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.components.AvatarImageView; +import org.thoughtcrime.securesms.groups.GroupId; +import org.thoughtcrime.securesms.recipients.RecipientId; +import org.thoughtcrime.securesms.util.ThemeUtil; + +import java.util.Objects; + +public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogFragment { + + private static final String ARGS_RECIPIENT_ID = "RECIPIENT_ID"; + private static final String ARGS_GROUP_ID = "GROUP_ID"; + + private RecipientDialogViewModel viewModel; + private AvatarImageView avatar; + private TextView fullName; + private TextView usernameNumber; + private Button messageButton; + private Button secureCallButton; + private Button blockButton; + private Button unblockButton; + private Button viewSafetyNumberButton; + private Button makeGroupAdminButton; + private Button removeAdminButton; + private Button removeFromGroupButton; + + public static BottomSheetDialogFragment create(@NonNull RecipientId recipientId, + @Nullable GroupId groupId) + { + Bundle args = new Bundle(); + RecipientBottomSheetDialogFragment fragment = new RecipientBottomSheetDialogFragment(); + + args.putString(ARGS_RECIPIENT_ID, recipientId.serialize()); + if (groupId != null) { + args.putString(ARGS_GROUP_ID, groupId.toString()); + } + + fragment.setArguments(args); + + return fragment; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + setStyle(DialogFragment.STYLE_NORMAL, + ThemeUtil.isDarkTheme(requireContext()) ? R.style.Signal_RecipientBottomSheet + : R.style.Signal_RecipientBottomSheet_Light); + super.onCreate(savedInstanceState); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.recipient_bottom_sheet, container, false); + + avatar = view.findViewById(R.id.recipient_avatar); + fullName = view.findViewById(R.id.full_name); + usernameNumber = view.findViewById(R.id.username_number); + messageButton = view.findViewById(R.id.message_button); + secureCallButton = view.findViewById(R.id.secure_call_button); + blockButton = view.findViewById(R.id.block_button); + unblockButton = view.findViewById(R.id.unblock_button); + viewSafetyNumberButton = view.findViewById(R.id.view_safety_number_button); + makeGroupAdminButton = view.findViewById(R.id.make_group_admin_button); + removeAdminButton = view.findViewById(R.id.remove_group_admin_button); + removeFromGroupButton = view.findViewById(R.id.remove_from_group_button); + + return view; + } + + @Override + public void onViewCreated(@NonNull View fragmentView, @Nullable Bundle savedInstanceState) { + super.onViewCreated(fragmentView, savedInstanceState); + + Bundle arguments = requireArguments(); + RecipientId recipientId = RecipientId.from(Objects.requireNonNull(arguments.getString(ARGS_RECIPIENT_ID))); + GroupId groupId = GroupId.parseNullableOrThrow(arguments.getString(ARGS_GROUP_ID)); + + RecipientDialogViewModel.Factory factory = new RecipientDialogViewModel.Factory(requireContext().getApplicationContext(), recipientId, groupId); + + viewModel = ViewModelProviders.of(this, factory).get(RecipientDialogViewModel.class); + + viewModel.getRecipient().observe(getViewLifecycleOwner(), recipient -> { + avatar.setRecipient(recipient); + + String name = recipient.getProfileName().toString(); + fullName.setText(name); + fullName.setVisibility(TextUtils.isEmpty(name) ? View.GONE : View.VISIBLE); + + 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); + + boolean blocked = recipient.isBlocked(); + blockButton.setVisibility(blocked ? View.GONE : View.VISIBLE); + unblockButton.setVisibility(blocked ? View.VISIBLE : View.GONE); + + secureCallButton.setVisibility(recipient.isRegistered() ? View.VISIBLE : View.GONE); + }); + + viewModel.getAdminActionStatus().observe(getViewLifecycleOwner(), adminStatus -> { + makeGroupAdminButton.setVisibility(adminStatus.isCanMakeAdmin() ? View.VISIBLE : View.GONE); + removeAdminButton.setVisibility(adminStatus.isCanMakeNonAdmin() ? View.VISIBLE : View.GONE); + removeFromGroupButton.setVisibility(adminStatus.isCanRemove() ? View.VISIBLE : View.GONE); + }); + + viewModel.getIdentity().observe(getViewLifecycleOwner(), identityRecord -> { + viewSafetyNumberButton.setVisibility(identityRecord != null ? View.VISIBLE : View.GONE); + + if (identityRecord != null) { + viewSafetyNumberButton.setOnClickListener(view -> { + dismiss(); + viewModel.onViewSafetyNumberClicked(requireActivity(), identityRecord); + }); + } + }); + + avatar.setOnClickListener(view -> { + dismiss(); + viewModel.onAvatarClicked(requireActivity()); + }); + + messageButton.setOnClickListener(view -> { + dismiss(); + viewModel.onMessageClicked(requireActivity()); + }); + + secureCallButton.setOnClickListener(view -> { + dismiss(); + viewModel.onSecureCallClicked(requireActivity()); + }); + + blockButton.setOnClickListener(view -> viewModel.onBlockClicked(requireActivity())); + unblockButton.setOnClickListener(view -> viewModel.onUnblockClicked(requireActivity())); + + makeGroupAdminButton.setOnClickListener(view -> viewModel.onMakeGroupAdminClicked()); + removeAdminButton.setOnClickListener(view -> viewModel.onRemoveGroupAdminClicked()); + removeFromGroupButton.setOnClickListener(view -> viewModel.onRemoveFromGroupClicked()); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogRepository.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogRepository.java new file mode 100644 index 0000000000..e6d689b4ae --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogRepository.java @@ -0,0 +1,82 @@ +package org.thoughtcrime.securesms.recipients.ui.bottomsheet; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.database.GroupDatabase; +import org.thoughtcrime.securesms.database.IdentityDatabase; +import org.thoughtcrime.securesms.groups.GroupId; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.recipients.RecipientId; +import org.thoughtcrime.securesms.util.concurrent.SignalExecutors; +import org.thoughtcrime.securesms.util.concurrent.SimpleTask; + +final class RecipientDialogRepository { + + @NonNull private final GroupDatabase groupDatabase; + @NonNull private final Context context; + @NonNull private final RecipientId recipientId; + @Nullable private final GroupId groupId; + + RecipientDialogRepository(@NonNull Context context, + @NonNull RecipientId recipientId, + @Nullable GroupId groupId) + { + this.context = context; + this.groupDatabase = DatabaseFactory.getGroupDatabase(context); + this.recipientId = recipientId; + this.groupId = groupId; + } + + @NonNull RecipientId getRecipientId() { + return recipientId; + } + + @Nullable GroupId getGroupId() { + return groupId; + } + + void isAdminOfGroup(@NonNull RecipientId recipientId, @NonNull AdminCallback callback) { + SimpleTask.run(SignalExecutors.BOUNDED, + () -> { + if (groupId != null) { + Recipient recipient = Recipient.resolved(recipientId); + return groupDatabase.getGroup(groupId) + .transform(g -> g.isAdmin(recipient)) + .or(false); + } else { + return false; + } + }, + callback::isAdmin); + } + + void getIdentity(@NonNull IdentityCallback callback) { + SimpleTask.run(SignalExecutors.BOUNDED, + () -> DatabaseFactory.getIdentityDatabase(context) + .getIdentity(recipientId) + .orNull(), + callback::remoteIdentity); + } + + public void getRecipient(@NonNull RecipientCallback recipientCallback) { + SimpleTask.run(SignalExecutors.BOUNDED, + () -> Recipient.resolved(recipientId), + recipientCallback::onRecipient); + } + + interface AdminCallback { + void isAdmin(boolean admin); + } + + interface IdentityCallback { + void remoteIdentity(@Nullable IdentityDatabase.IdentityRecord identityRecord); + } + + interface RecipientCallback { + void onRecipient(@NonNull Recipient recipient); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogViewModel.java new file mode 100644 index 0000000000..b18a15a6bb --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogViewModel.java @@ -0,0 +1,158 @@ +package org.thoughtcrime.securesms.recipients.ui.bottomsheet; + +import android.app.Activity; +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +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 org.thoughtcrime.securesms.BlockUnblockDialog; +import org.thoughtcrime.securesms.RecipientPreferenceActivity; +import org.thoughtcrime.securesms.VerifyIdentityActivity; +import org.thoughtcrime.securesms.database.IdentityDatabase; +import org.thoughtcrime.securesms.groups.GroupId; +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.livedata.LiveDataPair; + +final class RecipientDialogViewModel extends ViewModel { + + private final Context context; + private final RecipientDialogRepository recipientDialogRepository; + private final LiveData recipient; + private final MutableLiveData identity; + private final LiveData adminActionStatus; + + private RecipientDialogViewModel(@NonNull Context context, + @NonNull RecipientDialogRepository recipientDialogRepository) + { + this.context = context; + this.recipientDialogRepository = recipientDialogRepository; + this.identity = new MutableLiveData<>(); + + MutableLiveData localIsAdmin = new DefaultValueLiveData<>(false); + MutableLiveData recipientIsAdmin = new DefaultValueLiveData<>(false); + + if (recipientDialogRepository.getGroupId() != null && recipientDialogRepository.getGroupId().isV2()) { + recipientDialogRepository.isAdminOfGroup(Recipient.self().getId(), localIsAdmin::setValue); + recipientDialogRepository.isAdminOfGroup(recipientDialogRepository.getRecipientId(), recipientIsAdmin::setValue); + } + + adminActionStatus = Transformations.map(new LiveDataPair<>(localIsAdmin, recipientIsAdmin, false, false), + pair -> { + boolean localAdmin = pair.first(); + boolean recipientAdmin = pair.second(); + + return new AdminActionStatus(localAdmin, + localAdmin && !recipientAdmin, + localAdmin && recipientAdmin); + }); + + recipient = Recipient.live(recipientDialogRepository.getRecipientId()).getLiveData(); + + recipientDialogRepository.getIdentity(identity::setValue); + } + + LiveData getRecipient() { + return recipient; + } + + LiveData getAdminActionStatus() { + return adminActionStatus; + } + + LiveData getIdentity() { + return identity; + } + + void onMessageClicked(@NonNull Activity activity) { + recipientDialogRepository.getRecipient(recipient -> CommunicationActions.startConversation(activity, recipient, null)); + } + + void onSecureCallClicked(@NonNull Activity activity) { + recipientDialogRepository.getRecipient(recipient -> CommunicationActions.startVoiceCall(activity, recipient)); + } + + void onBlockClicked(@NonNull FragmentActivity activity) { + recipientDialogRepository.getRecipient(recipient -> BlockUnblockDialog.showBlockFor(activity, activity.getLifecycle(), recipient, () -> RecipientUtil.block(context, recipient))); + } + + void onUnblockClicked(@NonNull FragmentActivity activity) { + recipientDialogRepository.getRecipient(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)); + } + + void onAvatarClicked(@NonNull Activity activity) { + activity.startActivity(RecipientPreferenceActivity.getLaunchIntent(activity, recipientDialogRepository.getRecipientId())); + } + + void onMakeGroupAdminClicked() { + // TODO GV2 + throw new AssertionError("NYI"); + } + + void onRemoveGroupAdminClicked() { + // TODO GV2 + throw new AssertionError("NYI"); + } + + void onRemoveFromGroupClicked() { + // TODO GV2 + throw new AssertionError("NYI"); + } + + static class AdminActionStatus { + private final boolean canRemove; + private final boolean canMakeAdmin; + private final boolean canMakeNonAdmin; + + AdminActionStatus(boolean canRemove, boolean canMakeAdmin, boolean canMakeNonAdmin) { + this.canRemove = canRemove; + this.canMakeAdmin = canMakeAdmin; + this.canMakeNonAdmin = canMakeNonAdmin; + } + + boolean isCanRemove() { + return canRemove; + } + + boolean isCanMakeAdmin() { + return canMakeAdmin; + } + + boolean isCanMakeNonAdmin() { + return canMakeNonAdmin; + } + } + + public static class Factory implements ViewModelProvider.Factory { + + private final Context context; + private final RecipientId recipientId; + private final GroupId groupId; + + Factory(@NonNull Context context, @NonNull RecipientId recipientId, @Nullable GroupId groupId) { + this.context = context; + this.recipientId = recipientId; + this.groupId = groupId; + } + + @Override + public @NonNull T create(@NonNull Class modelClass) { + //noinspection unchecked + return (T) new RecipientDialogViewModel(context, new RecipientDialogRepository(context, recipientId, groupId)); + } + } +} diff --git a/app/src/main/res/drawable/ic_block_tinted_24.xml b/app/src/main/res/drawable/ic_block_tinted_24.xml new file mode 100644 index 0000000000..c3084e98b0 --- /dev/null +++ b/app/src/main/res/drawable/ic_block_tinted_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_info_outline_tinted_24.xml b/app/src/main/res/drawable/ic_info_outline_tinted_24.xml new file mode 100644 index 0000000000..e09816bf46 --- /dev/null +++ b/app/src/main/res/drawable/ic_info_outline_tinted_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_info_solid_tinted_24.xml b/app/src/main/res/drawable/ic_info_solid_tinted_24.xml new file mode 100644 index 0000000000..9fab7b7f5c --- /dev/null +++ b/app/src/main/res/drawable/ic_info_solid_tinted_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_leave_tinted_24.xml b/app/src/main/res/drawable/ic_leave_tinted_24.xml new file mode 100644 index 0000000000..c40f4fad20 --- /dev/null +++ b/app/src/main/res/drawable/ic_leave_tinted_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_message_outline_tinted_24.xml b/app/src/main/res/drawable/ic_message_outline_tinted_24.xml index 86d8b5cc1a..5b9b175dfb 100644 --- a/app/src/main/res/drawable/ic_message_outline_tinted_24.xml +++ b/app/src/main/res/drawable/ic_message_outline_tinted_24.xml @@ -1,4 +1,9 @@ - - \ No newline at end of file + + + diff --git a/app/src/main/res/drawable/ic_message_outline_tinted_bitmap_24.xml b/app/src/main/res/drawable/ic_message_outline_tinted_bitmap_24.xml new file mode 100644 index 0000000000..86d8b5cc1a --- /dev/null +++ b/app/src/main/res/drawable/ic_message_outline_tinted_bitmap_24.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_message_solid_tinted_24.xml b/app/src/main/res/drawable/ic_message_solid_tinted_24.xml index 9b8a9fcd0d..7aa9ae47be 100644 --- a/app/src/main/res/drawable/ic_message_solid_tinted_24.xml +++ b/app/src/main/res/drawable/ic_message_solid_tinted_24.xml @@ -1,4 +1,9 @@ - - \ No newline at end of file + + + diff --git a/app/src/main/res/drawable/ic_message_solid_tinted_bitmap_24.xml b/app/src/main/res/drawable/ic_message_solid_tinted_bitmap_24.xml new file mode 100644 index 0000000000..9b8a9fcd0d --- /dev/null +++ b/app/src/main/res/drawable/ic_message_solid_tinted_bitmap_24.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_phone_right_outline_tinted_24.xml b/app/src/main/res/drawable/ic_phone_right_outline_tinted_24.xml new file mode 100644 index 0000000000..6e3c562206 --- /dev/null +++ b/app/src/main/res/drawable/ic_phone_right_outline_tinted_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_phone_right_solid_tinted_24.xml b/app/src/main/res/drawable/ic_phone_right_solid_tinted_24.xml new file mode 100644 index 0000000000..956d34e4b1 --- /dev/null +++ b/app/src/main/res/drawable/ic_phone_right_solid_tinted_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/rounded_dialog.xml b/app/src/main/res/drawable/rounded_dialog.xml new file mode 100644 index 0000000000..9e0caea72f --- /dev/null +++ b/app/src/main/res/drawable/rounded_dialog.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/rounded_dialog_dark.xml b/app/src/main/res/drawable/rounded_dialog_dark.xml new file mode 100644 index 0000000000..a78269651c --- /dev/null +++ b/app/src/main/res/drawable/rounded_dialog_dark.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/recipient_bottom_sheet.xml b/app/src/main/res/layout/recipient_bottom_sheet.xml new file mode 100644 index 0000000000..8db547a63c --- /dev/null +++ b/app/src/main/res/layout/recipient_bottom_sheet.xml @@ -0,0 +1,138 @@ + + + + + + + + + + + +