Merge remote-tracking branch 'upstream/main'

This commit is contained in:
adbenitez
2024-03-27 12:38:14 -04:00
20 changed files with 101 additions and 145 deletions
+3
View File
@@ -40,6 +40,9 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<!-- force compiling emojipicker on sdk<21; we'll add a runtime switch to not use the picker in this case -->
<uses-sdk tools:overrideLibrary="androidx.emoji2.emojipicker"/>
<application android:name=".ApplicationContext"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
+2
View File
@@ -36,6 +36,8 @@ dependencies {
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.6.2'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2'
implementation 'androidx.work:work-runtime:2.8.1'
implementation 'androidx.emoji2:emoji2-emojipicker:1.4.0'
implementation 'com.google.guava:guava:29.0-android'
implementation 'com.google.android.exoplayer:exoplayer-core:2.9.6' // plays video and audio
implementation 'com.google.android.exoplayer:exoplayer-ui:2.9.6'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
+7
View File
@@ -113,6 +113,13 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/AddReaction"/>
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/reaction_any"
android:text="•••"
android:textColor="?attr/pref_icon_tint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/AddReaction"/>
</org.thoughtcrime.securesms.reactions.AddReactionView>
</FrameLayout>
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.emoji2.emojipicker.EmojiPickerView
android:id="@+id/emoji_picker"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:emojiGridColumns="8"/>
</RelativeLayout>
@@ -75,9 +75,7 @@ public abstract class BaseActionBarActivity extends AppCompatActivity {
}
private void initializeScreenshotSecurity() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH &&
Prefs.isScreenSecurityEnabled(this))
{
if (Prefs.isScreenSecurityEnabled(this)) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
}
}
@@ -23,8 +23,6 @@ public abstract class BaseActivity extends FragmentActivity {
}
public static boolean isMenuWorkaroundRequired() {
return VERSION.SDK_INT < VERSION_CODES.KITKAT &&
VERSION.SDK_INT > VERSION_CODES.GINGERBREAD_MR1 &&
("LGE".equalsIgnoreCase(Build.MANUFACTURER) || "E6710".equalsIgnoreCase(Build.DEVICE));
return VERSION.SDK_INT < VERSION_CODES.KITKAT && ("LGE".equalsIgnoreCase(Build.MANUFACTURER) || "E6710".equalsIgnoreCase(Build.DEVICE));
}
}
@@ -917,13 +917,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
recipient = new Recipient(this, dcChat);
glideRequests = GlideApp.with(this);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
LinearLayout conversationContainer = ViewUtil.findById(this, R.id.conversation_container);
conversationContainer.setClipChildren(true);
conversationContainer.setClipToPadding(true);
}
setComposePanelVisibility();
initializeContactRequest();
}
@@ -138,7 +138,6 @@ public class ConversationFragment extends MessageSelectorFragment
}, 60 * 1000, 60 * 1000);
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle bundle) {
final View view = inflater.inflate(R.layout.conversation_fragment, container, false);
@@ -142,11 +142,8 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
}
@TargetApi(VERSION_CODES.JELLY_BEAN)
private void setFullscreenIfPossible() {
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);
}
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);
}
@Override
@@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.audio;
import android.annotation.TargetApi;
import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioRecord;
@@ -8,7 +7,6 @@ import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaRecorder;
import android.os.Build;
import android.util.Log;
import org.thoughtcrime.securesms.util.Prefs;
@@ -18,7 +16,6 @@ import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public class AudioCodec {
private static final String TAG = AudioCodec.class.getSimpleName();
@@ -1,9 +1,7 @@
package org.thoughtcrime.securesms.audio;
import android.annotation.TargetApi;
import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import androidx.annotation.NonNull;
import android.util.Log;
@@ -20,7 +18,6 @@ import org.thoughtcrime.securesms.util.Util;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public class AudioRecorder {
private static final String TAG = AudioRecorder.class.getSimpleName();
@@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.components;
import android.Manifest;
import android.animation.Animator;
import android.annotation.TargetApi;
import android.app.Activity;
@@ -96,10 +95,6 @@ public class AttachmentTypeSelector extends PopupWindow {
setLocationButtonImage(context);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
ViewUtil.findById(layout, R.id.location_linear_layout).setVisibility(View.INVISIBLE);
}
setContentView(layout);
setWidth(LinearLayout.LayoutParams.MATCH_PARENT);
setHeight(LinearLayout.LayoutParams.WRAP_CONTENT);
@@ -203,9 +203,7 @@ public class AudioView extends FrameLayout implements AudioSlidePlayer.Listener
this.seekBar.getProgressDrawable().setColorFilter(foregroundTint, PorterDuff.Mode.SRC_IN);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
this.seekBar.getThumb().setColorFilter(foregroundTint, PorterDuff.Mode.SRC_IN);
}
this.seekBar.getThumb().setColorFilter(foregroundTint, PorterDuff.Mode.SRC_IN);
}
public void getSeekBarGlobalVisibleRect(@NonNull Rect rect) {
@@ -106,11 +106,6 @@ public class InputPanel extends ConstraintLayout
this.recordLockCancel.setOnClickListener(v -> microphoneRecorderView.cancelAction());
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
this.microphoneRecorderView.setVisibility(View.GONE);
this.microphoneRecorderView.setClickable(false);
}
if (Prefs.isSystemEmojiPreferred(getContext())) {
mediaKeyboard.setVisibility(View.GONE);
emojiVisible = false;
@@ -58,13 +58,10 @@ public class RepeatableImageKey extends ImageButton {
}
private class Repeater implements Runnable {
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
@Override
public void run() {
notifyListener();
postDelayed(this, VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1
? ViewConfiguration.getKeyRepeatDelay()
: 50);
postDelayed(this, ViewConfiguration.getKeyRepeatDelay());
}
}
@@ -75,14 +72,11 @@ public class RepeatableImageKey extends ImageButton {
this.repeater = new Repeater();
}
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
view.postDelayed(repeater, VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1
? ViewConfiguration.getKeyRepeatTimeout()
: ViewConfiguration.getLongPressTimeout());
view.postDelayed(repeater, ViewConfiguration.getKeyRepeatTimeout());
performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
return false;
case MotionEvent.ACTION_CANCEL:
@@ -231,17 +231,15 @@ public class EmojiProvider {
paint);
}
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public void setBitmap(Bitmap bitmap) {
setBitmap(bitmap, false);
}
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public void setBitmap(Bitmap bitmap, boolean background) {
if (!background) {
Util.assertMainThread();
}
if (VERSION.SDK_INT < VERSION_CODES.HONEYCOMB_MR1 || bmp == null || !bmp.sameAs(bitmap)) {
if (bmp == null || !bmp.sameAs(bitmap)) {
bmp = bitmap;
invalidateSelf();
}
@@ -100,9 +100,7 @@ public class KeepAliveService extends Service {
builder.setContentTitle(getString(R.string.app_name));
builder.setContentText(getString(R.string.notify_background_connection_enabled));
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN ) {
builder.setPriority(NotificationCompat.PRIORITY_MIN);
}
builder.setPriority(NotificationCompat.PRIORITY_MIN);
builder.setWhen(0);
builder.setContentIntent(contentIntent);
builder.setSmallIcon(R.drawable.notification_permanent);
@@ -1,11 +1,14 @@
package org.thoughtcrime.securesms.reactions;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.emoji2.emojipicker.EmojiPickerView;
import com.b44t.messenger.DcContact;
import com.b44t.messenger.DcContext;
@@ -16,13 +19,14 @@ import com.b44t.messenger.rpc.Rpc;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.util.Prefs;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.util.Map;
public class AddReactionView extends LinearLayout {
private EmojiTextView [] defaultReactionViews;
private EmojiTextView anyReactionView;
private boolean anyReactionClearsReaction;
private Context context;
private DcContext dcContext;
private Rpc rpc;
@@ -51,7 +55,12 @@ public class AddReactionView extends LinearLayout {
};
for (int i = 0; i < defaultReactionViews.length; i++) {
final int ii = i;
defaultReactionViews[i].setOnClickListener(v -> reactionClicked(ii));
defaultReactionViews[i].setOnClickListener(v -> defaultReactionClicked(ii));
}
anyReactionView = findViewById(R.id.reaction_any);
anyReactionView.setOnClickListener(v -> anyReactionClicked());
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
anyReactionView.setVisibility(View.GONE); // EmojiPickerView requires SDK 21 or newer
}
}
}
@@ -69,14 +78,26 @@ public class AddReactionView extends LinearLayout {
this.listener = listener;
final String existingReaction = getSelfReaction();
boolean existingHilited = false;
for (EmojiTextView defaultReactionView : defaultReactionViews) {
if (defaultReactionView.getText().toString().equals(existingReaction)) {
defaultReactionView.setBackground(ContextCompat.getDrawable(context, R.drawable.reaction_pill_background_selected));
existingHilited = true;
} else {
defaultReactionView.setBackground(null);
}
}
if (existingReaction != null && !existingHilited) {
anyReactionView.setText(existingReaction);
anyReactionView.setBackground(ContextCompat.getDrawable(context, R.drawable.reaction_pill_background_selected));
anyReactionClearsReaction = true;
} else {
anyReactionView.setText("•••");
anyReactionView.setBackground(null);
anyReactionClearsReaction = false;
}
final int offset = (int)(this.getHeight() * 0.666);
int x = (int)parentView.getX();
if (msgToReactTo.isOutgoing()) {
@@ -117,17 +138,48 @@ public class AddReactionView extends LinearLayout {
return result;
}
private void reactionClicked(int i) {
private void defaultReactionClicked(int i) {
final String reaction = defaultReactionViews[i].getText().toString();
sendReaction(reaction);
if (listener != null) {
listener.onShallHide();
}
}
private void anyReactionClicked() {
if (anyReactionClearsReaction) {
sendReaction(null);
} else {
View pickerLayout = View.inflate(context, R.layout.reaction_picker, null);
final AlertDialog alertDialog = new AlertDialog.Builder(context)
.setView(pickerLayout)
.setTitle(R.string.react)
.setPositiveButton(R.string.cancel, null)
.create();
EmojiPickerView pickerView = ViewUtil.findById(pickerLayout, R.id.emoji_picker);
pickerView.setOnEmojiPickedListener((it) -> {
sendReaction(it.getEmoji());
alertDialog.dismiss();
});
alertDialog.show();
}
if (listener != null) {
listener.onShallHide();
}
}
private void sendReaction(final String reaction) {
try {
final String reaction = defaultReactionViews[i].getText().toString();
if (reaction.equals(getSelfReaction())) {
if (reaction == null || reaction.equals(getSelfReaction())) {
rpc.sendReaction(dcContext.getAccountId(), msgToReactTo.getId(), "");
} else {
rpc.sendReaction(dcContext.getAccountId(), msgToReactTo.getId(), reaction);
}
if (listener != null) {
listener.onShallHide();
}
} catch(Exception e) {
e.printStackTrace();
}
@@ -49,47 +49,19 @@ import org.thoughtcrime.securesms.util.views.Stub;
public class ViewUtil {
@SuppressWarnings("deprecation")
public static void setBackground(final @NonNull View v, final @Nullable Drawable drawable) {
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
v.setBackground(drawable);
} else {
v.setBackgroundDrawable(drawable);
}
}
public static void setY(final @NonNull View v, final int y) {
if (VERSION.SDK_INT >= 11) {
ViewCompat.setY(v, y);
} else {
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams)v.getLayoutParams();
params.topMargin = y;
v.setLayoutParams(params);
}
v.setBackground(drawable);
}
public static float getY(final @NonNull View v) {
if (VERSION.SDK_INT >= 11) {
return ViewCompat.getY(v);
} else {
return ((ViewGroup.MarginLayoutParams)v.getLayoutParams()).topMargin;
}
return ViewCompat.getY(v);
}
public static void setX(final @NonNull View v, final int x) {
if (VERSION.SDK_INT >= 11) {
ViewCompat.setX(v, x);
} else {
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams)v.getLayoutParams();
params.leftMargin = x;
v.setLayoutParams(params);
}
ViewCompat.setX(v, x);
}
public static float getX(final @NonNull View v) {
if (VERSION.SDK_INT >= 11) {
return ViewCompat.getX(v);
} else {
return ((LayoutParams)v.getLayoutParams()).leftMargin;
}
return ViewCompat.getX(v);
}
public static void swapChildInPlace(ViewGroup parent, View toRemove, View toAdd, int defaultIndex) {
@@ -17,17 +17,12 @@
package org.thoughtcrime.securesms.video;
import android.content.Context;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.MediaController;
import android.widget.Toast;
import android.widget.VideoView;
import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.ExoPlayerFactory;
@@ -50,7 +45,6 @@ import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.AttachmentServer;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.mms.VideoSlide;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.video.exo.AttachmentDataSourceFactory;
@@ -61,7 +55,6 @@ public class VideoPlayer extends FrameLayout {
private static final String TAG = VideoPlayer.class.getName();
@Nullable private final VideoView videoView;
@Nullable private final SimpleExoPlayerView exoView;
@Nullable private SimpleExoPlayer exoPlayer;
@@ -81,27 +74,17 @@ public class VideoPlayer extends FrameLayout {
inflate(context, R.layout.video_player, this);
if (Build.VERSION.SDK_INT >= 16) {
this.exoView = ViewUtil.findById(this, R.id.video_view);
this.videoView = null;
} else {
this.videoView = ViewUtil.findById(this, R.id.video_view);
this.exoView = null;
initializeVideoViewControls(videoView);
}
this.exoView = ViewUtil.findById(this, R.id.video_view);
}
public void setVideoSource(@NonNull VideoSlide videoSource, boolean autoplay)
throws IOException
{
if (Build.VERSION.SDK_INT >= 16) setExoViewSource(videoSource, autoplay);
else setVideoViewSource(videoSource, autoplay);
setExoViewSource(videoSource, autoplay);
}
public void pause() {
if (this.attachmentServer != null && this.videoView != null) {
this.videoView.stopPlayback();
} else if (this.exoPlayer != null) {
if (this.exoPlayer != null) {
this.exoPlayer.setPlayWhenReady(false);
}
}
@@ -143,40 +126,6 @@ public class VideoPlayer extends FrameLayout {
exoPlayer.setPlayWhenReady(autoplay);
}
private void setVideoViewSource(@NonNull VideoSlide videoSource, boolean autoplay)
throws IOException
{
if (this.attachmentServer != null) {
this.attachmentServer.stop();
}
if (videoSource.getUri() != null && PartAuthority.isLocalUri(videoSource.getUri())) {
Log.w(TAG, "Starting video attachment server for part provider Uri...");
this.attachmentServer = new AttachmentServer(getContext(), videoSource.asAttachment());
this.attachmentServer.start();
//noinspection ConstantConditions
this.videoView.setVideoURI(this.attachmentServer.getUri());
} else if (videoSource.getUri() != null) {
Log.w(TAG, "Playing video directly from non-local Uri...");
//noinspection ConstantConditions
this.videoView.setVideoURI(videoSource.getUri());
} else {
Toast.makeText(getContext(), getContext().getString(R.string.error), Toast.LENGTH_LONG).show();
return;
}
if (autoplay) this.videoView.start();
}
private void initializeVideoViewControls(@NonNull VideoView videoView) {
MediaController mediaController = new MediaController(getContext());
mediaController.setAnchorView(videoView);
mediaController.setMediaPlayer(videoView);
videoView.setMediaController(mediaController);
}
private static class ExoPlayerListener implements Player.EventListener {
private final Window window;