Add polish to voice note bubbles.

This commit is contained in:
Alex Hart 2020-10-22 16:22:21 -03:00 committed by Cody Henthorne
parent b4b1e5b605
commit ac54b5cbdf
24 changed files with 272 additions and 46 deletions

View file

@ -61,6 +61,7 @@ public final class AudioView extends FrameLayout {
@ColorInt private final int waveFormPlayedBarsColor;
@ColorInt private final int waveFormUnplayedBarsColor;
@ColorInt private final int waveFormThumbTint;
@Nullable private SlideClickListener downloadListener;
private int backwardsCounter;
@ -107,6 +108,9 @@ public final class AudioView extends FrameLayout {
this.waveFormPlayedBarsColor = typedArray.getColor(R.styleable.AudioView_waveformPlayedBarsColor, Color.WHITE);
this.waveFormUnplayedBarsColor = typedArray.getColor(R.styleable.AudioView_waveformUnplayedBarsColor, Color.WHITE);
this.waveFormThumbTint = typedArray.getColor(R.styleable.AudioView_waveformThumbTint, Color.WHITE);
progressAndPlay.getBackground().setColorFilter(typedArray.getColor(R.styleable.AudioView_progressAndPlayTint, Color.BLACK), PorterDuff.Mode.SRC_IN);
} finally {
if (typedArray != null) {
typedArray.recycle();
@ -132,10 +136,15 @@ public final class AudioView extends FrameLayout {
public void setAudio(final @NonNull AudioSlide audio,
final @Nullable Callbacks callbacks,
final boolean showControls)
final boolean showControls,
final boolean forceHideDuration)
{
this.callbacks = callbacks;
if (duration != null) {
duration.setVisibility(View.VISIBLE);
}
if (seekBar instanceof WaveFormSeekBarView) {
if (audioSlide != null && !Objects.equals(audioSlide.getUri(), audio.getUri())) {
WaveFormSeekBarView waveFormView = (WaveFormSeekBarView) seekBar;
@ -150,9 +159,11 @@ public final class AudioView extends FrameLayout {
seekBar.setEnabled(false);
downloadButton.setOnClickListener(new DownloadClickedListener(audio));
if (circleProgress.isSpinning()) circleProgress.stopSpinning();
circleProgress.setVisibility(View.GONE);
} else if (showControls && audio.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_STARTED) {
controlToggle.displayQuick(progressAndPlay);
seekBar.setEnabled(false);
circleProgress.setVisibility(View.VISIBLE);
circleProgress.spin();
} else {
seekBar.setEnabled(true);
@ -164,13 +175,13 @@ public final class AudioView extends FrameLayout {
if (seekBar instanceof WaveFormSeekBarView) {
WaveFormSeekBarView waveFormView = (WaveFormSeekBarView) seekBar;
waveFormView.setColors(waveFormPlayedBarsColor, waveFormUnplayedBarsColor);
waveFormView.setColors(waveFormPlayedBarsColor, waveFormUnplayedBarsColor, waveFormThumbTint);
if (android.os.Build.VERSION.SDK_INT >= 23) {
new AudioWaveForm(getContext(), audio).getWaveForm(
data -> {
if (duration != null) {
durationMillis = data.getDuration(TimeUnit.MILLISECONDS);
updateProgress(0, 0);
durationMillis = data.getDuration(TimeUnit.MILLISECONDS);
updateProgress(0, 0);
if (!forceHideDuration && duration != null) {
duration.setVisibility(VISIBLE);
}
waveFormView.setWaveData(data.getWaveForm());
@ -183,12 +194,21 @@ public final class AudioView extends FrameLayout {
}
}
}
if (forceHideDuration && duration != null) {
duration.setVisibility(View.GONE);
}
}
public void setDownloadClickListener(@Nullable SlideClickListener listener) {
this.downloadListener = listener;
}
public @Nullable Uri getAudioSlideUri() {
if (audioSlide != null) return audioSlide.getUri();
else return null;
}
private void onPlaybackState(@NonNull VoiceNotePlaybackState voiceNotePlaybackState) {
onStart(voiceNotePlaybackState.getUri(), voiceNotePlaybackState.isAutoReset());
onProgress(voiceNotePlaybackState.getUri(),
@ -274,6 +294,10 @@ public final class AudioView extends FrameLayout {
}
private void updateProgress(float progress, long millis) {
if (callbacks != null) {
callbacks.onProgressUpdated(durationMillis, millis);
}
if (duration != null && durationMillis > 0) {
long remainingSecs = TimeUnit.MILLISECONDS.toSeconds(durationMillis - millis);
duration.setText(getResources().getString(R.string.AudioView_duration, remainingSecs / 60, remainingSecs % 60));
@ -336,7 +360,7 @@ public final class AudioView extends FrameLayout {
if (!smallView || seekBar.getProgress() == 0) {
circleProgress.setInstantProgress(1);
}
circleProgress.setVisibility(VISIBLE);
circleProgress.setVisibility(GONE);
playPauseButton.setVisibility(VISIBLE);
controlToggle.displayQuick(progressAndPlay);
}
@ -435,5 +459,6 @@ public final class AudioView extends FrameLayout {
void onPause(@NonNull Uri audioUri);
void onSeekTo(@NonNull Uri audioUri, double progress);
void onStopAndReset(@NonNull Uri audioUri);
void onProgressUpdated(long durationMillis, long playheadMillis);
}
}

View file

@ -5,6 +5,7 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.view.View;
@ -15,18 +16,26 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieProperty;
import com.airbnb.lottie.model.KeyPath;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.dualsim.SubscriptionInfoCompat;
import org.thoughtcrime.securesms.util.dualsim.SubscriptionManagerCompat;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
public class ConversationItemFooter extends LinearLayout {
@ -36,6 +45,9 @@ public class ConversationItemFooter extends LinearLayout {
private ImageView insecureIndicatorView;
private DeliveryStatusView deliveryStatusView;
private boolean onlyShowSendingStatus;
private View audioSpace;
private TextView audioDuration;
private LottieAnimationView revealDot;
public ConversationItemFooter(Context context) {
super(context);
@ -60,11 +72,15 @@ public class ConversationItemFooter extends LinearLayout {
timerView = findViewById(R.id.footer_expiration_timer);
insecureIndicatorView = findViewById(R.id.footer_insecure_indicator);
deliveryStatusView = findViewById(R.id.footer_delivery_status);
audioDuration = findViewById(R.id.footer_audio_duration);
audioSpace = findViewById(R.id.footer_audio_duration_space);
revealDot = findViewById(R.id.footer_revealed_dot);
if (attrs != null) {
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.ConversationItemFooter, 0, 0);
setTextColor(typedArray.getInt(R.styleable.ConversationItemFooter_footer_text_color, getResources().getColor(R.color.core_white)));
setIconColor(typedArray.getInt(R.styleable.ConversationItemFooter_footer_icon_color, getResources().getColor(R.color.core_white)));
setRevealDotColor(typedArray.getInt(R.styleable.ConversationItemFooter_footer_reveal_dot_color, getResources().getColor(R.color.core_white)));
typedArray.recycle();
}
}
@ -81,11 +97,18 @@ public class ConversationItemFooter extends LinearLayout {
presentTimer(messageRecord);
presentInsecureIndicator(messageRecord);
presentDeliveryStatus(messageRecord);
hideAudioDurationViews();
}
public void setAudioDuration(long totalDurationMillis, long currentPostionMillis) {
long remainingSecs = TimeUnit.MILLISECONDS.toSeconds(totalDurationMillis - currentPostionMillis);
audioDuration.setText(getResources().getString(R.string.AudioView_duration, remainingSecs / 60, remainingSecs % 60));
}
public void setTextColor(int color) {
dateView.setTextColor(color);
simView.setTextColor(color);
audioDuration.setTextColor(color);
}
public void setIconColor(int color) {
@ -94,6 +117,14 @@ public class ConversationItemFooter extends LinearLayout {
deliveryStatusView.setTint(color);
}
public void setRevealDotColor(int color) {
revealDot.addValueCallback(
new KeyPath("**"),
LottieProperty.COLOR_FILTER,
frameInfo -> new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP)
);
}
public void setOnlyShowSendingStatus(boolean onlyShowSending, MessageRecord messageRecord) {
this.onlyShowSendingStatus = onlyShowSending;
presentDeliveryStatus(messageRecord);
@ -204,4 +235,64 @@ public class ConversationItemFooter extends LinearLayout {
}
}
}
private void presentAudioDuration(@NonNull MessageRecord messageRecord) {
if (messageRecord.isMms()) {
MmsMessageRecord mmsMessageRecord = (MmsMessageRecord) messageRecord;
if (mmsMessageRecord.getSlideDeck().getAudioSlide() != null) {
if (messageRecord.isOutgoing()) {
moveAudioViewsForOutgoing();
} else {
moveAudioViewsForIncoming();
}
showAudioDurationViews();
} else {
hideAudioDurationViews();
}
} else {
hideAudioDurationViews();
}
}
private void moveAudioViewsForOutgoing() {
removeView(audioSpace);
removeView(audioDuration);
removeView(revealDot);
addView(audioSpace, 0);
addView(revealDot, 0);
addView(audioDuration, 0);
int padStart = ViewUtil.dpToPx(60);
int padLeft = getLayoutDirection() == LAYOUT_DIRECTION_LTR ? padStart : 0;
int padRight = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? padStart : 0;
audioDuration.setPadding(padLeft, 0, padRight, 0);
}
private void moveAudioViewsForIncoming() {
removeView(audioSpace);
removeView(audioDuration);
removeView(revealDot);
addView(audioSpace);
addView(revealDot);
addView(audioDuration);
audioDuration.setPadding(0, 0, 0, 0);
}
private void showAudioDurationViews() {
audioSpace.setVisibility(View.VISIBLE);
audioDuration.setVisibility(View.VISIBLE);
if (FeatureFlags.viewedReceipts()) {
revealDot.setVisibility(View.VISIBLE);
}
}
private void hideAudioDurationViews() {
audioSpace.setVisibility(View.GONE);
audioDuration.setVisibility(View.GONE);
revealDot.setVisibility(View.GONE);
}
}

View file

@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.animation.Interpolator;
@ -13,6 +14,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import androidx.appcompat.widget.AppCompatSeekBar;
import androidx.core.content.ContextCompat;
import org.thoughtcrime.securesms.R;
@ -65,9 +67,12 @@ public final class WaveFormSeekBarView extends AppCompatSeekBar {
barWidth = getResources().getDimensionPixelSize(R.dimen.wave_form_bar_width);
}
public void setColors(@ColorInt int playedBarColor, @ColorInt int unplayedBarColor) {
public void setColors(@ColorInt int playedBarColor, @ColorInt int unplayedBarColor, @ColorInt int thumbTint) {
this.playedBarColor = playedBarColor;
this.unplayedBarColor = unplayedBarColor;
getThumb().setColorFilter(thumbTint, PorterDuff.Mode.SRC_IN);
invalidate();
}

View file

@ -88,6 +88,8 @@ final class VoiceNotePlaybackPreparer implements MediaSessionConnector.PlaybackP
@Override
public void onPrepareFromUri(final Uri uri, Bundle extras) {
Log.d(TAG, "onPrepareFromUri: " + uri);
long messageId = extras.getLong(VoiceNoteMediaController.EXTRA_MESSAGE_ID);
double progress = extras.getDouble(VoiceNoteMediaController.EXTRA_PROGRESS, 0);
boolean singlePlayback = extras.getBoolean(VoiceNoteMediaController.EXTRA_PLAY_SINGLE, false);

View file

@ -10,6 +10,7 @@ import android.os.Bundle;
import android.os.Process;
import android.os.RemoteException;
import android.support.v4.media.MediaBrowserCompat;
import android.support.v4.media.MediaDescriptionCompat;
import android.support.v4.media.session.MediaControllerCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
@ -162,7 +163,16 @@ public class VoiceNotePlaybackService extends MediaBrowserServiceCompat {
@Override
public void onPositionDiscontinuity(int reason) {
int currentWindowIndex = player.getCurrentWindowIndex();
int currentWindowIndex = player.getCurrentWindowIndex();
if (currentWindowIndex == C.INDEX_UNSET) {
return;
}
if (reason == Player.DISCONTINUITY_REASON_PERIOD_TRANSITION) {
MediaDescriptionCompat mediaDescriptionCompat = queueDataAdapter.getMediaDescription(currentWindowIndex);
Log.d(TAG, "onPositionDiscontinuity: current window uri: " + mediaDescriptionCompat.getMediaUri());
}
boolean isWithinThreshold = currentWindowIndex < LOAD_MORE_THRESHOLD ||
currentWindowIndex + LOAD_MORE_THRESHOLD >= queueDataAdapter.size();

View file

@ -93,6 +93,7 @@ import org.thoughtcrime.securesms.jobs.SmsSendJob;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.AudioSlide;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.ImageSlide;
import org.thoughtcrime.securesms.mms.PartAuthority;
@ -112,6 +113,7 @@ import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.InterceptableLongClickCopyLinkSpan;
import org.thoughtcrime.securesms.util.LongClickMovementMethod;
import org.thoughtcrime.securesms.util.MessageRecordUtil;
import org.thoughtcrime.securesms.util.SearchUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ThemeUtil;
@ -127,6 +129,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import static org.thoughtcrime.securesms.util.ThemeUtil.isDarkTheme;
@ -408,6 +411,7 @@ public class ConversationItem extends LinearLayout implements BindableConversati
}
cancelPulseOutlinerAnimation();
if (eventListener != null && audioViewStub.resolved()) {
Log.d(TAG, "unbind: unregistering voice note callbacks for audio slide " + audioViewStub.get().getAudioSlideUri());
eventListener.onUnregisterVoiceNoteCallbacks(audioViewStub.get().getPlaybackStateObserver());
}
}
@ -659,6 +663,7 @@ public class ConversationItem extends LinearLayout implements BindableConversati
boolean showControls = !messageRecord.isFailed();
if (eventListener != null && audioViewStub.resolved()) {
Log.d(TAG, "setMediaAttributes: unregistering voice note callbacks for audio slide " + audioViewStub.get().getAudioSlideUri());
eventListener.onUnregisterVoiceNoteCallbacks(audioViewStub.get().getPlaybackStateObserver());
}
@ -744,13 +749,15 @@ public class ConversationItem extends LinearLayout implements BindableConversati
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
if (revealableStub.resolved()) revealableStub.get().setVisibility(View.GONE);
//noinspection ConstantConditions
audioViewStub.get().setAudio(((MediaMmsMessageRecord) messageRecord).getSlideDeck().getAudioSlide(), new AudioViewCallbacks(), showControls);
audioViewStub.get().setAudio(Objects.requireNonNull(((MediaMmsMessageRecord) messageRecord).getSlideDeck().getAudioSlide()), new AudioViewCallbacks(), showControls, false);
audioViewStub.get().setDownloadClickListener(singleDownloadClickListener);
audioViewStub.get().setOnLongClickListener(passthroughClickListener);
if (eventListener != null) {
Log.d(TAG, "setMediaAttributes: registered listener for audio slide " + audioViewStub.get().getAudioSlideUri());
eventListener.onRegisterVoiceNoteCallbacks(audioViewStub.get().getPlaybackStateObserver());
} else {
Log.w(TAG, "setMediaAttributes: could not register listener for audio slide " + audioViewStub.get().getAudioSlideUri());
}
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
@ -1572,6 +1579,11 @@ public class ConversationItem extends LinearLayout implements BindableConversati
public void onStopAndReset(@NonNull Uri audioUri) {
throw new UnsupportedOperationException();
}
@Override
public void onProgressUpdated(long durationMillis, long playheadMillis) {
footer.setAudioDuration(durationMillis, playheadMillis);
}
}
private void handleMessageApproval() {

View file

@ -440,7 +440,7 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
long mmsId = Objects.requireNonNull(mediaRecord.getAttachment()).getMmsId();
audioItemListener.unregisterPlaybackStateObserver(audioView.getPlaybackStateObserver());
audioView.setAudio((AudioSlide) slide, new AudioViewCallbacksAdapter(audioItemListener, mmsId), true);
audioView.setAudio((AudioSlide) slide, new AudioViewCallbacksAdapter(audioItemListener, mmsId), true, true);
audioItemListener.registerPlaybackStateObserver(audioView.getPlaybackStateObserver());
audioView.setOnClickListener(view -> itemClickListener.onMediaClicked(mediaRecord));
@ -520,6 +520,10 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
public void onStopAndReset(@NonNull Uri audioUri) {
audioItemListener.onStopAndReset(audioUri);
}
@Override
public void onProgressUpdated(long durationMillis, long playheadMillis) {
}
}
interface ItemClickListener {

View file

@ -277,7 +277,7 @@ public class AttachmentManager {
attachmentViewStub.get().setVisibility(View.VISIBLE);
if (slide.hasAudio()) {
audioView.setAudio((AudioSlide) slide, null, false);
audioView.setAudio((AudioSlide) slide, null, false, false);
removableMediaView.display(audioView, false);
result.set(true);
} else if (slide.hasDocument()) {

View file

@ -59,6 +59,7 @@ public final class FeatureFlags {
private static final String CLIENT_EXPIRATION = "android.clientExpiration";
public static final String RESEARCH_MEGAPHONE_1 = "research.megaphone.1";
public static final String MODERN_PROFILE_SHARING = "android.modernProfileSharing";
private static final String VIEWED_RECEIPTS = "android.viewed.receipts";
/**
* We will only store remote values for flags in this set. If you want a flag to be controllable
@ -75,7 +76,8 @@ public final class FeatureFlags {
VERIFY_V2,
CLIENT_EXPIRATION,
RESEARCH_MEGAPHONE_1,
MODERN_PROFILE_SHARING
MODERN_PROFILE_SHARING,
VIEWED_RECEIPTS
);
/**
@ -243,6 +245,13 @@ public final class FeatureFlags {
return getBoolean(MODERN_PROFILE_SHARING, false);
}
/**
* Whether the user should display the content revealed dot in voice notes.
*/
public static boolean viewedReceipts() {
return getBoolean(VIEWED_RECEIPTS, false);
}
/** Only for rendering debug info. */
public static synchronized @NonNull Map<String, Object> getMemoryValues() {
return new TreeMap<>(REMOTE_VALUES);

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size android:height="6dp" android:width="6dp" />
<solid android:color="@color/white" />
</shape>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"
android:thickness="2dp"
android:useLevel="false">
<size android:width="2dp" android:height="28dp" />
<corners android:radius="30dp" />
<solid android:color="@color/white" />
</shape>

View file

@ -18,9 +18,10 @@
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:paddingStart="12dp"
android:paddingTop="10dp"
android:paddingTop="8dp"
android:paddingEnd="8dp"
android:paddingBottom="10dp"
android:paddingBottom="8dp"
android:thumb="@drawable/audio_wave_thumb"
tools:progress="50" />
<TextView

View file

@ -6,10 +6,12 @@
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:padding="2dp"
android:gravity="center"
tools:showIn="@layout/audio_view">
<FrameLayout
android:background="@drawable/circle_tintable"
android:id="@+id/progress_and_play"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -25,8 +27,8 @@
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/play"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="@drawable/circle_touch_highlight_background"
android:contentDescription="@string/audio_view__play_pause_accessibility_description"

View file

@ -1,65 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
<merge 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="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical|end"
android:orientation="horizontal"
tools:parentTag="org.thoughtcrime.securesms.components.ConversationItemFooter">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/footer_audio_duration"
style="@style/Signal.Text.Caption.MessageSent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
tools:text="0:00"
tools:visibility="visible" />
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/footer_revealed_dot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:visibility="gone"
app:lottie_rawRes="@raw/lottie_played"
tools:visibility="visible" />
<Space
android:id="@+id/footer_audio_duration_space"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:visibility="gone"
tools:visibility="visible" />
<TextView
android:id="@+id/footer_date"
android:autoLink="none"
style="@style/Signal.Text.Caption.MessageSent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoLink="none"
android:linksClickable="false"
style="@style/Signal.Text.Caption.MessageSent"
android:textColor="?conversation_item_sent_text_secondary_color"
tools:text="30m"/>
tools:text="30m" />
<org.thoughtcrime.securesms.components.ExpirationTimerView
android:id="@+id/footer_expiration_timer"
android:layout_marginStart="4dp"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_gravity="center_vertical"
android:layout_marginStart="4dp"
android:visibility="gone"
tools:visibility="visible"/>
tools:visibility="visible" />
<TextView
android:id="@+id/footer_sim_info"
android:autoLink="none"
style="@style/Signal.Text.Caption.MessageSent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="4dp"
android:autoLink="none"
android:ellipsize="end"
android:fontFamily="sans-serif-light"
android:linksClickable="false"
android:maxWidth="140dp"
android:maxLines="1"
android:ellipsize="end"
android:linksClickable="false"
style="@style/Signal.Text.Caption.MessageSent"
android:layout_gravity="center_vertical"
android:fontFamily="sans-serif-light"
android:visibility="gone"
tools:visibility="visible"
tools:text="to SIM1" />
tools:text="to SIM1"
tools:visibility="visible" />
<ImageView
android:id="@+id/footer_insecure_indicator"
android:layout_width="12dp"
android:layout_height="11dp"
android:layout_gravity="center_vertical"
android:layout_marginStart="4dp"
android:contentDescription="@string/conversation_item__secure_message_description"
android:src="@drawable/ic_unlocked_white_18dp"
android:visibility="gone"
android:layout_marginStart="4dp"
android:layout_gravity="center_vertical"
android:contentDescription="@string/conversation_item__secure_message_description"
tools:visibility="visible"/>
tools:visibility="visible" />
<org.thoughtcrime.securesms.components.DeliveryStatusView
android:id="@+id/footer_delivery_status"
android:layout_marginStart="4dp"
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:layout_marginStart="4dp" />
</merge>

View file

@ -11,4 +11,5 @@
app:backgroundTintColor="@color/blue_500"
app:waveformPlayedBarsColor="@color/core_white"
app:waveformUnplayedBarsColor="@color/transparent_white_40"
app:progressAndPlayTint="@color/transparent_white_20"
tools:visibility="visible"/>

View file

@ -203,6 +203,7 @@
android:clipChildren="false"
android:clipToPadding="false"
android:alpha="0.7"
app:footer_reveal_dot_color="@color/core_white"
app:footer_text_color="?conversation_item_received_text_secondary_color"
app:footer_icon_color="?conversation_item_received_text_secondary_color"/>

View file

@ -7,6 +7,8 @@
android:layout_height="wrap_content"
app:foregroundTintColor="@color/grey_500"
app:backgroundTintColor="@color/white"
app:waveformPlayedBarsColor="@color/core_ultramarine_light"
app:waveformUnplayedBarsColor="@color/core_grey_25"
app:waveformThumbTint="?attr/audio_seek_bar_sent_played_color"
app:waveformPlayedBarsColor="?attr/audio_seek_bar_sent_played_color"
app:waveformUnplayedBarsColor="?attr/audio_seek_bar_sent_unplayed_color"
app:progressAndPlayTint="?attr/audio_play_pause_sent_background_tint"
android:visibility="gone"/>

View file

@ -140,6 +140,7 @@
android:clipChildren="false"
android:clipToPadding="false"
android:gravity="end"
app:footer_reveal_dot_color="?attr/conversation_footer_sent_reveal_dot_color"
app:footer_icon_color="?attr/conversation_item_sent_icon_color"
app:footer_text_color="?attr/conversation_item_sent_text_secondary_color" />

View file

@ -0,0 +1 @@
{"v":"5.7.1","fr":29.9700012207031,"ip":0,"op":40.0000016292334,"w":16,"h":16,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[8,8,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[37.5,37.5,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":18,"s":[56.25,56.25,100]},{"t":29.0000011811942,"s":[0,0,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16.002,16.002],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0.032,0.001],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":900.000036657751,"st":0,"bm":0}],"markers":[{"tm":63.0000025660426,"cm":"1","dr":0}]}

View file

@ -11,6 +11,4 @@
<dimen name="album_5_total_height">250dp</dimen>
<dimen name="album_5_cell_size_big">149dp</dimen>
<dimen name="album_5_cell_size_small">99dp</dimen>
<dimen name="message_audio_width">260dp</dimen>
</resources>

View file

@ -94,6 +94,7 @@
<attr name="conversation_number_picker_text_color_selected" format="reference|color"/>
<attr name="conversation_sticker_footer_text_color" format="reference|color"/>
<attr name="conversation_sticker_footer_icon_color" format="reference|color"/>
<attr name="conversation_footer_sent_reveal_dot_color" format="color" />
<attr name="conversation_sticker_author_color" format="reference|color"/>
<attr name="conversation_popup_theme" format="reference"/>
<attr name="conversation_title_color" format="reference" />
@ -141,6 +142,10 @@
<attr name="compose_icon_tint" />
<attr name="audio_seek_bar_sent_played_color" format="color" />
<attr name="audio_seek_bar_sent_unplayed_color" format="color" />
<attr name="audio_play_pause_sent_background_tint" format="color" />
<attr name="conversation_item_background" format="reference"/>
<attr name="conversation_item_bubble_background" format="reference|color"/>
<attr name="conversation_item_sent_text_primary_color" format="reference|color"/>
@ -387,8 +392,10 @@
<declare-styleable name="AudioView">
<attr name="foregroundTintColor" format="color" />
<attr name="backgroundTintColor" format="color" />
<attr name="waveformThumbTint" format="color" />
<attr name="waveformPlayedBarsColor" format="color" />
<attr name="waveformUnplayedBarsColor" format="color" />
<attr name="progressAndPlayTint" format="color" />
<attr name="small" format="boolean" />
<attr name="autoRewind" format="boolean" />
</declare-styleable>
@ -486,6 +493,7 @@
<declare-styleable name="ConversationItemFooter">
<attr name="footer_text_color" format="color" />
<attr name="footer_icon_color" format="color" />
<attr name="footer_reveal_dot_color" format="color" />
</declare-styleable>
<declare-styleable name="ConversationItemThumbnail">

View file

@ -47,7 +47,7 @@
<dimen name="media_bubble_min_height">100dp</dimen>
<dimen name="media_bubble_max_height">320dp</dimen>
<dimen name="media_bubble_sticker_dimens">175dp</dimen>
<dimen name="message_audio_width">210dp</dimen>
<dimen name="message_audio_width">242dp</dimen>
<dimen name="media_picker_folder_width">175dp</dimen>
<dimen name="media_picker_item_width">85dp</dimen>

View file

@ -1505,7 +1505,7 @@
<string name="ViewOnceMessageActivity_video_duration" translatable="false">%1$02d:%2$02d</string>
<!-- AudioView -->
<string name="AudioView_duration" translatable="false">%1$02d:%2$02d</string>
<string name="AudioView_duration" translatable="false">%1$d:%2$02d</string>
<!-- MessageDisplayHelper -->
<string name="MessageDisplayHelper_bad_encrypted_message">Bad encrypted message</string>

View file

@ -167,6 +167,10 @@
<item name="backup_enable_dialog_divider_background">@color/core_grey_20</item>
<item name="backup_enable_subhead_color">@color/core_grey_65</item>
<item name="audio_seek_bar_sent_played_color">@color/core_grey_60</item>
<item name="audio_seek_bar_sent_unplayed_color">@color/core_grey_25</item>
<item name="audio_play_pause_sent_background_tint">@color/core_white</item>
<item name="insight_modal_background">@drawable/insights_modal_background</item>
<item name="insight_modal_button_background">@color/core_grey_10</item>
<item name="insight_title">@color/core_grey_90</item>
@ -272,6 +276,7 @@
<item name="conversation_number_picker_text_color_selected">@color/black</item>
<item name="conversation_sticker_footer_text_color">@color/core_grey_60</item>
<item name="conversation_sticker_footer_icon_color">@color/core_grey_60</item>
<item name="conversation_footer_sent_reveal_dot_color">@color/core_grey_60</item>
<item name="conversation_sticker_author_color">@color/core_grey_95</item>
<item name="conversation_popup_theme">@style/ThemeOverlay.AppCompat.Light</item>
<item name="conversation_title_color">@color/white</item>
@ -502,6 +507,10 @@
<item name="backup_enable_dialog_divider_background">@color/core_grey_60</item>
<item name="backup_enable_subhead_color">@color/core_grey_25</item>
<item name="audio_seek_bar_sent_played_color">@color/core_grey_15</item>
<item name="audio_seek_bar_sent_unplayed_color">@color/core_grey_60</item>
<item name="audio_play_pause_sent_background_tint">@color/core_grey_60</item>
<item name="insight_modal_background">@drawable/insights_modal_background_dark</item>
<item name="insight_modal_button_background">@color/core_grey_60</item>
<item name="insight_title">@color/core_grey_25</item>
@ -661,6 +670,7 @@
<item name="conversation_number_picker_text_color_selected">@color/white</item>
<item name="conversation_sticker_footer_text_color">@color/core_grey_25</item>
<item name="conversation_sticker_footer_icon_color">@color/core_grey_25</item>
<item name="conversation_footer_sent_reveal_dot_color">@color/core_grey_25</item>
<item name="conversation_sticker_author_color">@color/core_grey_05</item>
<item name="conversation_popup_theme">@style/ThemeOverlay.AppCompat.Dark</item>
<item name="conversation_title_color">@color/transparent_white_90</item>