mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-23 10:05:15 +00:00
Bumping min sdk to 26 and further cleaning version checks
This commit is contained in:
parent
a585e9c091
commit
088368769d
@ -4,12 +4,8 @@ import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import org.session.libsession.utilities.TextSecurePreferences;
|
||||
import org.session.libsession.utilities.dynamiclanguage.DynamicLanguageActivityHelper;
|
||||
|
@ -17,7 +17,6 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
@ -26,7 +25,6 @@ import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
@ -57,6 +55,9 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.RequestManager;
|
||||
|
||||
import org.session.libsession.messaging.messages.control.DataExtractionNotification;
|
||||
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment;
|
||||
@ -72,8 +73,6 @@ import org.thoughtcrime.securesms.database.loaders.PagingMediaLoader;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.mediapreview.MediaPreviewViewModel;
|
||||
import org.thoughtcrime.securesms.mediapreview.MediaRailAdapter;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.RequestManager;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.util.AttachmentUtil;
|
||||
|
@ -2,15 +2,10 @@ package org.thoughtcrime.securesms.components;
|
||||
|
||||
import android.Manifest;
|
||||
import android.animation.Animator;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import android.util.Pair;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
@ -26,8 +21,12 @@ import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.PopupWindow;
|
||||
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
|
||||
import org.session.libsession.utilities.ViewUtil;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
|
||||
@ -172,7 +171,6 @@ public class AttachmentTypeSelector extends PopupWindow {
|
||||
button.startAnimation(animation);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private void animateWindowInCircular(@Nullable View anchor, @NonNull View contentView) {
|
||||
Pair<Integer, Integer> coordinates = getClickOrigin(anchor, contentView);
|
||||
Animator animator = ViewAnimationUtils.createCircularReveal(contentView,
|
||||
@ -191,7 +189,6 @@ public class AttachmentTypeSelector extends PopupWindow {
|
||||
getContentView().startAnimation(animation);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private void animateWindowOutCircular(@Nullable View anchor, @NonNull View contentView) {
|
||||
Pair<Integer, Integer> coordinates = getClickOrigin(anchor, contentView);
|
||||
Animator animator = ViewAnimationUtils.createCircularReveal(getContentView(),
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.thoughtcrime.securesms.components;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.InputType;
|
||||
import android.text.Spannable;
|
||||
@ -16,15 +15,14 @@ import android.view.inputmethod.InputConnection;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.os.BuildCompat;
|
||||
import androidx.core.view.inputmethod.EditorInfoCompat;
|
||||
import androidx.core.view.inputmethod.InputConnectionCompat;
|
||||
import androidx.core.view.inputmethod.InputContentInfoCompat;
|
||||
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiEditText;
|
||||
import org.session.libsignal.utilities.Log;
|
||||
import org.session.libsession.utilities.TextSecurePreferences;
|
||||
import org.session.libsignal.utilities.Log;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiEditText;
|
||||
|
||||
public class ComposeText extends EmojiEditText {
|
||||
|
||||
@ -153,7 +151,6 @@ public class ComposeText extends EmojiEditText {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB_MR2)
|
||||
private static class CommitContentListener implements InputConnectionCompat.OnCommitContentListener {
|
||||
|
||||
private static final String TAG = CommitContentListener.class.getSimpleName();
|
||||
|
@ -1,9 +1,7 @@
|
||||
package org.thoughtcrime.securesms.components;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
@ -27,7 +25,6 @@ public class ConversationItemAlertView extends LinearLayout {
|
||||
initialize(attrs);
|
||||
}
|
||||
|
||||
@TargetApi(VERSION_CODES.HONEYCOMB)
|
||||
public ConversationItemAlertView(final Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
initialize(attrs);
|
||||
|
@ -1,9 +1,6 @@
|
||||
package org.thoughtcrime.securesms.components;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.view.animation.Animation;
|
||||
@ -11,6 +8,8 @@ import android.view.animation.AnimationSet;
|
||||
import android.view.animation.ScaleAnimation;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
||||
|
||||
public class HidingLinearLayout extends LinearLayout {
|
||||
|
||||
public HidingLinearLayout(Context context) {
|
||||
@ -21,7 +20,6 @@ public class HidingLinearLayout extends LinearLayout {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public HidingLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
@ -1,13 +1,12 @@
|
||||
package org.thoughtcrime.securesms.components;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class InputPanel extends LinearLayout {
|
||||
|
||||
public InputPanel(Context context) {
|
||||
@ -18,7 +17,6 @@ public class InputPanel extends LinearLayout {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public InputPanel(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
@ -16,26 +16,25 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms.components;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.preference.PreferenceManager;
|
||||
import androidx.appcompat.widget.LinearLayoutCompat;
|
||||
import android.util.AttributeSet;
|
||||
import org.session.libsignal.utilities.Log;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
import androidx.appcompat.widget.LinearLayoutCompat;
|
||||
|
||||
import org.session.libsession.utilities.ServiceUtil;
|
||||
import org.session.libsession.utilities.Util;
|
||||
import org.session.libsignal.utilities.Log;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
|
||||
/**
|
||||
* LinearLayout that, when a view container, will report back when it thinks a soft keyboard
|
||||
* has been opened and what its height would be.
|
||||
@ -95,7 +94,7 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
|
||||
}
|
||||
|
||||
private void updateKeyboardState() {
|
||||
if (viewInset == 0 && Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) viewInset = getViewInset();
|
||||
if (viewInset == 0) viewInset = getViewInset();
|
||||
|
||||
getWindowVisibleDisplayFrame(rect);
|
||||
|
||||
@ -118,7 +117,6 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(VERSION_CODES.LOLLIPOP)
|
||||
private int getViewInset() {
|
||||
try {
|
||||
Field attachInfoField = View.class.getDeclaredField("mAttachInfo");
|
||||
|
@ -3,24 +3,24 @@ package org.thoughtcrime.securesms.components;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.bumptech.glide.RequestManager;
|
||||
|
||||
import org.session.libsession.utilities.Stub;
|
||||
import org.thoughtcrime.securesms.mms.VideoSlide;
|
||||
import org.thoughtcrime.securesms.video.VideoPlayer;
|
||||
|
||||
import org.session.libsession.utilities.Stub;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
|
||||
public class MediaView extends FrameLayout {
|
||||
|
||||
private ZoomingImageView imageView;
|
||||
@ -41,12 +41,6 @@ public class MediaView extends FrameLayout {
|
||||
initialize();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public MediaView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
initialize();
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
inflate(getContext(), R.layout.media_view, this);
|
||||
|
||||
|
@ -1,19 +1,11 @@
|
||||
package org.thoughtcrime.securesms.components;
|
||||
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.MediaStore;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.Loader;
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@ -21,16 +13,24 @@ import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.Loader;
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.Key;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.signature.MediaStoreSignature;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
import org.session.libsession.utilities.ViewUtil;
|
||||
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
|
||||
import org.thoughtcrime.securesms.database.loaders.RecentPhotosLoader;
|
||||
import com.bumptech.glide.Glide;
|
||||
|
||||
import org.session.libsession.utilities.ViewUtil;
|
||||
import network.loki.messenger.R;
|
||||
|
||||
public class RecentPhotoViewRail extends FrameLayout implements LoaderManager.LoaderCallbacks<Cursor> {
|
||||
|
||||
@ -130,14 +130,12 @@ public class RecentPhotoViewRail extends FrameLayout implements LoaderManager.Lo
|
||||
|
||||
}
|
||||
|
||||
@TargetApi(16)
|
||||
@SuppressWarnings("SuspiciousNameCombination")
|
||||
private String getWidthColumn(int orientation) {
|
||||
if (orientation == 0 || orientation == 180) return MediaStore.Images.ImageColumns.WIDTH;
|
||||
else return MediaStore.Images.ImageColumns.HEIGHT;
|
||||
}
|
||||
|
||||
@TargetApi(16)
|
||||
@SuppressWarnings("SuspiciousNameCombination")
|
||||
private String getHeightColumn(int orientation) {
|
||||
if (orientation == 0 || orientation == 180) return MediaStore.Images.ImageColumns.HEIGHT;
|
||||
|
@ -1,10 +1,7 @@
|
||||
package org.thoughtcrime.securesms.components;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
@ -24,7 +21,7 @@ public class SquareFrameLayout extends FrameLayout {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
@TargetApi(VERSION_CODES.HONEYCOMB) @SuppressWarnings("unused")
|
||||
@SuppressWarnings("unused")
|
||||
public SquareFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
||||
|
||||
package org.thoughtcrime.securesms.components.camera;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
@ -28,26 +27,26 @@ import android.hardware.Camera.Parameters;
|
||||
import android.hardware.Camera.Size;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
import org.session.libsignal.utilities.Log;
|
||||
import android.view.OrientationEventListener;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||
import org.session.libsignal.utilities.guava.Optional;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.session.libsession.utilities.TextSecurePreferences;
|
||||
import org.session.libsession.utilities.Util;
|
||||
import org.session.libsignal.utilities.Log;
|
||||
import org.session.libsignal.utilities.guava.Optional;
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class CameraView extends ViewGroup {
|
||||
private static final String TAG = CameraView.class.getSimpleName();
|
||||
@ -91,7 +90,6 @@ public class CameraView extends ViewGroup {
|
||||
addView(surface);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
public void onResume() {
|
||||
if (state != State.PAUSED) return;
|
||||
state = State.RESUMED;
|
||||
@ -255,33 +253,15 @@ public class CameraView extends ViewGroup {
|
||||
return Camera.getNumberOfCameras() > 1;
|
||||
}
|
||||
|
||||
public boolean isRearCamera() {
|
||||
return cameraId == CameraInfo.CAMERA_FACING_BACK;
|
||||
}
|
||||
|
||||
public void flipCamera() {
|
||||
if (Camera.getNumberOfCameras() > 1) {
|
||||
cameraId = cameraId == CameraInfo.CAMERA_FACING_BACK
|
||||
? CameraInfo.CAMERA_FACING_FRONT
|
||||
: CameraInfo.CAMERA_FACING_BACK;
|
||||
onPause();
|
||||
onResume();
|
||||
TextSecurePreferences.setDirectCaptureCameraId(getContext(), cameraId);
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(14)
|
||||
private void onCameraReady(final @NonNull Camera camera) {
|
||||
final Parameters parameters = camera.getParameters();
|
||||
|
||||
if (VERSION.SDK_INT >= 14) {
|
||||
parameters.setRecordingHint(true);
|
||||
final List<String> focusModes = parameters.getSupportedFocusModes();
|
||||
if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
|
||||
parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
|
||||
} else if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
|
||||
parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
|
||||
}
|
||||
parameters.setRecordingHint(true);
|
||||
final List<String> focusModes = parameters.getSupportedFocusModes();
|
||||
if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
|
||||
parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
|
||||
} else if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
|
||||
parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
|
||||
}
|
||||
|
||||
displayOrientation = CameraUtils.getCameraDisplayOrientation(getActivity(), getCameraInfo());
|
||||
@ -465,7 +445,7 @@ public class CameraView extends ViewGroup {
|
||||
}
|
||||
final float newWidth = visibleRect.width() * scale;
|
||||
final float newHeight = visibleRect.height() * scale;
|
||||
final float centerX = (VERSION.SDK_INT < 14 || isTroublemaker()) ? previewWidth - newWidth / 2 : previewWidth / 2;
|
||||
final float centerX = (isTroublemaker()) ? previewWidth - newWidth / 2 : previewWidth / 2;
|
||||
final float centerY = previewHeight / 2;
|
||||
|
||||
visibleRect.set((int) (centerX - newWidth / 2),
|
||||
|
@ -16,15 +16,9 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms.conversation.v2
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.ActivityManager
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.net.Uri
|
||||
import android.os.Build.VERSION
|
||||
import android.os.Build.VERSION_CODES
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.text.TextUtils
|
||||
@ -33,22 +27,16 @@ import android.view.View
|
||||
import com.annimon.stream.Stream
|
||||
import com.google.android.mms.pdu_alt.CharacterSets
|
||||
import com.google.android.mms.pdu_alt.EncodedStringValue
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsignal.utilities.Log
|
||||
import org.thoughtcrime.securesms.components.ComposeText
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.UnsupportedEncodingException
|
||||
import java.util.Collections
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
object Util {
|
||||
private val TAG: String = Log.tag(Util::class.java)
|
||||
|
||||
private val BUILD_LIFESPAN = TimeUnit.DAYS.toMillis(90)
|
||||
|
||||
fun <T> asList(vararg elements: T): List<T> {
|
||||
val result = mutableListOf<T>() // LinkedList()
|
||||
Collections.addAll(result, *elements)
|
||||
@ -104,19 +92,6 @@ object Util {
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
fun rightPad(value: String, length: Int): String {
|
||||
if (value.length >= length) {
|
||||
return value
|
||||
}
|
||||
|
||||
val out = StringBuilder(value)
|
||||
while (out.length < length) {
|
||||
out.append(" ")
|
||||
}
|
||||
|
||||
return out.toString()
|
||||
}
|
||||
|
||||
fun isEmpty(value: Array<EncodedStringValue?>?): Boolean {
|
||||
return value == null || value.size == 0
|
||||
}
|
||||
@ -133,64 +108,6 @@ object Util {
|
||||
return charSequence == null || charSequence.length == 0
|
||||
}
|
||||
|
||||
fun hasItems(collection: Collection<*>?): Boolean {
|
||||
return collection != null && !collection.isEmpty()
|
||||
}
|
||||
|
||||
fun <K, V> getOrDefault(map: Map<K, V>, key: K, defaultValue: V): V? {
|
||||
return if (map.containsKey(key)) map[key] else defaultValue
|
||||
}
|
||||
|
||||
fun getFirstNonEmpty(vararg values: String?): String {
|
||||
for (value in values) {
|
||||
if (!value.isNullOrEmpty()) { return value }
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
fun emptyIfNull(value: String?): String {
|
||||
return value ?: ""
|
||||
}
|
||||
|
||||
fun emptyIfNull(value: CharSequence?): CharSequence {
|
||||
return value ?: ""
|
||||
}
|
||||
|
||||
fun getBoldedString(value: String?): CharSequence {
|
||||
val spanned = SpannableString(value)
|
||||
spanned.setSpan(
|
||||
StyleSpan(Typeface.BOLD), 0,
|
||||
spanned.length,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
|
||||
return spanned
|
||||
}
|
||||
|
||||
fun toIsoString(bytes: ByteArray?): String {
|
||||
try {
|
||||
return String(bytes!!, charset(CharacterSets.MIMENAME_ISO_8859_1))
|
||||
} catch (e: UnsupportedEncodingException) {
|
||||
throw AssertionError("ISO_8859_1 must be supported!")
|
||||
}
|
||||
}
|
||||
|
||||
fun toIsoBytes(isoString: String): ByteArray {
|
||||
try {
|
||||
return isoString.toByteArray(charset(CharacterSets.MIMENAME_ISO_8859_1))
|
||||
} catch (e: UnsupportedEncodingException) {
|
||||
throw AssertionError("ISO_8859_1 must be supported!")
|
||||
}
|
||||
}
|
||||
|
||||
fun toUtf8Bytes(utf8String: String): ByteArray {
|
||||
try {
|
||||
return utf8String.toByteArray(charset(CharacterSets.MIMENAME_UTF_8))
|
||||
} catch (e: UnsupportedEncodingException) {
|
||||
throw AssertionError("UTF_8 must be supported!")
|
||||
}
|
||||
}
|
||||
|
||||
fun wait(lock: Any, timeout: Long) {
|
||||
try {
|
||||
(lock as Object).wait(timeout)
|
||||
@ -225,20 +142,6 @@ object Util {
|
||||
return parts
|
||||
}
|
||||
|
||||
fun combine(vararg elements: ByteArray?): ByteArray {
|
||||
try {
|
||||
val baos = ByteArrayOutputStream()
|
||||
|
||||
for (element in elements) {
|
||||
baos.write(element)
|
||||
}
|
||||
|
||||
return baos.toByteArray()
|
||||
} catch (e: IOException) {
|
||||
throw AssertionError(e)
|
||||
}
|
||||
}
|
||||
|
||||
fun trim(input: ByteArray?, length: Int): ByteArray {
|
||||
val result = ByteArray(length)
|
||||
System.arraycopy(input, 0, result, 0, result.size)
|
||||
@ -251,26 +154,6 @@ object Util {
|
||||
else Uri.parse(uri)
|
||||
}
|
||||
|
||||
@TargetApi(VERSION_CODES.KITKAT)
|
||||
fun isLowMemory(context: Context): Boolean {
|
||||
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||
|
||||
return (VERSION.SDK_INT >= VERSION_CODES.KITKAT && activityManager.isLowRamDevice) ||
|
||||
activityManager.largeMemoryClass <= 64
|
||||
}
|
||||
|
||||
fun clamp(value: Int, min: Int, max: Int): Int {
|
||||
return min(max(value.toDouble(), min.toDouble()), max.toDouble()).toInt()
|
||||
}
|
||||
|
||||
fun clamp(value: Long, min: Long, max: Long): Long {
|
||||
return min(max(value.toDouble(), min.toDouble()), max.toDouble()).toLong()
|
||||
}
|
||||
|
||||
fun clamp(value: Float, min: Float, max: Float): Float {
|
||||
return min(max(value.toDouble(), min.toDouble()), max.toDouble()).toFloat()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns half of the difference between the given length, and the length when scaled by the
|
||||
* given scale.
|
||||
@ -280,74 +163,6 @@ object Util {
|
||||
return (length - scaledLength) / 2
|
||||
}
|
||||
|
||||
fun readTextFromClipboard(context: Context): String? {
|
||||
run {
|
||||
val clipboardManager =
|
||||
context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
return if (clipboardManager.hasPrimaryClip() && clipboardManager.primaryClip!!.itemCount > 0) {
|
||||
clipboardManager.primaryClip!!.getItemAt(0).text.toString()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun writeTextToClipboard(context: Context, text: String) {
|
||||
writeTextToClipboard(context, context.getString(R.string.app_name), text)
|
||||
}
|
||||
|
||||
fun writeTextToClipboard(context: Context, label: String, text: String) {
|
||||
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip = ClipData.newPlainText(label, text)
|
||||
clipboard.setPrimaryClip(clip)
|
||||
}
|
||||
|
||||
fun toIntExact(value: Long): Int {
|
||||
if (value.toInt().toLong() != value) {
|
||||
throw ArithmeticException("integer overflow")
|
||||
}
|
||||
return value.toInt()
|
||||
}
|
||||
|
||||
fun isEquals(first: Long?, second: Long): Boolean {
|
||||
return first != null && first == second
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
fun <T> concatenatedList(vararg items: Collection<T>): List<T> {
|
||||
val concat: MutableList<T> = ArrayList(
|
||||
Stream.of(*items).reduce(0) { sum: Int, list: Collection<T> -> sum + list.size })
|
||||
|
||||
for (list in items) {
|
||||
concat.addAll(list)
|
||||
}
|
||||
|
||||
return concat
|
||||
}
|
||||
|
||||
fun isLong(value: String): Boolean {
|
||||
try {
|
||||
value.toLong()
|
||||
return true
|
||||
} catch (e: NumberFormatException) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
fun parseInt(integer: String, defaultValue: Int): Int {
|
||||
return try {
|
||||
integer.toInt()
|
||||
} catch (e: NumberFormatException) {
|
||||
defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
// Method to determine if we're currently in a left-to-right or right-to-left language like Arabic
|
||||
fun usingRightToLeftLanguage(context: Context): Boolean {
|
||||
val config = context.resources.configuration
|
||||
return config.layoutDirection == View.LAYOUT_DIRECTION_RTL
|
||||
}
|
||||
|
||||
// Method to determine if we're currently in a left-to-right or right-to-left language like Arabic
|
||||
fun usingLeftToRightLanguage(context: Context): Boolean {
|
||||
val config = context.resources.configuration
|
||||
|
@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.conversation.v2.input_bar
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.util.AttributeSet
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputConnection
|
||||
@ -57,7 +56,7 @@ class InputBarEditText : AppCompatEditText {
|
||||
InputConnectionCompat.OnCommitContentListener { inputContentInfo, flags, opts ->
|
||||
val lacksPermission = (flags and InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0
|
||||
// read and display inputContentInfo asynchronously
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1 && lacksPermission) {
|
||||
if (lacksPermission) {
|
||||
try {
|
||||
inputContentInfo.requestPermission()
|
||||
} catch (e: Exception) {
|
||||
|
@ -42,9 +42,8 @@ class BiometricSecretProvider {
|
||||
builder.setUnlockedDeviceRequired(true)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
builder.setInvalidatedByBiometricEnrollment(true)
|
||||
}
|
||||
builder.setInvalidatedByBiometricEnrollment(true)
|
||||
|
||||
keyGenerator.initialize(builder.build())
|
||||
keyGenerator.generateKeyPair()
|
||||
}
|
||||
|
@ -252,11 +252,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
|
||||
String channelId = context.getString(R.string.NotificationChannel_failures);
|
||||
|
||||
if (NotificationChannels.supported()) {
|
||||
NotificationChannel channel = new NotificationChannel(channelId, channelId, NotificationManager.IMPORTANCE_HIGH);
|
||||
channel.enableVibration(true);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
NotificationChannel channel = new NotificationChannel(channelId, channelId, NotificationManager.IMPORTANCE_HIGH);
|
||||
channel.enableVibration(true);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
@ -266,10 +264,6 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
.setContentText(context.getString(R.string.ErrorNotifier_migration_downgrade))
|
||||
.setAutoCancel(true);
|
||||
|
||||
if (!NotificationChannels.supported()) {
|
||||
builder.setPriority(NotificationCompat.PRIORITY_HIGH);
|
||||
}
|
||||
|
||||
notificationManager.notify(5874, builder.build());
|
||||
|
||||
// Throw the error (app will crash but there is nothing else we can do unfortunately)
|
||||
|
@ -1,6 +1,5 @@
|
||||
package org.thoughtcrime.securesms.mediasend;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
@ -200,14 +199,12 @@ class MediaRepository {
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@TargetApi(16)
|
||||
@SuppressWarnings("SuspiciousNameCombination")
|
||||
private String getWidthColumn(int orientation) {
|
||||
if (orientation == 0 || orientation == 180) return Images.Media.WIDTH;
|
||||
else return Images.Media.HEIGHT;
|
||||
}
|
||||
|
||||
@TargetApi(16)
|
||||
@SuppressWarnings("SuspiciousNameCombination")
|
||||
private String getHeightColumn(int orientation) {
|
||||
if (orientation == 0 || orientation == 180) return Images.Media.HEIGHT;
|
||||
|
@ -50,8 +50,8 @@ public abstract class AbstractNotificationBuilder extends NotificationCompat.Bui
|
||||
}
|
||||
|
||||
public void setAlarms(@Nullable Uri ringtone, VibrateState vibrate) {
|
||||
Uri defaultRingtone = NotificationChannels.supported() ? NotificationChannels.getMessageRingtone(context) : TextSecurePreferences.getNotificationRingtone(context);
|
||||
boolean defaultVibrate = NotificationChannels.supported() ? NotificationChannels.getMessageVibrate(context) : TextSecurePreferences.isNotificationVibrateEnabled(context);
|
||||
Uri defaultRingtone = NotificationChannels.getMessageRingtone(context);
|
||||
boolean defaultVibrate = NotificationChannels.getMessageVibrate(context);
|
||||
|
||||
if (ringtone == null && !TextUtils.isEmpty(defaultRingtone.toString())) setSound(defaultRingtone);
|
||||
else if (ringtone != null && !ringtone.toString().isEmpty()) setSound(ringtone);
|
||||
|
@ -37,10 +37,6 @@ public class MultipleRecipientNotificationBuilder extends AbstractNotificationBu
|
||||
setContentIntent(PendingIntent.getActivity(context, 0, new Intent(context, HomeActivity.class), PendingIntent.FLAG_IMMUTABLE));
|
||||
setCategory(NotificationCompat.CATEGORY_MESSAGE);
|
||||
setGroupSummary(true);
|
||||
|
||||
if (!NotificationChannels.supported()) {
|
||||
setPriority(TextSecurePreferences.getNotificationPriority(context));
|
||||
}
|
||||
}
|
||||
|
||||
public void setMessageCount(int messageCount, int threadCount) {
|
||||
|
@ -1,15 +1,12 @@
|
||||
package org.thoughtcrime.securesms.notifications;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationChannelGroup;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.media.AudioAttributes;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
|
||||
@ -24,7 +21,6 @@ import org.session.libsession.utilities.Address;
|
||||
import org.session.libsession.utilities.ServiceUtil;
|
||||
import org.session.libsession.utilities.TextSecurePreferences;
|
||||
import org.session.libsession.utilities.recipients.Recipient;
|
||||
import org.session.libsession.utilities.recipients.Recipient.VibrateState;
|
||||
import org.session.libsignal.utilities.Log;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent;
|
||||
@ -63,10 +59,6 @@ public class NotificationChannels {
|
||||
* ignored for API < 26.
|
||||
*/
|
||||
public static synchronized void create(@NonNull Context context) {
|
||||
if (!supported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
|
||||
|
||||
int oldVersion = TextSecurePreferences.getNotificationChannelVersion(context);
|
||||
@ -82,32 +74,6 @@ public class NotificationChannels {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Recreates all notification channels for contacts with custom notifications enabled. Should be
|
||||
* safe to call repeatedly. Needs to be executed on a background thread.
|
||||
*/
|
||||
@WorkerThread
|
||||
public static synchronized void restoreContactNotificationChannels(@NonNull Context context) {
|
||||
if (!NotificationChannels.supported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RecipientDatabase db = DatabaseComponent.get(context).recipientDatabase();
|
||||
|
||||
try (RecipientDatabase.RecipientReader reader = db.getRecipientsWithNotificationChannels()) {
|
||||
Recipient recipient;
|
||||
while ((recipient = reader.getNext()) != null) {
|
||||
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
|
||||
if (!channelExists(notificationManager.getNotificationChannel(recipient.getNotificationChannel()))) {
|
||||
String id = createChannelFor(context, recipient);
|
||||
db.setNotificationChannel(recipient, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ensureCustomChannelConsistency(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The channel ID for the default messages channel.
|
||||
*/
|
||||
@ -115,13 +81,6 @@ public class NotificationChannels {
|
||||
return getMessagesChannelId(TextSecurePreferences.getNotificationMessagesChannelVersion(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether or not notification channels are supported.
|
||||
*/
|
||||
public static boolean supported() {
|
||||
return Build.VERSION.SDK_INT >= 26;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A name suitable to be displayed as the notification channel title.
|
||||
*/
|
||||
@ -137,119 +96,18 @@ public class NotificationChannels {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a channel for the specified recipient.
|
||||
* @return The channel ID for the newly-created channel.
|
||||
*/
|
||||
public static synchronized String createChannelFor(@NonNull Context context, @NonNull Recipient recipient) {
|
||||
VibrateState vibrateState = recipient.getMessageVibrate();
|
||||
boolean vibrationEnabled = vibrateState == VibrateState.DEFAULT ? TextSecurePreferences.isNotificationVibrateEnabled(context) : vibrateState == VibrateState.ENABLED;
|
||||
Uri messageRingtone = recipient.getMessageRingtone() != null ? recipient.getMessageRingtone() : getMessageRingtone(context);
|
||||
String displayName = getChannelDisplayNameFor(context, recipient.getName(), recipient.getProfileName(), recipient.getAddress());
|
||||
|
||||
return createChannelFor(context, recipient.getAddress(), displayName, messageRingtone, vibrationEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* More verbose version of {@link #createChannelFor(Context, Recipient)}.
|
||||
*/
|
||||
public static synchronized @Nullable String createChannelFor(@NonNull Context context,
|
||||
@NonNull Address address,
|
||||
@NonNull String displayName,
|
||||
@Nullable Uri messageSound,
|
||||
boolean vibrationEnabled)
|
||||
{
|
||||
if (!supported()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String channelId = generateChannelIdFor(address);
|
||||
NotificationChannel channel = new NotificationChannel(channelId, displayName, NotificationManager.IMPORTANCE_HIGH);
|
||||
|
||||
setLedPreference(channel, TextSecurePreferences.getNotificationLedColor(context));
|
||||
channel.setGroup(CATEGORY_MESSAGES);
|
||||
channel.enableVibration(vibrationEnabled);
|
||||
|
||||
if (messageSound != null) {
|
||||
channel.setSound(messageSound, new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
|
||||
.setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT)
|
||||
.build());
|
||||
}
|
||||
|
||||
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
|
||||
return channelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the channel generated for the provided recipient. Safe to call even if there was never
|
||||
* a channel made for that recipient.
|
||||
*/
|
||||
public static synchronized void deleteChannelFor(@NonNull Context context, @NonNull Recipient recipient) {
|
||||
if (!supported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
|
||||
String channel = recipient.getNotificationChannel();
|
||||
|
||||
if (channel != null) {
|
||||
Log.i(TAG, "Deleting channel");
|
||||
notificationManager.deleteNotificationChannel(channel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates the user to the system settings for the desired notification channel.
|
||||
*/
|
||||
public static void openChannelSettings(@NonNull Context context, @NonNull String channelId) {
|
||||
if (!supported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
|
||||
intent.putExtra(Settings.EXTRA_CHANNEL_ID, channelId);
|
||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName());
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the LED color for message notifications and all contact-specific message notification
|
||||
* channels. Performs database operations and should therefore be invoked on a background thread.
|
||||
*/
|
||||
@WorkerThread
|
||||
public static synchronized void updateMessagesLedColor(@NonNull Context context, @NonNull Integer color) {
|
||||
if (!supported()) {
|
||||
return;
|
||||
}
|
||||
Log.i(TAG, "Updating LED color.");
|
||||
|
||||
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
|
||||
|
||||
updateMessageChannel(context, channel -> setLedPreference(channel, color));
|
||||
updateAllRecipientChannelLedColors(context, notificationManager, color);
|
||||
|
||||
ensureCustomChannelConsistency(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The message ringtone set for the default message channel.
|
||||
*/
|
||||
public static synchronized @NonNull Uri getMessageRingtone(@NonNull Context context) {
|
||||
if (!supported()) {
|
||||
return Uri.EMPTY;
|
||||
}
|
||||
|
||||
Uri sound = ServiceUtil.getNotificationManager(context).getNotificationChannel(getMessagesChannel(context)).getSound();
|
||||
return sound == null ? Uri.EMPTY : sound;
|
||||
}
|
||||
|
||||
public static synchronized @Nullable Uri getMessageRingtone(@NonNull Context context, @NonNull Recipient recipient) {
|
||||
if (!supported() || recipient.getNotificationChannel() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
|
||||
NotificationChannel channel = notificationManager.getNotificationChannel(recipient.getNotificationChannel());
|
||||
|
||||
@ -265,9 +123,6 @@ public class NotificationChannels {
|
||||
* Update the message ringtone for the default message channel.
|
||||
*/
|
||||
public static synchronized void updateMessageRingtone(@NonNull Context context, @Nullable Uri uri) {
|
||||
if (!supported()) {
|
||||
return;
|
||||
}
|
||||
Log.i(TAG, "Updating default message ringtone with URI: " + String.valueOf(uri));
|
||||
|
||||
updateMessageChannel(context, channel -> {
|
||||
@ -275,121 +130,23 @@ public class NotificationChannels {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the message ringtone for a specific recipient. If that recipient has no channel, this
|
||||
* does nothing.
|
||||
*
|
||||
* This has to update the database, and therefore should be run on a background thread.
|
||||
*/
|
||||
@WorkerThread
|
||||
public static synchronized void updateMessageRingtone(@NonNull Context context, @NonNull Recipient recipient, @Nullable Uri uri) {
|
||||
if (!supported() || recipient.getNotificationChannel() == null) {
|
||||
return;
|
||||
}
|
||||
Log.i(TAG, "Updating recipient message ringtone with URI: " + String.valueOf(uri));
|
||||
|
||||
String newChannelId = generateChannelIdFor(recipient.getAddress());
|
||||
boolean success = updateExistingChannel(ServiceUtil.getNotificationManager(context),
|
||||
recipient.getNotificationChannel(),
|
||||
generateChannelIdFor(recipient.getAddress()),
|
||||
channel -> channel.setSound(uri == null ? Settings.System.DEFAULT_NOTIFICATION_URI : uri, getRingtoneAudioAttributes()));
|
||||
|
||||
DatabaseComponent.get(context).recipientDatabase().setNotificationChannel(recipient, success ? newChannelId : null);
|
||||
ensureCustomChannelConsistency(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The vibrate settings for the default message channel.
|
||||
*/
|
||||
public static synchronized boolean getMessageVibrate(@NonNull Context context) {
|
||||
if (!supported()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ServiceUtil.getNotificationManager(context).getNotificationChannel(getMessagesChannel(context)).shouldVibrate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The vibrate setting for a specific recipient. If that recipient has no channel, this
|
||||
* will return the setting for the default message channel.
|
||||
*/
|
||||
public static synchronized boolean getMessageVibrate(@NonNull Context context, @NonNull Recipient recipient) {
|
||||
if (!supported()) {
|
||||
return getMessageVibrate(context);
|
||||
}
|
||||
|
||||
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
|
||||
NotificationChannel channel = notificationManager.getNotificationChannel(recipient.getNotificationChannel());
|
||||
|
||||
if (!channelExists(channel)) {
|
||||
Log.w(TAG, "Recipient didn't have a channel. Returning message default.");
|
||||
return getMessageVibrate(context);
|
||||
}
|
||||
|
||||
return channel.shouldVibrate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the vibrate property for the default message channel.
|
||||
*/
|
||||
public static synchronized void updateMessageVibrate(@NonNull Context context, boolean enabled) {
|
||||
if (!supported()) {
|
||||
return;
|
||||
}
|
||||
Log.i(TAG, "Updating default vibrate with value: " + enabled);
|
||||
|
||||
updateMessageChannel(context, channel -> channel.enableVibration(enabled));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the message ringtone for a specific recipient. If that recipient has no channel, this
|
||||
* does nothing.
|
||||
*
|
||||
* This has to update the database and should therefore be run on a background thread.
|
||||
*/
|
||||
@WorkerThread
|
||||
public static synchronized void updateMessageVibrate(@NonNull Context context, @NonNull Recipient recipient, VibrateState vibrateState) {
|
||||
if (!supported() || recipient.getNotificationChannel() == null) {
|
||||
return ;
|
||||
}
|
||||
Log.i(TAG, "Updating recipient vibrate with value: " + vibrateState);
|
||||
|
||||
boolean enabled = vibrateState == VibrateState.DEFAULT ? getMessageVibrate(context) : vibrateState == VibrateState.ENABLED;
|
||||
String newChannelId = generateChannelIdFor(recipient.getAddress());
|
||||
boolean success = updateExistingChannel(ServiceUtil.getNotificationManager(context),
|
||||
recipient.getNotificationChannel(),
|
||||
newChannelId,
|
||||
channel -> channel.enableVibration(enabled));
|
||||
|
||||
DatabaseComponent.get(context).recipientDatabase().setNotificationChannel(recipient, success ? newChannelId : null);
|
||||
ensureCustomChannelConsistency(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the name of an existing channel to match the recipient's current name. Will have no
|
||||
* effect if the recipient doesn't have an existing valid channel.
|
||||
*/
|
||||
public static synchronized void updateContactChannelName(@NonNull Context context, @NonNull Recipient recipient) {
|
||||
if (!supported() || recipient.getNotificationChannel() == null) {
|
||||
return;
|
||||
}
|
||||
Log.i(TAG, "Updating contact channel name");
|
||||
|
||||
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
|
||||
|
||||
if (notificationManager.getNotificationChannel(recipient.getNotificationChannel()) == null) {
|
||||
Log.w(TAG, "Tried to update the name of a channel, but that channel doesn't exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
NotificationChannel channel = new NotificationChannel(recipient.getNotificationChannel(),
|
||||
getChannelDisplayNameFor(context, recipient.getName(), recipient.getProfileName(), recipient.getAddress()),
|
||||
NotificationManager.IMPORTANCE_HIGH);
|
||||
channel.setGroup(CATEGORY_MESSAGES);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
@TargetApi(26)
|
||||
@WorkerThread
|
||||
public static synchronized void ensureCustomChannelConsistency(@NonNull Context context) {
|
||||
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
|
||||
@ -421,7 +178,6 @@ public class NotificationChannels {
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(26)
|
||||
private static void onCreate(@NonNull Context context, @NonNull NotificationManager notificationManager) {
|
||||
NotificationChannelGroup messagesGroup = new NotificationChannelGroup(CATEGORY_MESSAGES, context.getResources().getString(R.string.NotificationChannel_group_messages));
|
||||
notificationManager.createNotificationChannelGroup(messagesGroup);
|
||||
@ -454,7 +210,6 @@ public class NotificationChannels {
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(26)
|
||||
private static void onUpgrade(@NonNull NotificationManager notificationManager, int oldVersion, int newVersion) {
|
||||
Log.i(TAG, "Upgrading channels from " + oldVersion + " to " + newVersion);
|
||||
|
||||
@ -469,7 +224,6 @@ public class NotificationChannels {
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(26)
|
||||
private static void setLedPreference(@NonNull NotificationChannel channel, @NonNull Integer ledColor) {
|
||||
if ("none".equals(ledColor)) {
|
||||
channel.enableLights(false);
|
||||
@ -484,7 +238,6 @@ public class NotificationChannels {
|
||||
return CONTACT_PREFIX + address.serialize() + "_" + System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@TargetApi(26)
|
||||
private static @NonNull NotificationChannel copyChannel(@NonNull NotificationChannel original, @NonNull String id) {
|
||||
NotificationChannel copy = new NotificationChannel(id, original.getName(), original.getImportance());
|
||||
|
||||
@ -505,27 +258,7 @@ public class NotificationChannels {
|
||||
return MESSAGES_PREFIX + version;
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
@TargetApi(26)
|
||||
private static void updateAllRecipientChannelLedColors(@NonNull Context context, @NonNull NotificationManager notificationManager, @NonNull Integer color) {
|
||||
RecipientDatabase database = DatabaseComponent.get(context).recipientDatabase();
|
||||
|
||||
try (RecipientDatabase.RecipientReader recipients = database.getRecipientsWithNotificationChannels()) {
|
||||
Recipient recipient;
|
||||
while ((recipient = recipients.getNext()) != null) {
|
||||
assert recipient.getNotificationChannel() != null;
|
||||
|
||||
String newChannelId = generateChannelIdFor(recipient.getAddress());
|
||||
boolean success = updateExistingChannel(notificationManager, recipient.getNotificationChannel(), newChannelId, channel -> setLedPreference(channel, color));
|
||||
|
||||
database.setNotificationChannel(recipient, success ? newChannelId : null);
|
||||
}
|
||||
}
|
||||
|
||||
ensureCustomChannelConsistency(context);
|
||||
}
|
||||
|
||||
@TargetApi(26)
|
||||
private static void updateMessageChannel(@NonNull Context context, @NonNull ChannelUpdater updater) {
|
||||
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
|
||||
int existingVersion = TextSecurePreferences.getNotificationMessagesChannelVersion(context);
|
||||
@ -539,7 +272,6 @@ public class NotificationChannels {
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(26)
|
||||
private static boolean updateExistingChannel(@NonNull NotificationManager notificationManager,
|
||||
@NonNull String channelId,
|
||||
@NonNull String newChannelId,
|
||||
@ -559,20 +291,17 @@ public class NotificationChannels {
|
||||
return true;
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
private static AudioAttributes getRingtoneAudioAttributes() {
|
||||
return new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
|
||||
.setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT)
|
||||
.build();
|
||||
}
|
||||
|
||||
@TargetApi(26)
|
||||
private static boolean channelExists(@Nullable NotificationChannel channel) {
|
||||
return channel != null && !NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId());
|
||||
}
|
||||
|
||||
private interface ChannelUpdater {
|
||||
@TargetApi(26)
|
||||
void update(@NonNull NotificationChannel channel);
|
||||
}
|
||||
}
|
||||
|
@ -51,8 +51,7 @@ public class NotificationState {
|
||||
Recipient recipient = notifications.getFirst().getRecipient();
|
||||
|
||||
if (recipient != null) {
|
||||
return NotificationChannels.supported() ? NotificationChannels.getMessageRingtone(context, recipient)
|
||||
: recipient.resolve().getMessageRingtone();
|
||||
return NotificationChannels.getMessageRingtone(context, recipient);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,9 +32,5 @@ public class PendingMessageNotificationBuilder extends AbstractNotificationBuild
|
||||
setContentIntent(PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE));
|
||||
setAutoCancel(true);
|
||||
setAlarms(null, Recipient.VibrateState.DEFAULT);
|
||||
|
||||
if (!NotificationChannels.supported()) {
|
||||
setPriority(TextSecurePreferences.getNotificationPriority(context));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.text.SpannableStringBuilder;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@ -24,11 +23,10 @@ import androidx.core.app.NotificationCompat.Action;
|
||||
import androidx.core.app.RemoteInput;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
|
||||
import org.session.libsession.avatars.ContactColors;
|
||||
import org.session.libsession.avatars.ContactPhoto;
|
||||
import org.session.libsession.avatars.ResourceContactPhoto;
|
||||
import org.session.libsession.messaging.contacts.Contact;
|
||||
import org.session.libsession.utilities.NotificationPrivacyPreference;
|
||||
import org.session.libsession.utilities.TextSecurePreferences;
|
||||
@ -38,7 +36,6 @@ import org.session.libsignal.utilities.Log;
|
||||
import org.thoughtcrime.securesms.database.SessionContactDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent;
|
||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
|
||||
import com.bumptech.glide.Glide;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
import org.thoughtcrime.securesms.mms.SlideDeck;
|
||||
import org.thoughtcrime.securesms.util.AvatarPlaceholderGenerator;
|
||||
@ -69,10 +66,6 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
|
||||
setSmallIcon(R.drawable.ic_notification);
|
||||
setColor(ContextCompat.getColor(context, R.color.accent_green));
|
||||
setCategory(NotificationCompat.CATEGORY_MESSAGE);
|
||||
|
||||
if (!NotificationChannels.supported()) {
|
||||
setPriority(TextSecurePreferences.getNotificationPriority(context));
|
||||
}
|
||||
}
|
||||
|
||||
public void setThread(@NonNull Recipient recipient) {
|
||||
@ -179,13 +172,11 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
|
||||
|
||||
Action replyAction = new Action(R.drawable.ic_reply_white_36dp, actionName, quickReplyIntent);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
replyAction = new Action.Builder(R.drawable.ic_reply_white_36dp,
|
||||
actionName,
|
||||
wearableReplyIntent)
|
||||
.addRemoteInput(new RemoteInput.Builder(DefaultMessageNotifier.EXTRA_REMOTE_REPLY).setLabel(label).build())
|
||||
.build();
|
||||
}
|
||||
replyAction = new Action.Builder(R.drawable.ic_reply_white_36dp,
|
||||
actionName,
|
||||
wearableReplyIntent)
|
||||
.addRemoteInput(new RemoteInput.Builder(DefaultMessageNotifier.EXTRA_REMOTE_REPLY).setLabel(label).build())
|
||||
.build();
|
||||
|
||||
Action wearableReplyAction = new Action.Builder(R.drawable.ic_reply,
|
||||
actionName,
|
||||
|
@ -210,15 +210,8 @@ public class Permissions {
|
||||
.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public static boolean hasAny(@NonNull Context context, String... permissions) {
|
||||
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
|
||||
Stream.of(permissions).anyMatch(permission -> ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED);
|
||||
|
||||
}
|
||||
|
||||
public static boolean hasAll(@NonNull Context context, String... permissions) {
|
||||
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
|
||||
Stream.of(permissions).allMatch(permission -> ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED);
|
||||
return Stream.of(permissions).allMatch(permission -> ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED);
|
||||
|
||||
}
|
||||
|
||||
|
@ -56,14 +56,14 @@ class NotificationsPreferenceFragment : ListSummaryPreferenceFragment() {
|
||||
|
||||
true
|
||||
}
|
||||
if (NotificationChannels.supported()) {
|
||||
prefs.setNotificationRingtone(
|
||||
NotificationChannels.getMessageRingtone(requireContext()).toString()
|
||||
)
|
||||
prefs.setNotificationVibrateEnabled(
|
||||
NotificationChannels.getMessageVibrate(requireContext())
|
||||
)
|
||||
}
|
||||
|
||||
prefs.setNotificationRingtone(
|
||||
NotificationChannels.getMessageRingtone(requireContext()).toString()
|
||||
)
|
||||
prefs.setNotificationVibrateEnabled(
|
||||
NotificationChannels.getMessageVibrate(requireContext())
|
||||
)
|
||||
|
||||
findPreference<Preference>(TextSecurePreferences.RINGTONE_PREF)!!.onPreferenceChangeListener = RingtoneSummaryListener()
|
||||
findPreference<Preference>(TextSecurePreferences.NOTIFICATION_PRIVACY_PREF)!!.onPreferenceChangeListener = NotificationPrivacyListener()
|
||||
findPreference<Preference>(TextSecurePreferences.VIBRATE_PREF)!!.onPreferenceChangeListener =
|
||||
@ -99,18 +99,18 @@ class NotificationsPreferenceFragment : ListSummaryPreferenceFragment() {
|
||||
true
|
||||
}
|
||||
initializeListSummary(findPreference<Preference>(TextSecurePreferences.NOTIFICATION_PRIVACY_PREF) as ListPreference?)
|
||||
if (NotificationChannels.supported()) {
|
||||
findPreference<Preference>(TextSecurePreferences.NOTIFICATION_PRIORITY_PREF)!!.onPreferenceClickListener =
|
||||
Preference.OnPreferenceClickListener {
|
||||
val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
|
||||
intent.putExtra(
|
||||
Settings.EXTRA_CHANNEL_ID, NotificationChannels.getMessagesChannel(requireContext())
|
||||
)
|
||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, requireContext().packageName)
|
||||
startActivity(intent)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
findPreference<Preference>(TextSecurePreferences.NOTIFICATION_PRIORITY_PREF)!!.onPreferenceClickListener =
|
||||
Preference.OnPreferenceClickListener {
|
||||
val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
|
||||
intent.putExtra(
|
||||
Settings.EXTRA_CHANNEL_ID, NotificationChannels.getMessagesChannel(requireContext())
|
||||
)
|
||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, requireContext().packageName)
|
||||
startActivity(intent)
|
||||
true
|
||||
}
|
||||
|
||||
initializeRingtoneSummary(findPreference(TextSecurePreferences.RINGTONE_PREF))
|
||||
initializeMessageVibrateSummary(findPreference<Preference>(TextSecurePreferences.VIBRATE_PREF) as SwitchPreferenceCompat?)
|
||||
}
|
||||
|
@ -78,14 +78,9 @@ class PrivacySettingsPreferenceFragment : ListSummaryPreferenceFragment() {
|
||||
title(R.string.CallNotificationBuilder_system_notification_title)
|
||||
text(R.string.CallNotificationBuilder_system_notification_message)
|
||||
button(R.string.activity_notification_settings_title) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
|
||||
.putExtra(Settings.EXTRA_APP_PACKAGE, BuildConfig.APPLICATION_ID)
|
||||
} else {
|
||||
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||
.setData(Uri.parse("package:" + BuildConfig.APPLICATION_ID))
|
||||
}
|
||||
.apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }
|
||||
Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
|
||||
.putExtra(Settings.EXTRA_APP_PACKAGE, BuildConfig.APPLICATION_ID)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.takeIf { IntentUtils.isResolvable(requireContext(), it) }.let {
|
||||
startActivity(it)
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.util.SparseArray
|
||||
@ -210,7 +209,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.settings_general, menu)
|
||||
if (BuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
menu.findItem(R.id.action_qr_code)?.contentDescription = resources.getString(R.string.AccessibilityId_view_qr_code)
|
||||
}
|
||||
return true
|
||||
|
@ -2,14 +2,12 @@ package org.thoughtcrime.securesms.preferences.widgets;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
|
||||
public class SignalListPreference extends ListPreference {
|
||||
@ -18,13 +16,11 @@ public class SignalListPreference extends ListPreference {
|
||||
private CharSequence summary;
|
||||
private OnPreferenceClickListener clickListener;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public SignalListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
initialize();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public SignalListPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initialize();
|
||||
|
@ -23,7 +23,6 @@
|
||||
package org.thoughtcrime.securesms.scribbles.widget;
|
||||
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
@ -34,7 +33,6 @@ import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Shader;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
@ -98,12 +96,6 @@ public class VerticalSlideColorPicker extends View {
|
||||
init();
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public VerticalSlideColorPicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
setWillNotDraw(false);
|
||||
|
||||
|
@ -18,17 +18,15 @@ package org.thoughtcrime.securesms.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.text.format.DateFormat;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import android.os.Build;
|
||||
import android.text.format.DateFormat;
|
||||
|
||||
import org.session.libsignal.utilities.Log;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -127,11 +125,7 @@ public class DateUtils extends android.text.format.DateUtils {
|
||||
@SuppressLint("ObsoleteSdkInt")
|
||||
public static long parseIso8601(@Nullable String date) {
|
||||
SimpleDateFormat format;
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX", Locale.getDefault());
|
||||
} else {
|
||||
format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault());
|
||||
}
|
||||
format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX", Locale.getDefault());
|
||||
|
||||
if (date.isEmpty()) {
|
||||
return -1;
|
||||
|
@ -1,15 +1,15 @@
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.ClipData;
|
||||
import android.content.Context;
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.text.TextPaint;
|
||||
import android.text.style.URLSpan;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
|
||||
public class LongClickCopySpan extends URLSpan {
|
||||
@ -39,23 +39,10 @@ public class LongClickCopySpan extends URLSpan {
|
||||
ds.setUnderlineText(!isHighlighted);
|
||||
}
|
||||
|
||||
void setHighlighted(boolean highlighted, @ColorInt int highlightColor) {
|
||||
this.isHighlighted = highlighted;
|
||||
this.highlightColor = highlightColor;
|
||||
}
|
||||
|
||||
private void copyUrl(Context context, String url) {
|
||||
int sdk = android.os.Build.VERSION.SDK_INT;
|
||||
if (sdk < android.os.Build.VERSION_CODES.HONEYCOMB) {
|
||||
@SuppressWarnings("deprecation") android.text.ClipboardManager clipboard =
|
||||
(android.text.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
clipboard.setText(url);
|
||||
} else {
|
||||
copyUriSdk11(context, url);
|
||||
}
|
||||
copyUriSdk11(context, url);
|
||||
}
|
||||
|
||||
@TargetApi(android.os.Build.VERSION_CODES.HONEYCOMB)
|
||||
private void copyUriSdk11(Context context, String url) {
|
||||
android.content.ClipboardManager clipboard =
|
||||
(android.content.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
|
@ -131,7 +131,7 @@ public class StickyHeaderDecoration extends RecyclerView.ItemDecoration {
|
||||
int layoutPos)
|
||||
{
|
||||
int headerHeight = getHeaderHeightForLayout(header);
|
||||
int top = getChildY(parent, child) - headerHeight;
|
||||
int top = (int)child.getY() - headerHeight;
|
||||
if (sticky && layoutPos == 0) {
|
||||
final int count = parent.getChildCount();
|
||||
final long currentId = adapter.getHeaderId(adapterPos);
|
||||
@ -142,7 +142,7 @@ public class StickyHeaderDecoration extends RecyclerView.ItemDecoration {
|
||||
long nextId = adapter.getHeaderId(adapterPosHere);
|
||||
if (nextId != currentId) {
|
||||
final View next = parent.getChildAt(translatedChildPosition(parent, i));
|
||||
final int offset = getChildY(parent, next) - (headerHeight + getHeader(parent, adapter, adapterPosHere).itemView.getHeight());
|
||||
final int offset = (int)next.getY() - (headerHeight + getHeader(parent, adapter, adapterPosHere).itemView.getHeight());
|
||||
if (offset < 0) {
|
||||
return offset;
|
||||
} else {
|
||||
@ -162,16 +162,6 @@ public class StickyHeaderDecoration extends RecyclerView.ItemDecoration {
|
||||
return isReverseLayout(parent) ? parent.getChildCount() - 1 - position : position;
|
||||
}
|
||||
|
||||
private int getChildY(RecyclerView parent, View child) {
|
||||
if (VERSION.SDK_INT < 11) {
|
||||
Rect rect = new Rect();
|
||||
parent.getChildVisibleRect(child, rect, null);
|
||||
return rect.top;
|
||||
} else {
|
||||
return (int)ViewCompat.getY(child);
|
||||
}
|
||||
}
|
||||
|
||||
protected int getHeaderHeightForLayout(View header) {
|
||||
return renderInline ? 0 : header.getHeight();
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import android.media.AudioDeviceInfo;
|
||||
import android.media.AudioFocusRequest;
|
||||
import android.media.AudioManager;
|
||||
import android.media.SoundPool;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
@ -116,14 +115,9 @@ public abstract class AudioManagerCompat {
|
||||
abstract public void abandonCallAudioFocus();
|
||||
|
||||
public static AudioManagerCompat create(@NonNull Context context) {
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
return new Api26AudioManagerCompat(context);
|
||||
} else {
|
||||
return new Api21AudioManagerCompat(context);
|
||||
}
|
||||
return new Api26AudioManagerCompat(context);
|
||||
}
|
||||
|
||||
@RequiresApi(26)
|
||||
private static class Api26AudioManagerCompat extends AudioManagerCompat {
|
||||
|
||||
private static AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder()
|
||||
|
@ -69,7 +69,7 @@ allprojects {
|
||||
}
|
||||
|
||||
project.ext {
|
||||
androidMinimumSdkVersion = 23
|
||||
androidMinimumSdkVersion = 26
|
||||
androidTargetSdkVersion = 34
|
||||
androidCompileSdkVersion = 34
|
||||
}
|
||||
|
@ -73,8 +73,8 @@ object SnodeAPI {
|
||||
private const val maxRetryCount = 6
|
||||
private const val minimumSnodePoolCount = 12
|
||||
private const val minimumSwarmSnodeCount = 3
|
||||
// Use port 4433 if the API level can handle the network security configuration and enforce pinned certificates
|
||||
private val seedNodePort = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) 443 else 4443
|
||||
// Use port 4433 to enforce pinned certificates
|
||||
private val seedNodePort = 4443
|
||||
|
||||
private const val useTestnet = false
|
||||
|
||||
|
@ -5,17 +5,12 @@ import android.media.MediaCodec
|
||||
import android.media.MediaDataSource
|
||||
import android.media.MediaExtractor
|
||||
import android.media.MediaFormat
|
||||
import android.os.Build
|
||||
|
||||
import androidx.annotation.RequiresApi
|
||||
|
||||
import java.io.FileDescriptor
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import java.nio.ShortBuffer
|
||||
import kotlin.jvm.Throws
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.math.sqrt
|
||||
@ -44,7 +39,6 @@ class DecodedAudio {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
@Throws(IOException::class)
|
||||
fun create(dataSource: MediaDataSource): DecodedAudio {
|
||||
val mediaExtractor = MediaExtractor().apply { setDataSource(dataSource) }
|
||||
@ -69,15 +63,7 @@ class DecodedAudio {
|
||||
|
||||
val samples: ShortBuffer
|
||||
get() {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
|
||||
Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1
|
||||
) {
|
||||
// Hack for Nougat where asReadOnlyBuffer fails to respect byte ordering.
|
||||
// See https://code.google.com/p/android/issues/detail?id=223824
|
||||
decodedSamples
|
||||
} else {
|
||||
decodedSamples.asReadOnlyBuffer()
|
||||
}
|
||||
return decodedSamples.asReadOnlyBuffer()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -128,15 +114,13 @@ class DecodedAudio {
|
||||
codec.start()
|
||||
|
||||
// Check if the track is in PCM 16 bit encoding.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
try {
|
||||
val pcmEncoding = codec.outputFormat.getInteger(MediaFormat.KEY_PCM_ENCODING)
|
||||
if (pcmEncoding != AudioFormat.ENCODING_PCM_16BIT) {
|
||||
throw IOException("Unsupported PCM encoding code: $pcmEncoding")
|
||||
}
|
||||
} catch (e: NullPointerException) {
|
||||
// If KEY_PCM_ENCODING is not specified, means it's ENCODING_PCM_16BIT.
|
||||
try {
|
||||
val pcmEncoding = codec.outputFormat.getInteger(MediaFormat.KEY_PCM_ENCODING)
|
||||
if (pcmEncoding != AudioFormat.ENCODING_PCM_16BIT) {
|
||||
throw IOException("Unsupported PCM encoding code: $pcmEncoding")
|
||||
}
|
||||
} catch (e: NullPointerException) {
|
||||
// If KEY_PCM_ENCODING is not specified, means it's ENCODING_PCM_16BIT.
|
||||
}
|
||||
|
||||
var decodedSamplesSize: Int = 0 // size of the output buffer containing decoded samples.
|
||||
|
@ -6,13 +6,8 @@ import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.os.Build;
|
||||
import android.os.PowerManager;
|
||||
import android.os.Vibrator;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
@ -54,8 +49,4 @@ public class ServiceUtil {
|
||||
return (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
|
||||
public static @Nullable SubscriptionManager getSubscriptionManager(@NonNull Context context) {
|
||||
return (SubscriptionManager) context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
|
||||
}
|
||||
}
|
||||
|
@ -19,21 +19,16 @@ package org.session.libsession.utilities;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewStub;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.view.animation.Animation;
|
||||
import android.widget.LinearLayout.LayoutParams;
|
||||
|
||||
import androidx.annotation.IdRes;
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
||||
|
||||
import org.session.libsignal.utilities.ListenableFuture;
|
||||
@ -42,58 +37,7 @@ import org.session.libsignal.utilities.SettableFuture;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public static float getY(final @NonNull View v) {
|
||||
if (VERSION.SDK_INT >= 11) {
|
||||
return ViewCompat.getY(v);
|
||||
} else {
|
||||
return ((ViewGroup.MarginLayoutParams)v.getLayoutParams()).topMargin;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public static float getX(final @NonNull View v) {
|
||||
if (VERSION.SDK_INT >= 11) {
|
||||
return ViewCompat.getX(v);
|
||||
} else {
|
||||
return ((LayoutParams)v.getLayoutParams()).leftMargin;
|
||||
}
|
||||
}
|
||||
|
||||
public static void swapChildInPlace(ViewGroup parent, View toRemove, View toAdd, int defaultIndex) {
|
||||
int childIndex = parent.indexOfChild(toRemove);
|
||||
if (childIndex > -1) parent.removeView(toRemove);
|
||||
parent.addView(toAdd, childIndex > -1 ? childIndex : defaultIndex);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends View> T inflateStub(@NonNull View parent, @IdRes int stubId) {
|
||||
return (T)((ViewStub)parent.findViewById(stubId)).inflate();
|
||||
v.setBackground(drawable);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@ -106,10 +50,6 @@ public class ViewUtil {
|
||||
return (T) parent.findViewById(resId);
|
||||
}
|
||||
|
||||
public static <T extends View> Stub<T> findStubById(@NonNull Activity parent, @IdRes int resId) {
|
||||
return new Stub<T>((ViewStub)parent.findViewById(resId));
|
||||
}
|
||||
|
||||
private static Animation getAlphaAnimation(float from, float to, int duration) {
|
||||
final Animation anim = new AlphaAnimation(from, to);
|
||||
anim.setInterpolator(new FastOutSlowInInterpolator());
|
||||
@ -177,58 +117,4 @@ public class ViewUtil {
|
||||
return (int)((dp * context.getResources().getDisplayMetrics().density) + 0.5);
|
||||
}
|
||||
|
||||
public static void updateLayoutParams(@NonNull View view, int width, int height) {
|
||||
view.getLayoutParams().width = width;
|
||||
view.getLayoutParams().height = height;
|
||||
view.requestLayout();
|
||||
}
|
||||
|
||||
public static int getLeftMargin(@NonNull View view) {
|
||||
if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
|
||||
return ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).leftMargin;
|
||||
}
|
||||
return ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).rightMargin;
|
||||
}
|
||||
|
||||
public static int getRightMargin(@NonNull View view) {
|
||||
if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
|
||||
return ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).rightMargin;
|
||||
}
|
||||
return ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).leftMargin;
|
||||
}
|
||||
|
||||
public static void setLeftMargin(@NonNull View view, int margin) {
|
||||
if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
|
||||
((ViewGroup.MarginLayoutParams) view.getLayoutParams()).leftMargin = margin;
|
||||
} else {
|
||||
((ViewGroup.MarginLayoutParams) view.getLayoutParams()).rightMargin = margin;
|
||||
}
|
||||
view.forceLayout();
|
||||
view.requestLayout();
|
||||
}
|
||||
|
||||
public static void setTopMargin(@NonNull View view, int margin) {
|
||||
((ViewGroup.MarginLayoutParams) view.getLayoutParams()).topMargin = margin;
|
||||
view.requestLayout();
|
||||
}
|
||||
|
||||
public static void setPaddingTop(@NonNull View view, int padding) {
|
||||
view.setPadding(view.getPaddingLeft(), padding, view.getPaddingRight(), view.getPaddingBottom());
|
||||
}
|
||||
|
||||
public static void setPaddingBottom(@NonNull View view, int padding) {
|
||||
view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(), padding);
|
||||
}
|
||||
|
||||
public static boolean isPointInsideView(@NonNull View view, float x, float y) {
|
||||
int[] location = new int[2];
|
||||
|
||||
view.getLocationOnScreen(location);
|
||||
|
||||
int viewX = location[0];
|
||||
int viewY = location[1];
|
||||
|
||||
return x > viewX && x < viewX + view.getWidth() &&
|
||||
y > viewY && y < viewY + view.getHeight();
|
||||
}
|
||||
}
|
||||
|
@ -718,7 +718,7 @@ public class Recipient implements RecipientModifiedListener {
|
||||
}
|
||||
|
||||
public synchronized @Nullable String getNotificationChannel() {
|
||||
return !(Build.VERSION.SDK_INT >= 26) ? null : notificationChannel;
|
||||
return notificationChannel;
|
||||
}
|
||||
|
||||
public void setNotificationChannel(@Nullable String value) {
|
||||
|
Loading…
Reference in New Issue
Block a user