Add ability to edit default reactions.

fork-5.53.8
Cody Henthorne 2021-05-27 11:21:11 -04:00
rodzic 811bef8c35
commit e5b0941d30
19 zmienionych plików z 587 dodań i 37 usunięć

Wyświetl plik

@ -595,6 +595,10 @@
android:screenOrientation="portrait"
android:theme="@style/Theme.Signal.WallpaperCropper" />
<activity android:name=".reactions.edit.EditReactionsActivity"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<service android:enabled="true" android:name=".service.webrtc.WebRtcCallService"/>
<service android:enabled="true" android:name=".service.ApplicationMigrationService"/>
<service android:enabled="true" android:exported="false" android:name=".service.KeyCachingService"/>

Wyświetl plik

@ -0,0 +1,44 @@
package org.thoughtcrime.securesms.animation.transitions
import android.animation.Animator
import android.animation.ObjectAnimator
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.transition.Transition
import androidx.transition.TransitionValues
private const val ALPHA = "signal.alpha_transition.alpha"
/**
* Alpha transition that can be used with [ConstraintLayout]
*/
class AlphaTransition : Transition() {
override fun captureStartValues(transitionValues: TransitionValues) {
captureValues(transitionValues)
}
override fun captureEndValues(transitionValues: TransitionValues) {
captureValues(transitionValues)
}
private fun captureValues(transitionValues: TransitionValues) {
val view: View = transitionValues.view
if (view !is ConstraintLayout) {
transitionValues.values[ALPHA] = view.alpha
}
}
override fun createAnimator(sceneRoot: ViewGroup, startValues: TransitionValues?, endValues: TransitionValues?): Animator? {
if (startValues == null || endValues == null) {
return null
}
val view: View = endValues.view
val startAlpha: Float = startValues.values[ALPHA] as? Float ?: view.alpha
val endAlpha: Float = endValues.values[ALPHA] as? Float ?: view.alpha
return ObjectAnimator.ofFloat(view, "alpha", startAlpha, endAlpha)
}
}

Wyświetl plik

@ -118,11 +118,15 @@ public final class ConversationReactionOverlay extends RelativeLayout {
toolbar.setOnMenuItemClickListener(this::handleToolbarItemClicked);
toolbar.setNavigationOnClickListener(view -> hide());
emojiViews = Stream.of(ReactionEmoji.values())
.map(e -> findViewById(e.viewId))
.toArray(EmojiImageView[]::new);
emojiViews = new EmojiImageView[] { findViewById(R.id.reaction_1),
findViewById(R.id.reaction_2),
findViewById(R.id.reaction_3),
findViewById(R.id.reaction_4),
findViewById(R.id.reaction_5),
findViewById(R.id.reaction_6),
findViewById(R.id.reaction_7) };
customEmojiIndex = ReactionEmoji.values().length - 1;
customEmojiIndex = emojiViews.length - 1;
distanceFromTouchDownPointToTopOfScrubberDeadZone = getResources().getDimensionPixelSize(R.dimen.conversation_reaction_scrub_deadzone_distance_from_touch_top);
distanceFromTouchDownPointToBottomOfScrubberDeadZone = getResources().getDimensionPixelSize(R.dimen.conversation_reaction_scrub_deadzone_distance_from_touch_bottom);
@ -364,7 +368,8 @@ public final class ConversationReactionOverlay extends RelativeLayout {
}
private void setupSelectedEmoji() {
final String oldEmoji = getOldEmoji(messageRecord);
final List<String> emojis = SignalStore.emojiValues().getReactions();
final String oldEmoji = getOldEmoji(messageRecord);
if (oldEmoji == null) {
selectedView.setVisibility(View.GONE);
@ -380,7 +385,7 @@ public final class ConversationReactionOverlay extends RelativeLayout {
view.setTranslationY(0);
boolean isAtCustomIndex = i == customEmojiIndex;
boolean isNotAtCustomIndexAndOldEmojiMatches = !isAtCustomIndex && oldEmoji != null && ReactionEmoji.values()[i].emoji.equals(EmojiUtil.getCanonicalRepresentation(oldEmoji));
boolean isNotAtCustomIndexAndOldEmojiMatches = !isAtCustomIndex && oldEmoji != null && emojis.get(i).equals(EmojiUtil.getCanonicalRepresentation(oldEmoji));
boolean isAtCustomIndexAndOldEmojiExists = isAtCustomIndex && oldEmoji != null;
if (!foundSelected &&
@ -401,13 +406,13 @@ public final class ConversationReactionOverlay extends RelativeLayout {
view.setImageEmoji(oldEmoji);
view.setTag(oldEmoji);
} else {
view.setImageEmoji(SignalStore.emojiValues().getPreferredVariation(ReactionEmoji.values()[i].emoji));
view.setImageEmoji(SignalStore.emojiValues().getPreferredVariation(emojis.get(i)));
}
} else if (isAtCustomIndex) {
view.setImageDrawable(ContextCompat.getDrawable(getContext(), R.drawable.ic_any_emoji_32));
view.setTag(null);
} else {
view.setImageEmoji(SignalStore.emojiValues().getPreferredVariation(ReactionEmoji.values()[i].emoji));
view.setImageEmoji(SignalStore.emojiValues().getPreferredVariation(emojis.get(i)));
}
}
}
@ -469,7 +474,7 @@ public final class ConversationReactionOverlay extends RelativeLayout {
if (selected == customEmojiIndex) {
onReactionSelectedListener.onCustomReactionSelected(messageRecord, emojiViews[selected].getTag() != null);
} else {
onReactionSelectedListener.onReactionSelected(messageRecord, SignalStore.emojiValues().getPreferredVariation(ReactionEmoji.values()[selected].emoji));
onReactionSelectedListener.onReactionSelected(messageRecord, SignalStore.emojiValues().getPreferredVariation(SignalStore.emojiValues().getReactions().get(selected)));
}
} else {
hide();
@ -642,24 +647,6 @@ public final class ConversationReactionOverlay extends RelativeLayout {
}
}
private enum ReactionEmoji {
HEART(R.id.reaction_1, "\u2764\ufe0f"),
THUMBS_UP(R.id.reaction_2, "\ud83d\udc4d"),
THUMBS_DOWN(R.id.reaction_3, "\ud83d\udc4e"),
LAUGH(R.id.reaction_4, "\ud83d\ude02"),
SURPRISE(R.id.reaction_5, "\ud83d\ude2e"),
SAD(R.id.reaction_6, "\ud83d\ude22"),
ANGRY(R.id.reaction_7, "\ud83d\ude21");
final @IdRes int viewId;
final String emoji;
ReactionEmoji(int viewId, String emoji) {
this.viewId = viewId;
this.emoji = emoji;
}
}
private enum OverlayState {
HIDDEN,
UNINITAILIZED,

Wyświetl plik

@ -116,7 +116,6 @@ import org.thoughtcrime.securesms.payments.preferences.PaymentsActivity;
import org.thoughtcrime.securesms.payments.preferences.details.PaymentDetailsFragmentArgs;
import org.thoughtcrime.securesms.payments.preferences.details.PaymentDetailsParcelable;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.ratelimit.RecaptchaProofActivity;
import org.thoughtcrime.securesms.ratelimit.RecaptchaProofBottomSheetFragment;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.KeyCachingService;

Wyświetl plik

@ -1,16 +1,28 @@
package org.thoughtcrime.securesms.keyvalue;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.components.emoji.EmojiUtil;
import org.thoughtcrime.securesms.util.Util;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class EmojiValues extends SignalStoreValues {
private static final String PREFIX = "emojiPref__";
public static final List<String> DEFAULT_REACTIONS_LIST = Arrays.asList("\u2764\ufe0f",
"\ud83d\udc4d",
"\ud83d\udc4e",
"\ud83d\ude02",
"\ud83d\ude2e",
"\ud83d\ude22");
private static final String PREFIX = "emojiPref__";
private static final String NEXT_SCHEDULED_CHECK = PREFIX + "next_scheduled_check";
private static final String REACTIONS_LIST = PREFIX + "reactions_list";
EmojiValues(@NonNull KeyValueStore store) {
super(store);
@ -23,7 +35,7 @@ public class EmojiValues extends SignalStoreValues {
@Override
@NonNull List<String> getKeysToIncludeInBackup() {
return Collections.emptyList();
return Collections.singletonList(REACTIONS_LIST);
}
public long getNextScheduledCheck() {
@ -49,4 +61,17 @@ public class EmojiValues extends SignalStoreValues {
return getString(PREFIX + canonical, emoji);
}
public @NonNull List<String> getReactions() {
String list = getString(REACTIONS_LIST, "");
if (TextUtils.isEmpty(list)) {
return DEFAULT_REACTIONS_LIST;
} else {
return Arrays.asList(list.split(","));
}
}
public void setReactions(List<String> reactions) {
putString(REACTIONS_LIST, Util.join(reactions, ","));
}
}

Wyświetl plik

@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.reactions.any;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.util.SparseArray;
import android.view.KeyEvent;
@ -35,6 +36,7 @@ import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider;
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.reactions.ReactionsLoader;
import org.thoughtcrime.securesms.reactions.edit.EditReactionsActivity;
import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.ViewUtil;
@ -53,6 +55,7 @@ public final class ReactWithAnyEmojiBottomSheetDialogFragment extends BottomShee
private static final String ARG_START_PAGE = "arg_start_page";
private static final String ARG_SHADOWS = "arg_shadows";
private static final String ARG_RECENT_KEY = "arg_recent_key";
private static final String ARG_EDIT = "arg_edit";
private ReactWithAnyEmojiViewModel viewModel;
private TextSwitcher categoryLabel;
@ -62,6 +65,8 @@ public final class ReactWithAnyEmojiBottomSheetDialogFragment extends BottomShee
private SparseArray<ReactWithAnyEmojiAdapter.ScrollableChild> pageArray = new SparseArray<>();
private Callback callback;
private ReactionsLoader reactionsLoader;
private View editReactions;
private boolean showEditReactions;
public static DialogFragment createForMessageRecord(@NonNull MessageRecord messageRecord, int startingPage) {
DialogFragment fragment = new ReactWithAnyEmojiBottomSheetDialogFragment();
@ -72,6 +77,7 @@ public final class ReactWithAnyEmojiBottomSheetDialogFragment extends BottomShee
args.putInt(ARG_START_PAGE, startingPage);
args.putBoolean(ARG_SHADOWS, false);
args.putString(ARG_RECENT_KEY, REACTION_STORAGE_KEY);
args.putBoolean(ARG_EDIT, true);
fragment.setArguments(args);
return fragment;
@ -91,11 +97,29 @@ public final class ReactWithAnyEmojiBottomSheetDialogFragment extends BottomShee
return fragment;
}
public static DialogFragment createForEditReactions() {
DialogFragment fragment = new ReactWithAnyEmojiBottomSheetDialogFragment();
Bundle args = new Bundle();
args.putLong(ARG_MESSAGE_ID, -1);
args.putBoolean(ARG_IS_MMS, false);
args.putInt(ARG_START_PAGE, -1);
args.putBoolean(ARG_SHADOWS, false);
args.putString(ARG_RECENT_KEY, REACTION_STORAGE_KEY);
fragment.setArguments(args);
return fragment;
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
callback = (Callback) context;
if (getParentFragment() instanceof Callback) {
callback = (Callback) getParentFragment();
} else {
callback = (Callback) context;
}
}
@Override
@ -159,6 +183,12 @@ public final class ReactWithAnyEmojiBottomSheetDialogFragment extends BottomShee
categoryLabel = view.findViewById(R.id.category_label);
categoryPager = view.findViewById(R.id.category_pager);
editReactions = view.findViewById(R.id.edit_reactions);
showEditReactions = requireArguments().getBoolean(ARG_EDIT, false);
if (showEditReactions) {
editReactions.setOnClickListener(v -> startActivity(new Intent(requireContext(), EditReactionsActivity.class)));
}
adapter = new ReactWithAnyEmojiAdapter(this, this, (position, pageView) -> {
pageArray.put(position, pageView);
@ -264,6 +294,7 @@ public final class ReactWithAnyEmojiBottomSheetDialogFragment extends BottomShee
}
categoryLabel.setText(getString(adapter.getItem(position).getLabel()));
editReactions.setVisibility(showEditReactions && position == 0 ? View.VISIBLE : View.GONE);
}
private int getStartingPage(boolean firstPageHasContent) {

Wyświetl plik

@ -0,0 +1,27 @@
package org.thoughtcrime.securesms.reactions.edit
import android.os.Bundle
import org.thoughtcrime.securesms.PassphraseRequiredActivity
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme
import org.thoughtcrime.securesms.util.DynamicTheme
class EditReactionsActivity : PassphraseRequiredActivity() {
private val theme: DynamicTheme = DynamicNoActionBarTheme()
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
super.onCreate(savedInstanceState, ready)
theme.onCreate(this)
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.replace(android.R.id.content, EditReactionsFragment())
.commit()
}
}
override fun onResume() {
super.onResume()
theme.onResume(this)
}
}

Wyświetl plik

@ -0,0 +1,176 @@
package org.thoughtcrime.securesms.reactions.edit
import android.animation.ObjectAnimator
import android.os.Bundle
import android.view.View
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import androidx.appcompat.widget.Toolbar
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.ViewModelProviders
import androidx.transition.ChangeBounds
import androidx.transition.Transition
import androidx.transition.TransitionManager
import androidx.transition.TransitionSet
import org.thoughtcrime.securesms.LoggingFragment
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.animation.transitions.AlphaTransition
import org.thoughtcrime.securesms.components.emoji.EmojiImageView
import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiBottomSheetDialogFragment
import org.thoughtcrime.securesms.util.ViewUtil
private val SELECTED_SIZE = ViewUtil.dpToPx(36)
private val UNSELECTED_SIZE = ViewUtil.dpToPx(26)
/**
* Edit default reactions that show when long pressing.
*/
class EditReactionsFragment : LoggingFragment(R.layout.edit_reactions_fragment), ReactWithAnyEmojiBottomSheetDialogFragment.Callback {
private lateinit var toolbar: Toolbar
private lateinit var reactionViews: List<EmojiImageView>
private lateinit var scrubber: ConstraintLayout
private lateinit var mask: View
private lateinit var defaultSet: ConstraintSet
private lateinit var viewModel: EditReactionsViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
toolbar = view.findViewById(R.id.toolbar)
toolbar.setTitle(R.string.EditReactionsFragment__edit_reactions)
toolbar.setNavigationOnClickListener {
requireActivity().onBackPressed()
}
reactionViews = listOf(
view.findViewById(R.id.reaction_1),
view.findViewById(R.id.reaction_2),
view.findViewById(R.id.reaction_3),
view.findViewById(R.id.reaction_4),
view.findViewById(R.id.reaction_5),
view.findViewById(R.id.reaction_6)
)
reactionViews.forEach { it.setOnClickListener(this::onEmojiClick) }
scrubber = view.findViewById(R.id.edit_reactions_fragment_scrubber)
defaultSet = ConstraintSet().apply { clone(scrubber) }
mask = view.findViewById(R.id.edit_reactions_fragment_reaction_mask)
view.findViewById<View>(R.id.edit_reactions_reset_emoji).setOnClickListener { viewModel.resetToDefaults() }
view.findViewById<View>(R.id.edit_reactions_fragment_save).setOnClickListener {
viewModel.save()
requireActivity().onBackPressed()
}
viewModel = ViewModelProviders.of(this).get(EditReactionsViewModel::class.java)
viewModel.reactions.observe(viewLifecycleOwner) { emojis ->
emojis.forEachIndexed { index, emoji -> reactionViews[index].setImageEmoji(emoji) }
}
viewModel.selection.observe(viewLifecycleOwner) { selection ->
if (selection == EditReactionsViewModel.NO_SELECTION) {
deselectAll()
ObjectAnimator.ofFloat(mask, "alpha", 0f).start()
} else {
ObjectAnimator.ofFloat(mask, "alpha", 1f).start()
select(reactionViews[selection])
ReactWithAnyEmojiBottomSheetDialogFragment.createForEditReactions().show(childFragmentManager, REACT_SHEET_TAG)
}
}
view.setOnClickListener { viewModel.setSelection(EditReactionsViewModel.NO_SELECTION) }
}
private fun select(emojiImageView: EmojiImageView) {
val set = ConstraintSet()
set.clone(scrubber)
reactionViews.forEach { view ->
view.clearAnimation()
view.rotation = 0f
if (view.id == emojiImageView.id) {
set.constrainWidth(view.id, SELECTED_SIZE)
set.constrainHeight(view.id, SELECTED_SIZE)
set.setAlpha(view.id, 1f)
} else {
set.constrainWidth(view.id, UNSELECTED_SIZE)
set.constrainHeight(view.id, UNSELECTED_SIZE)
set.setAlpha(view.id, 0.3f)
}
}
TransitionManager.beginDelayedTransition(scrubber, createSelectTransitionSet(emojiImageView))
set.applyTo(scrubber)
}
private fun deselectAll() {
reactionViews.forEach { it.clearAnimation() }
TransitionManager.beginDelayedTransition(scrubber, createTransitionSet())
defaultSet.applyTo(scrubber)
}
private fun onEmojiClick(view: View) {
viewModel.setSelection(reactionViews.indexOf(view))
}
override fun onReactWithAnyEmojiDialogDismissed() {
viewModel.setSelection(EditReactionsViewModel.NO_SELECTION)
}
override fun onReactWithAnyEmojiPageChanged(page: Int) {
}
override fun onReactWithAnyEmojiSelected(emoji: String) {
viewModel.onEmojiSelected(emoji)
}
companion object {
private const val REACT_SHEET_TAG = "REACT_SHEET_TAG"
private fun createTransitionSet(): Transition {
return TransitionSet().apply {
ordering = TransitionSet.ORDERING_TOGETHER
duration = 250
addTransition(AlphaTransition())
addTransition(ChangeBounds())
}
}
private fun createSelectTransitionSet(target: View): Transition {
return createTransitionSet().addListener(object : Transition.TransitionListener {
override fun onTransitionEnd(transition: Transition) {
startRockingAnimation(target)
}
override fun onTransitionStart(transition: Transition) = Unit
override fun onTransitionCancel(transition: Transition) = Unit
override fun onTransitionPause(transition: Transition) = Unit
override fun onTransitionResume(transition: Transition) = Unit
})
}
private fun startRockingAnimation(target: View) {
val startRocking: Animation = AnimationUtils.loadAnimation(target.context, R.anim.rock_start)
startRocking.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationEnd(animation: Animation?) {
val continualRocking: Animation = AnimationUtils.loadAnimation(target.context, R.anim.rock)
continualRocking.repeatCount = Animation.INFINITE
continualRocking.repeatMode = Animation.REVERSE
target.startAnimation(continualRocking)
}
override fun onAnimationStart(animation: Animation?) = Unit
override fun onAnimationRepeat(animation: Animation?) = Unit
})
target.clearAnimation()
target.startAnimation(startRocking)
}
}
}

Wyświetl plik

@ -0,0 +1,47 @@
package org.thoughtcrime.securesms.reactions.edit
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import org.thoughtcrime.securesms.keyvalue.EmojiValues
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil
import org.thoughtcrime.securesms.util.livedata.Store
class EditReactionsViewModel : ViewModel() {
private val emojiValues: EmojiValues = SignalStore.emojiValues()
private val store: Store<State> = Store(State(reactions = emojiValues.reactions.map { emojiValues.getPreferredVariation(it) }))
val reactions: LiveData<List<String>> = LiveDataUtil.mapDistinct(store.stateLiveData, State::reactions)
val selection: LiveData<Int> = LiveDataUtil.mapDistinct(store.stateLiveData, State::selection)
fun setSelection(selection: Int) {
store.update { it.copy(selection = selection) }
}
fun onEmojiSelected(emoji: String) {
store.update { state ->
if (state.selection != NO_SELECTION && state.selection in state.reactions.indices) {
val preferredEmoji: String = emojiValues.getPreferredVariation(emoji)
val newReactions: List<String> = state.reactions.toMutableList().apply { set(state.selection, preferredEmoji) }
state.copy(reactions = newReactions)
} else {
state
}
}
}
fun resetToDefaults() {
store.update { it.copy(reactions = EmojiValues.DEFAULT_REACTIONS_LIST) }
}
fun save() {
emojiValues.reactions = store.state.reactions
}
companion object {
const val NO_SELECTION: Int = -1
}
data class State(val selection: Int = NO_SELECTION, val reactions: List<String>)
}

Wyświetl plik

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200"
android:fromDegrees="-8"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="8" />

Wyświetl plik

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="100"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="-8" />

Wyświetl plik

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="rectangle" xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/transparent_black_80" />
<solid android:color="@color/core_grey_80" />
<corners android:radius="30dp" />
</shape>

Wyświetl plik

@ -5,7 +5,7 @@
android:viewportHeight="32">
<path
android:pathData="M16,16m-16,0a16,16 0,1 1,32 0a16,16 0,1 1,-32 0"
android:fillColor="@color/core_grey_80"/>
android:fillColor="@color/core_grey_70"/>
<path
android:pathData="M8,16m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"
android:fillColor="@color/core_grey_05"/>

Wyświetl plik

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/settings_ripple_color" />

Wyświetl plik

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<color android:color="@color/settings_ripple_color" />
</item>
</selector>

Wyświetl plik

@ -0,0 +1,155 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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">
<include layout="@layout/dsl_settings_toolbar" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/edit_reactions_one_third_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.2" />
<com.google.android.material.button.MaterialButton
android:id="@+id/edit_reactions_reset_emoji"
style="@style/Signal.Widget.Button.Medium.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="@string/EditReactionsFragment__reset_emoji"
app:layout_constraintBottom_toBottomOf="@+id/edit_reactions_fragment_save"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/edit_reactions_fragment_save" />
<com.dd.CircularProgressButton
android:id="@+id/edit_reactions_fragment_save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:textAllCaps="false"
android:textColor="@color/white"
android:textSize="14sp"
app:cornerRadius="80dp"
app:cpb_colorIndicator="@color/white"
app:cpb_colorProgress="?colorAccent"
app:cpb_cornerRadius="28dp"
app:cpb_selectorIdle="@drawable/progress_button_state"
app:cpb_textIdle="@string/EditReactionsFragment_save"
app:elevation="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="@+id/edit_reactions_fragment_tap_to_replace"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="@string/EditReactionsFragment__tap_to_replace_an_emoji"
android:textAppearance="@style/Signal.Text.Caption"
android:textColor="@color/signal_text_secondary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/edit_reactions_fragment_scrubber" />
<View
android:id="@+id/edit_reactions_fragment_reaction_mask"
android:layout_width="0dp"
android:layout_height="0dp"
android:alpha="0"
android:background="@color/reactions_screen_shade_color"
app:elevation="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/edit_reactions_fragment_scrubber"
android:layout_width="@dimen/reaction_scrubber_width"
android:clickable="false"
android:layout_height="?attr/actionBarSize"
android:background="@drawable/conversation_reaction_overlay_background"
android:clipChildren="false"
android:clipToPadding="false"
android:elevation="4dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/edit_reactions_one_third_guideline"
tools:ignore="UnusedAttribute">
<org.thoughtcrime.securesms.components.emoji.EmojiImageView
android:id="@+id/reaction_1"
android:layout_width="34dp"
android:layout_height="34dp"
android:foreground="@drawable/dsl_preference_item_background_borderless"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/reaction_2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@tools:sample/avatars" />
<org.thoughtcrime.securesms.components.emoji.EmojiImageView
android:id="@+id/reaction_2"
android:layout_width="34dp"
android:layout_height="34dp"
android:foreground="@drawable/dsl_preference_item_background_borderless"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/reaction_3"
app:layout_constraintStart_toEndOf="@id/reaction_1"
app:layout_constraintTop_toTopOf="parent"
tools:src="@tools:sample/avatars" />
<org.thoughtcrime.securesms.components.emoji.EmojiImageView
android:id="@+id/reaction_3"
android:layout_width="34dp"
android:layout_height="34dp"
android:foreground="@drawable/dsl_preference_item_background_borderless"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/reaction_4"
app:layout_constraintStart_toEndOf="@id/reaction_2"
app:layout_constraintTop_toTopOf="parent"
tools:src="@tools:sample/avatars" />
<org.thoughtcrime.securesms.components.emoji.EmojiImageView
android:id="@+id/reaction_4"
android:layout_width="34dp"
android:layout_height="34dp"
android:foreground="@drawable/dsl_preference_item_background_borderless"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/reaction_5"
app:layout_constraintStart_toEndOf="@id/reaction_3"
app:layout_constraintTop_toTopOf="parent"
tools:src="@tools:sample/avatars" />
<org.thoughtcrime.securesms.components.emoji.EmojiImageView
android:id="@+id/reaction_5"
android:layout_width="34dp"
android:layout_height="34dp"
android:foreground="@drawable/dsl_preference_item_background_borderless"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/reaction_6"
app:layout_constraintStart_toEndOf="@id/reaction_4"
app:layout_constraintTop_toTopOf="parent"
tools:src="@tools:sample/avatars" />
<org.thoughtcrime.securesms.components.emoji.EmojiImageView
android:id="@+id/reaction_6"
android:layout_width="34dp"
android:layout_height="34dp"
android:foreground="@drawable/dsl_preference_item_background_borderless"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/reaction_5"
app:layout_constraintTop_toTopOf="parent"
tools:src="@tools:sample/avatars" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

Wyświetl plik

@ -15,7 +15,7 @@
android:layout_marginEnd="10dp"
android:inAnimation="@anim/fade_in"
android:outAnimation="@anim/fade_out"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toStartOf="@+id/edit_reactions"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
@ -23,16 +23,29 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Signal.Subtitle2"
android:textColor="@color/signal_text_secondary"
android:textColor="@color/signal_icon_tint_primary"
tools:text="Smileys &amp; People" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Signal.Subtitle2"
android:textColor="@color/signal_text_secondary" />
android:textColor="@color/signal_icon_tint_primary" />
</TextSwitcher>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/edit_reactions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/EditReactionsFragment__edit_reactions"
android:foreground="?attr/selectableItemBackgroundBorderless"
android:padding="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_settings_outline_24"
app:tint="@color/signal_icon_tint_primary"
tools:ignore="UnusedAttribute" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/category_pager"
android:layout_width="match_parent"
@ -46,4 +59,4 @@
app:layout_constraintTop_toBottomOf="@id/category_label"
app:layout_constraintVertical_bias="0" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

Wyświetl plik

@ -206,6 +206,13 @@
app:exitAnim="@anim/fragment_open_exit"
app:popEnterAnim="@anim/fragment_close_enter"
app:popExitAnim="@anim/fragment_close_exit" />
<action
android:id="@+id/action_chatsSettingsFragment_to_editReactionsFragment"
app:destination="@id/editReactionsFragment"
app:enterAnim="@anim/fragment_open_enter"
app:exitAnim="@anim/fragment_open_exit"
app:popEnterAnim="@anim/fragment_close_enter"
app:popExitAnim="@anim/fragment_close_exit" />
</fragment>
<fragment
@ -233,6 +240,12 @@
android:name="org.thoughtcrime.securesms.components.settings.app.wrapped.WrappedMmsPreferencesFragment"
android:label="mms_preferences_fragment" />
<fragment
android:id="@+id/editReactionsFragment"
android:name="org.thoughtcrime.securesms.reactions.edit.EditReactionsFragment"
android:label="edit_reactions_fragment"
tools:layout="@layout/edit_reactions_fragment" />
<!-- endregion -->
<!-- Notifications -->

Wyświetl plik

@ -3502,6 +3502,12 @@
<string name="ChatColorGradientTool_top_edge_selector">Top edge selector</string>
<string name="ChatColorGradientTool_bottom_edge_selector">Bottom edge selector</string>
<!-- EditReactionsFragment -->
<string name="EditReactionsFragment__edit_reactions">Edit Reactions</string>
<string name="EditReactionsFragment__tap_to_replace_an_emoji">Tap to replace an emoji</string>
<string name="EditReactionsFragment__reset_emoji">Reset emoji</string>
<string name="EditReactionsFragment_save">Save</string>
<!-- EOF -->
</resources>