diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/AudioView.java b/app/src/main/java/org/thoughtcrime/securesms/components/AudioView.java index d5f48e0357..34e9f22f79 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/AudioView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/AudioView.java @@ -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); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java index 683d668177..42f3e33ddb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ConversationItemFooter.java @@ -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); + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/WaveFormSeekBarView.java b/app/src/main/java/org/thoughtcrime/securesms/components/WaveFormSeekBarView.java index 8f96ce42e2..1caade0782 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/WaveFormSeekBarView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/WaveFormSeekBarView.java @@ -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(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackPreparer.java b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackPreparer.java index dc698136fb..532534afca 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackPreparer.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackPreparer.java @@ -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); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService.java b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService.java index 8db03d3cd7..0736a1be21 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackService.java @@ -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(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java index 6197a60301..8519bcf393 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java @@ -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() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaGalleryAllAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaGalleryAllAdapter.java index 468ae751a9..430b4c0381 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaGalleryAllAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaGalleryAllAdapter.java @@ -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 { diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java b/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java index 10989189b3..fffb47332a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java @@ -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()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java index 947e1d328e..4fff992027 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java @@ -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 getMemoryValues() { return new TreeMap<>(REMOTE_VALUES); diff --git a/app/src/main/res/drawable/audio_read_indicator_circle.xml b/app/src/main/res/drawable/audio_read_indicator_circle.xml new file mode 100644 index 0000000000..dbfdf4f31d --- /dev/null +++ b/app/src/main/res/drawable/audio_read_indicator_circle.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/drawable/audio_wave_thumb.xml b/app/src/main/res/drawable/audio_wave_thumb.xml new file mode 100644 index 0000000000..2ca27e6afa --- /dev/null +++ b/app/src/main/res/drawable/audio_wave_thumb.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/audio_view.xml b/app/src/main/res/layout/audio_view.xml index 77da883d1c..6abd4a1ee4 100644 --- a/app/src/main/res/layout/audio_view.xml +++ b/app/src/main/res/layout/audio_view.xml @@ -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" /> @@ -25,8 +27,8 @@ - + + + + + + + tools:text="30m" /> + tools:visibility="visible" /> + tools:text="to SIM1" + tools:visibility="visible" /> + tools:visibility="visible" /> + android:layout_height="match_parent" + android:layout_gravity="center_vertical" + android:layout_marginStart="4dp" /> diff --git a/app/src/main/res/layout/conversation_item_received_audio.xml b/app/src/main/res/layout/conversation_item_received_audio.xml index df968566bb..68bd0a9d57 100644 --- a/app/src/main/res/layout/conversation_item_received_audio.xml +++ b/app/src/main/res/layout/conversation_item_received_audio.xml @@ -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"/> diff --git a/app/src/main/res/layout/conversation_item_received_multimedia.xml b/app/src/main/res/layout/conversation_item_received_multimedia.xml index 22ef259d5a..9593e0578b 100644 --- a/app/src/main/res/layout/conversation_item_received_multimedia.xml +++ b/app/src/main/res/layout/conversation_item_received_multimedia.xml @@ -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"/> diff --git a/app/src/main/res/layout/conversation_item_sent_audio.xml b/app/src/main/res/layout/conversation_item_sent_audio.xml index 8436056ba8..5d346cffab 100644 --- a/app/src/main/res/layout/conversation_item_sent_audio.xml +++ b/app/src/main/res/layout/conversation_item_sent_audio.xml @@ -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"/> diff --git a/app/src/main/res/layout/conversation_item_sent_multimedia.xml b/app/src/main/res/layout/conversation_item_sent_multimedia.xml index 19fd00c18c..55fa3ee326 100644 --- a/app/src/main/res/layout/conversation_item_sent_multimedia.xml +++ b/app/src/main/res/layout/conversation_item_sent_multimedia.xml @@ -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" /> diff --git a/app/src/main/res/raw/lottie_played.json b/app/src/main/res/raw/lottie_played.json new file mode 100644 index 0000000000..b7057ae9e0 --- /dev/null +++ b/app/src/main/res/raw/lottie_played.json @@ -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}]} \ No newline at end of file diff --git a/app/src/main/res/values-sw400dp/dimens.xml b/app/src/main/res/values-sw400dp/dimens.xml index c8b3912b9f..aee99d8491 100644 --- a/app/src/main/res/values-sw400dp/dimens.xml +++ b/app/src/main/res/values-sw400dp/dimens.xml @@ -11,6 +11,4 @@ 250dp 149dp 99dp - - 260dp \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index ba2e02b636..19d34d5329 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -94,6 +94,7 @@ + @@ -141,6 +142,10 @@ + + + + @@ -387,8 +392,10 @@ + + @@ -486,6 +493,7 @@ + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 0e65f9998e..8a47253288 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -47,7 +47,7 @@ 100dp 320dp 175dp - 210dp + 242dp 175dp 85dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 31bca907db..625f4937c3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1505,7 +1505,7 @@ %1$02d:%2$02d - %1$02d:%2$02d + %1$d:%2$02d Bad encrypted message diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index b806e89f03..a54b73a2c5 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -167,6 +167,10 @@ @color/core_grey_20 @color/core_grey_65 + @color/core_grey_60 + @color/core_grey_25 + @color/core_white + @drawable/insights_modal_background @color/core_grey_10 @color/core_grey_90 @@ -272,6 +276,7 @@ @color/black @color/core_grey_60 @color/core_grey_60 + @color/core_grey_60 @color/core_grey_95 @style/ThemeOverlay.AppCompat.Light @color/white @@ -502,6 +507,10 @@ @color/core_grey_60 @color/core_grey_25 + @color/core_grey_15 + @color/core_grey_60 + @color/core_grey_60 + @drawable/insights_modal_background_dark @color/core_grey_60 @color/core_grey_25 @@ -661,6 +670,7 @@ @color/white @color/core_grey_25 @color/core_grey_25 + @color/core_grey_25 @color/core_grey_05 @style/ThemeOverlay.AppCompat.Dark @color/transparent_white_90