mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-24 02:25:19 +00:00
Removing unrequired APi checks (#1620)
* Removing unrequired APi checks * Bumping min sdk to 26 and further cleaning version checks * More clean ups * Removed unused class
This commit is contained in:
parent
5a248da445
commit
93a28906fb
@ -291,10 +291,6 @@
|
|||||||
android:name="org.thoughtcrime.securesms.scribbles.StickerSelectActivity"
|
android:name="org.thoughtcrime.securesms.scribbles.StickerSelectActivity"
|
||||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
||||||
android:theme="@style/Theme.Session.ForceDark" />
|
android:theme="@style/Theme.Session.ForceDark" />
|
||||||
<activity
|
|
||||||
android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
|
|
||||||
android:screenOrientation="portrait"
|
|
||||||
android:theme="@style/Theme.AppCompat" />
|
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.ShortcutLauncherActivity"
|
android:name="org.thoughtcrime.securesms.ShortcutLauncherActivity"
|
||||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
||||||
|
@ -4,12 +4,8 @@ import android.app.ActivityManager;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
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 androidx.fragment.app.FragmentActivity;
|
||||||
import android.view.KeyEvent;
|
|
||||||
|
|
||||||
import org.session.libsession.utilities.TextSecurePreferences;
|
import org.session.libsession.utilities.TextSecurePreferences;
|
||||||
import org.session.libsession.utilities.dynamiclanguage.DynamicLanguageActivityHelper;
|
import org.session.libsession.utilities.dynamiclanguage.DynamicLanguageActivityHelper;
|
||||||
|
@ -39,7 +39,9 @@ import androidx.appcompat.app.AlertDialog;
|
|||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.appcompat.view.ActionMode;
|
import androidx.appcompat.view.ActionMode;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.fragment.app.FragmentStatePagerAdapter;
|
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||||
import androidx.loader.app.LoaderManager;
|
import androidx.loader.app.LoaderManager;
|
||||||
@ -421,11 +423,13 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
|
|||||||
mode.getMenuInflater().inflate(R.menu.media_overview_context, menu);
|
mode.getMenuInflater().inflate(R.menu.media_overview_context, menu);
|
||||||
mode.setTitle("1");
|
mode.setTitle("1");
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
FragmentActivity activity = getActivity();
|
||||||
Window window = getActivity().getWindow();
|
if (activity == null) return false;
|
||||||
|
|
||||||
|
Window window = activity.getWindow();
|
||||||
originalStatusBarColor = window.getStatusBarColor();
|
originalStatusBarColor = window.getStatusBarColor();
|
||||||
window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
window.setStatusBarColor(ContextCompat.getColor(activity, R.color.action_mode_status_bar));
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -455,11 +459,12 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
|
|||||||
public void onDestroyActionMode(ActionMode mode) {
|
public void onDestroyActionMode(ActionMode mode) {
|
||||||
actionMode = null;
|
actionMode = null;
|
||||||
getListAdapter().clearSelection();
|
getListAdapter().clearSelection();
|
||||||
((MediaOverviewActivity) getActivity()).onExitMultiSelect();
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
MediaOverviewActivity activity = ((MediaOverviewActivity) getActivity());
|
||||||
getActivity().getWindow().setStatusBarColor(originalStatusBarColor);
|
if(activity == null) return;
|
||||||
}
|
|
||||||
|
activity.onExitMultiSelect();
|
||||||
|
activity.getWindow().setStatusBarColor(originalStatusBarColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
package org.thoughtcrime.securesms;
|
package org.thoughtcrime.securesms;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
@ -26,7 +25,6 @@ import android.net.Uri;
|
|||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Build.VERSION;
|
import android.os.Build.VERSION;
|
||||||
import android.os.Build.VERSION_CODES;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
@ -57,6 +55,9 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||||||
import androidx.viewpager.widget.PagerAdapter;
|
import androidx.viewpager.widget.PagerAdapter;
|
||||||
import androidx.viewpager.widget.ViewPager;
|
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.messages.control.DataExtractionNotification;
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
import org.session.libsession.messaging.sending_receiving.MessageSender;
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment;
|
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.database.model.MmsMessageRecord;
|
||||||
import org.thoughtcrime.securesms.mediapreview.MediaPreviewViewModel;
|
import org.thoughtcrime.securesms.mediapreview.MediaPreviewViewModel;
|
||||||
import org.thoughtcrime.securesms.mediapreview.MediaRailAdapter;
|
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.mms.Slide;
|
||||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||||
import org.thoughtcrime.securesms.util.AttachmentUtil;
|
import org.thoughtcrime.securesms.util.AttachmentUtil;
|
||||||
|
@ -1,22 +1,19 @@
|
|||||||
package org.thoughtcrime.securesms.audio;
|
package org.thoughtcrime.securesms.audio;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.media.AudioFormat;
|
import android.media.AudioFormat;
|
||||||
import android.media.AudioRecord;
|
import android.media.AudioRecord;
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
import android.media.MediaCodecInfo;
|
import android.media.MediaCodecInfo;
|
||||||
import android.media.MediaFormat;
|
import android.media.MediaFormat;
|
||||||
import android.media.MediaRecorder;
|
import android.media.MediaRecorder;
|
||||||
import android.os.Build;
|
|
||||||
import org.session.libsignal.utilities.Log;
|
|
||||||
|
|
||||||
import org.session.libsession.utilities.Util;
|
import org.session.libsession.utilities.Util;
|
||||||
|
import org.session.libsignal.utilities.Log;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
|
||||||
public class AudioCodec {
|
public class AudioCodec {
|
||||||
|
|
||||||
private static final String TAG = AudioCodec.class.getSimpleName();
|
private static final String TAG = AudioCodec.class.getSimpleName();
|
||||||
|
@ -46,7 +46,7 @@ public class AudioSlidePlayer implements SensorEventListener {
|
|||||||
private final @NonNull Handler progressEventHandler;
|
private final @NonNull Handler progressEventHandler;
|
||||||
private final @NonNull AudioManager audioManager;
|
private final @NonNull AudioManager audioManager;
|
||||||
private final @NonNull SensorManager sensorManager;
|
private final @NonNull SensorManager sensorManager;
|
||||||
private final @NonNull Sensor proximitySensor;
|
private final Sensor proximitySensor;
|
||||||
private final @Nullable WakeLock wakeLock;
|
private final @Nullable WakeLock wakeLock;
|
||||||
|
|
||||||
private @NonNull WeakReference<Listener> listener;
|
private @NonNull WeakReference<Listener> listener;
|
||||||
@ -132,7 +132,9 @@ public class AudioSlidePlayer implements SensorEventListener {
|
|||||||
mediaPlayer.seekTo((long) (mediaPlayer.getDuration() * progress));
|
mediaPlayer.seekTo((long) (mediaPlayer.getDuration() * progress));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(proximitySensor != null) {
|
||||||
sensorManager.registerListener(AudioSlidePlayer.this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
|
sensorManager.registerListener(AudioSlidePlayer.this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
setPlaying(AudioSlidePlayer.this);
|
setPlaying(AudioSlidePlayer.this);
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,10 @@ package org.thoughtcrime.securesms.components;
|
|||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.net.Uri;
|
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.util.Pair;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@ -26,8 +21,12 @@ import android.widget.ImageView;
|
|||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.PopupWindow;
|
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.session.libsession.utilities.ViewUtil;
|
||||||
|
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
@ -126,15 +125,10 @@ public class AttachmentTypeSelector extends PopupWindow {
|
|||||||
public void onGlobalLayout() {
|
public void onGlobalLayout() {
|
||||||
getContentView().getViewTreeObserver().removeGlobalOnLayoutListener(this);
|
getContentView().getViewTreeObserver().removeGlobalOnLayoutListener(this);
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
animateWindowInCircular(anchor, getContentView());
|
animateWindowInCircular(anchor, getContentView());
|
||||||
} else {
|
|
||||||
animateWindowInTranslate(getContentView());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
animateButtonIn(imageButton, ANIMATION_DURATION / 2);
|
animateButtonIn(imageButton, ANIMATION_DURATION / 2);
|
||||||
animateButtonIn(cameraButton, ANIMATION_DURATION / 2);
|
animateButtonIn(cameraButton, ANIMATION_DURATION / 2);
|
||||||
|
|
||||||
@ -145,7 +139,6 @@ public class AttachmentTypeSelector extends PopupWindow {
|
|||||||
animateButtonIn(contactButton, 0);
|
animateButtonIn(contactButton, 0);
|
||||||
animateButtonIn(closeButton, 0);
|
animateButtonIn(closeButton, 0);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void updateHeight() {
|
private void updateHeight() {
|
||||||
int thresholdInDP = 120;
|
int thresholdInDP = 120;
|
||||||
@ -159,11 +152,7 @@ public class AttachmentTypeSelector extends PopupWindow {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dismiss() {
|
public void dismiss() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
animateWindowOutCircular(currentAnchor, getContentView());
|
animateWindowOutCircular(currentAnchor, getContentView());
|
||||||
} else {
|
|
||||||
animateWindowOutTranslate(getContentView());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setListener(@Nullable AttachmentClickedListener listener) {
|
public void setListener(@Nullable AttachmentClickedListener listener) {
|
||||||
@ -182,7 +171,6 @@ public class AttachmentTypeSelector extends PopupWindow {
|
|||||||
button.startAnimation(animation);
|
button.startAnimation(animation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
private void animateWindowInCircular(@Nullable View anchor, @NonNull View contentView) {
|
private void animateWindowInCircular(@Nullable View anchor, @NonNull View contentView) {
|
||||||
Pair<Integer, Integer> coordinates = getClickOrigin(anchor, contentView);
|
Pair<Integer, Integer> coordinates = getClickOrigin(anchor, contentView);
|
||||||
Animator animator = ViewAnimationUtils.createCircularReveal(contentView,
|
Animator animator = ViewAnimationUtils.createCircularReveal(contentView,
|
||||||
@ -201,7 +189,6 @@ public class AttachmentTypeSelector extends PopupWindow {
|
|||||||
getContentView().startAnimation(animation);
|
getContentView().startAnimation(animation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
private void animateWindowOutCircular(@Nullable View anchor, @NonNull View contentView) {
|
private void animateWindowOutCircular(@Nullable View anchor, @NonNull View contentView) {
|
||||||
Pair<Integer, Integer> coordinates = getClickOrigin(anchor, contentView);
|
Pair<Integer, Integer> coordinates = getClickOrigin(anchor, contentView);
|
||||||
Animator animator = ViewAnimationUtils.createCircularReveal(getContentView(),
|
Animator animator = ViewAnimationUtils.createCircularReveal(getContentView(),
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package org.thoughtcrime.securesms.components;
|
package org.thoughtcrime.securesms.components;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.text.Spannable;
|
import android.text.Spannable;
|
||||||
@ -16,15 +15,14 @@ import android.view.inputmethod.InputConnection;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.core.os.BuildCompat;
|
import androidx.core.os.BuildCompat;
|
||||||
import androidx.core.view.inputmethod.EditorInfoCompat;
|
import androidx.core.view.inputmethod.EditorInfoCompat;
|
||||||
import androidx.core.view.inputmethod.InputConnectionCompat;
|
import androidx.core.view.inputmethod.InputConnectionCompat;
|
||||||
import androidx.core.view.inputmethod.InputContentInfoCompat;
|
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.libsession.utilities.TextSecurePreferences;
|
||||||
|
import org.session.libsignal.utilities.Log;
|
||||||
|
import org.thoughtcrime.securesms.components.emoji.EmojiEditText;
|
||||||
|
|
||||||
public class ComposeText extends EmojiEditText {
|
public class ComposeText extends EmojiEditText {
|
||||||
|
|
||||||
@ -136,7 +134,6 @@ public class ComposeText extends EmojiEditText {
|
|||||||
editorInfo.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION;
|
editorInfo.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT < 21) return inputConnection;
|
|
||||||
if (mediaListener == null) return inputConnection;
|
if (mediaListener == null) return inputConnection;
|
||||||
if (inputConnection == null) return null;
|
if (inputConnection == null) return null;
|
||||||
|
|
||||||
@ -154,7 +151,6 @@ public class ComposeText extends EmojiEditText {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB_MR2)
|
|
||||||
private static class CommitContentListener implements InputConnectionCompat.OnCommitContentListener {
|
private static class CommitContentListener implements InputConnectionCompat.OnCommitContentListener {
|
||||||
|
|
||||||
private static final String TAG = CommitContentListener.class.getSimpleName();
|
private static final String TAG = CommitContentListener.class.getSimpleName();
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package org.thoughtcrime.securesms.components;
|
package org.thoughtcrime.securesms.components;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.os.Build.VERSION_CODES;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
@ -27,7 +25,6 @@ public class ConversationItemAlertView extends LinearLayout {
|
|||||||
initialize(attrs);
|
initialize(attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(VERSION_CODES.HONEYCOMB)
|
|
||||||
public ConversationItemAlertView(final Context context, AttributeSet attrs, int defStyle) {
|
public ConversationItemAlertView(final Context context, AttributeSet attrs, int defStyle) {
|
||||||
super(context, attrs, defStyle);
|
super(context, attrs, defStyle);
|
||||||
initialize(attrs);
|
initialize(attrs);
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
package org.thoughtcrime.securesms.components;
|
package org.thoughtcrime.securesms.components;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
|
||||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.animation.AlphaAnimation;
|
import android.view.animation.AlphaAnimation;
|
||||||
import android.view.animation.Animation;
|
import android.view.animation.Animation;
|
||||||
@ -11,6 +8,8 @@ import android.view.animation.AnimationSet;
|
|||||||
import android.view.animation.ScaleAnimation;
|
import android.view.animation.ScaleAnimation;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
||||||
|
|
||||||
public class HidingLinearLayout extends LinearLayout {
|
public class HidingLinearLayout extends LinearLayout {
|
||||||
|
|
||||||
public HidingLinearLayout(Context context) {
|
public HidingLinearLayout(Context context) {
|
||||||
@ -21,7 +20,6 @@ public class HidingLinearLayout extends LinearLayout {
|
|||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
|
||||||
public HidingLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
|
public HidingLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
super(context, attrs, defStyleAttr);
|
super(context, attrs, defStyleAttr);
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
package org.thoughtcrime.securesms.components;
|
package org.thoughtcrime.securesms.components;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
public class InputPanel extends LinearLayout {
|
public class InputPanel extends LinearLayout {
|
||||||
|
|
||||||
public InputPanel(Context context) {
|
public InputPanel(Context context) {
|
||||||
@ -18,7 +17,6 @@ public class InputPanel extends LinearLayout {
|
|||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
|
||||||
public InputPanel(Context context, AttributeSet attrs, int defStyleAttr) {
|
public InputPanel(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
super(context, attrs, defStyleAttr);
|
super(context, attrs, defStyleAttr);
|
||||||
}
|
}
|
||||||
|
@ -16,26 +16,25 @@
|
|||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.components;
|
package org.thoughtcrime.securesms.components;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Build.VERSION_CODES;
|
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import androidx.appcompat.widget.LinearLayoutCompat;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import org.session.libsignal.utilities.Log;
|
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import android.view.View;
|
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.ServiceUtil;
|
||||||
import org.session.libsession.utilities.Util;
|
import org.session.libsession.utilities.Util;
|
||||||
|
import org.session.libsignal.utilities.Log;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LinearLayout that, when a view container, will report back when it thinks a soft keyboard
|
* LinearLayout that, when a view container, will report back when it thinks a soft keyboard
|
||||||
* has been opened and what its height would be.
|
* has been opened and what its height would be.
|
||||||
@ -95,7 +94,7 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateKeyboardState() {
|
private void updateKeyboardState() {
|
||||||
if (viewInset == 0 && Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) viewInset = getViewInset();
|
if (viewInset == 0) viewInset = getViewInset();
|
||||||
|
|
||||||
getWindowVisibleDisplayFrame(rect);
|
getWindowVisibleDisplayFrame(rect);
|
||||||
|
|
||||||
@ -118,7 +117,6 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(VERSION_CODES.LOLLIPOP)
|
|
||||||
private int getViewInset() {
|
private int getViewInset() {
|
||||||
try {
|
try {
|
||||||
Field attachInfoField = View.class.getDeclaredField("mAttachInfo");
|
Field attachInfoField = View.class.getDeclaredField("mAttachInfo");
|
||||||
|
@ -3,24 +3,24 @@ package org.thoughtcrime.securesms.components;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
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.util.AttributeSet;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.bumptech.glide.RequestManager;
|
import com.bumptech.glide.RequestManager;
|
||||||
|
|
||||||
|
import org.session.libsession.utilities.Stub;
|
||||||
import org.thoughtcrime.securesms.mms.VideoSlide;
|
import org.thoughtcrime.securesms.mms.VideoSlide;
|
||||||
import org.thoughtcrime.securesms.video.VideoPlayer;
|
import org.thoughtcrime.securesms.video.VideoPlayer;
|
||||||
|
|
||||||
import org.session.libsession.utilities.Stub;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
public class MediaView extends FrameLayout {
|
public class MediaView extends FrameLayout {
|
||||||
|
|
||||||
private ZoomingImageView imageView;
|
private ZoomingImageView imageView;
|
||||||
@ -41,12 +41,6 @@ public class MediaView extends FrameLayout {
|
|||||||
initialize();
|
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() {
|
private void initialize() {
|
||||||
inflate(getContext(), R.layout.media_view, this);
|
inflate(getContext(), R.layout.media_view, this);
|
||||||
|
|
||||||
|
@ -1,19 +1,11 @@
|
|||||||
package org.thoughtcrime.securesms.components;
|
package org.thoughtcrime.securesms.components;
|
||||||
|
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.provider.MediaStore;
|
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.util.AttributeSet;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -21,16 +13,24 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageView;
|
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.Key;
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||||
import com.bumptech.glide.signature.MediaStoreSignature;
|
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.CursorRecyclerViewAdapter;
|
||||||
import org.thoughtcrime.securesms.database.loaders.RecentPhotosLoader;
|
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> {
|
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")
|
@SuppressWarnings("SuspiciousNameCombination")
|
||||||
private String getWidthColumn(int orientation) {
|
private String getWidthColumn(int orientation) {
|
||||||
if (orientation == 0 || orientation == 180) return MediaStore.Images.ImageColumns.WIDTH;
|
if (orientation == 0 || orientation == 180) return MediaStore.Images.ImageColumns.WIDTH;
|
||||||
else return MediaStore.Images.ImageColumns.HEIGHT;
|
else return MediaStore.Images.ImageColumns.HEIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(16)
|
|
||||||
@SuppressWarnings("SuspiciousNameCombination")
|
@SuppressWarnings("SuspiciousNameCombination")
|
||||||
private String getHeightColumn(int orientation) {
|
private String getHeightColumn(int orientation) {
|
||||||
if (orientation == 0 || orientation == 180) return MediaStore.Images.ImageColumns.HEIGHT;
|
if (orientation == 0 || orientation == 180) return MediaStore.Images.ImageColumns.HEIGHT;
|
||||||
|
@ -94,15 +94,11 @@ public class SearchToolbar extends LinearLayout {
|
|||||||
|
|
||||||
searchItem.expandActionView();
|
searchItem.expandActionView();
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
|
||||||
Animator animator = ViewAnimationUtils.createCircularReveal(this, (int)x, (int)y, 0, getWidth());
|
Animator animator = ViewAnimationUtils.createCircularReveal(this, (int)x, (int)y, 0, getWidth());
|
||||||
animator.setDuration(400);
|
animator.setDuration(400);
|
||||||
|
|
||||||
setVisibility(View.VISIBLE);
|
setVisibility(View.VISIBLE);
|
||||||
animator.start();
|
animator.start();
|
||||||
} else {
|
|
||||||
setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +112,6 @@ public class SearchToolbar extends LinearLayout {
|
|||||||
|
|
||||||
if (listener != null) listener.onSearchClosed();
|
if (listener != null) listener.onSearchClosed();
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
|
||||||
Animator animator = ViewAnimationUtils.createCircularReveal(this, (int)x, (int)y, getWidth(), 0);
|
Animator animator = ViewAnimationUtils.createCircularReveal(this, (int)x, (int)y, getWidth(), 0);
|
||||||
animator.setDuration(400);
|
animator.setDuration(400);
|
||||||
animator.addListener(new AnimationCompleteListener() {
|
animator.addListener(new AnimationCompleteListener() {
|
||||||
@ -126,9 +121,6 @@ public class SearchToolbar extends LinearLayout {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
animator.start();
|
animator.start();
|
||||||
} else {
|
|
||||||
setVisibility(View.INVISIBLE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
package org.thoughtcrime.securesms.components;
|
package org.thoughtcrime.securesms.components;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.graphics.BitmapFactory;
|
|
||||||
import android.os.Build.VERSION_CODES;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
@ -24,7 +21,7 @@ public class SquareFrameLayout extends FrameLayout {
|
|||||||
this(context, attrs, 0);
|
this(context, attrs, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(VERSION_CODES.HONEYCOMB) @SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public SquareFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
|
public SquareFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
super(context, attrs, defStyleAttr);
|
super(context, attrs, defStyleAttr);
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
|
|
||||||
package org.thoughtcrime.securesms.components.camera;
|
package org.thoughtcrime.securesms.components.camera;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
@ -28,26 +27,26 @@ import android.hardware.Camera.Parameters;
|
|||||||
import android.hardware.Camera.Size;
|
import android.hardware.Camera.Size;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Build.VERSION;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import org.session.libsignal.utilities.Log;
|
|
||||||
import android.view.OrientationEventListener;
|
import android.view.OrientationEventListener;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
import androidx.annotation.NonNull;
|
||||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
import androidx.annotation.Nullable;
|
||||||
import org.session.libsignal.utilities.guava.Optional;
|
|
||||||
|
|
||||||
import org.session.libsession.utilities.TextSecurePreferences;
|
import org.session.libsession.utilities.TextSecurePreferences;
|
||||||
import org.session.libsession.utilities.Util;
|
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.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public class CameraView extends ViewGroup {
|
public class CameraView extends ViewGroup {
|
||||||
private static final String TAG = CameraView.class.getSimpleName();
|
private static final String TAG = CameraView.class.getSimpleName();
|
||||||
@ -91,7 +90,6 @@ public class CameraView extends ViewGroup {
|
|||||||
addView(surface);
|
addView(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
if (state != State.PAUSED) return;
|
if (state != State.PAUSED) return;
|
||||||
state = State.RESUMED;
|
state = State.RESUMED;
|
||||||
@ -255,26 +253,9 @@ public class CameraView extends ViewGroup {
|
|||||||
return Camera.getNumberOfCameras() > 1;
|
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) {
|
private void onCameraReady(final @NonNull Camera camera) {
|
||||||
final Parameters parameters = camera.getParameters();
|
final Parameters parameters = camera.getParameters();
|
||||||
|
|
||||||
if (VERSION.SDK_INT >= 14) {
|
|
||||||
parameters.setRecordingHint(true);
|
parameters.setRecordingHint(true);
|
||||||
final List<String> focusModes = parameters.getSupportedFocusModes();
|
final List<String> focusModes = parameters.getSupportedFocusModes();
|
||||||
if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
|
if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
|
||||||
@ -282,7 +263,6 @@ public class CameraView extends ViewGroup {
|
|||||||
} else if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
|
} else if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
|
||||||
parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
|
parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
displayOrientation = CameraUtils.getCameraDisplayOrientation(getActivity(), getCameraInfo());
|
displayOrientation = CameraUtils.getCameraDisplayOrientation(getActivity(), getCameraInfo());
|
||||||
camera.setDisplayOrientation(displayOrientation);
|
camera.setDisplayOrientation(displayOrientation);
|
||||||
@ -465,7 +445,7 @@ public class CameraView extends ViewGroup {
|
|||||||
}
|
}
|
||||||
final float newWidth = visibleRect.width() * scale;
|
final float newWidth = visibleRect.width() * scale;
|
||||||
final float newHeight = visibleRect.height() * 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;
|
final float centerY = previewHeight / 2;
|
||||||
|
|
||||||
visibleRect.set((int) (centerX - newWidth / 2),
|
visibleRect.set((int) (centerX - newWidth / 2),
|
||||||
|
@ -16,15 +16,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.conversation.v2
|
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.content.Context
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build.VERSION
|
|
||||||
import android.os.Build.VERSION_CODES
|
|
||||||
import android.text.Spannable
|
import android.text.Spannable
|
||||||
import android.text.SpannableString
|
import android.text.SpannableString
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
@ -33,22 +27,16 @@ import android.view.View
|
|||||||
import com.annimon.stream.Stream
|
import com.annimon.stream.Stream
|
||||||
import com.google.android.mms.pdu_alt.CharacterSets
|
import com.google.android.mms.pdu_alt.CharacterSets
|
||||||
import com.google.android.mms.pdu_alt.EncodedStringValue
|
import com.google.android.mms.pdu_alt.EncodedStringValue
|
||||||
import network.loki.messenger.R
|
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
import org.thoughtcrime.securesms.components.ComposeText
|
import org.thoughtcrime.securesms.components.ComposeText
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.UnsupportedEncodingException
|
import java.io.UnsupportedEncodingException
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import kotlin.math.max
|
|
||||||
import kotlin.math.min
|
|
||||||
|
|
||||||
object Util {
|
object Util {
|
||||||
private val TAG: String = Log.tag(Util::class.java)
|
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> {
|
fun <T> asList(vararg elements: T): List<T> {
|
||||||
val result = mutableListOf<T>() // LinkedList()
|
val result = mutableListOf<T>() // LinkedList()
|
||||||
Collections.addAll(result, *elements)
|
Collections.addAll(result, *elements)
|
||||||
@ -104,19 +92,6 @@ object Util {
|
|||||||
return sb.toString()
|
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 {
|
fun isEmpty(value: Array<EncodedStringValue?>?): Boolean {
|
||||||
return value == null || value.size == 0
|
return value == null || value.size == 0
|
||||||
}
|
}
|
||||||
@ -133,64 +108,6 @@ object Util {
|
|||||||
return charSequence == null || charSequence.length == 0
|
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) {
|
fun wait(lock: Any, timeout: Long) {
|
||||||
try {
|
try {
|
||||||
(lock as Object).wait(timeout)
|
(lock as Object).wait(timeout)
|
||||||
@ -225,20 +142,6 @@ object Util {
|
|||||||
return parts
|
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 {
|
fun trim(input: ByteArray?, length: Int): ByteArray {
|
||||||
val result = ByteArray(length)
|
val result = ByteArray(length)
|
||||||
System.arraycopy(input, 0, result, 0, result.size)
|
System.arraycopy(input, 0, result, 0, result.size)
|
||||||
@ -251,26 +154,6 @@ object Util {
|
|||||||
else Uri.parse(uri)
|
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
|
* Returns half of the difference between the given length, and the length when scaled by the
|
||||||
* given scale.
|
* given scale.
|
||||||
@ -280,74 +163,6 @@ object Util {
|
|||||||
return (length - scaledLength) / 2
|
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
|
// Method to determine if we're currently in a left-to-right or right-to-left language like Arabic
|
||||||
fun usingLeftToRightLanguage(context: Context): Boolean {
|
fun usingLeftToRightLanguage(context: Context): Boolean {
|
||||||
val config = context.resources.configuration
|
val config = context.resources.configuration
|
||||||
|
@ -148,11 +148,8 @@ class InputBarButton : RelativeLayout {
|
|||||||
|
|
||||||
private fun onDown(event: MotionEvent) {
|
private fun onDown(event: MotionEvent) {
|
||||||
expand()
|
expand()
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
||||||
performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
|
performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
|
||||||
} else {
|
|
||||||
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
|
||||||
}
|
|
||||||
longPressCallback?.let { gestureHandler.removeCallbacks(it) }
|
longPressCallback?.let { gestureHandler.removeCallbacks(it) }
|
||||||
val newLongPressCallback = Runnable { onLongPress?.invoke() }
|
val newLongPressCallback = Runnable { onLongPress?.invoke() }
|
||||||
this.longPressCallback = newLongPressCallback
|
this.longPressCallback = newLongPressCallback
|
||||||
|
@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.conversation.v2.input_bar
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.view.inputmethod.InputConnection
|
import android.view.inputmethod.InputConnection
|
||||||
@ -57,7 +56,7 @@ class InputBarEditText : AppCompatEditText {
|
|||||||
InputConnectionCompat.OnCommitContentListener { inputContentInfo, flags, opts ->
|
InputConnectionCompat.OnCommitContentListener { inputContentInfo, flags, opts ->
|
||||||
val lacksPermission = (flags and InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0
|
val lacksPermission = (flags and InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0
|
||||||
// read and display inputContentInfo asynchronously
|
// read and display inputContentInfo asynchronously
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1 && lacksPermission) {
|
if (lacksPermission) {
|
||||||
try {
|
try {
|
||||||
inputContentInfo.requestPermission()
|
inputContentInfo.requestPermission()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -59,9 +59,6 @@ public class AttachmentSecretProvider {
|
|||||||
{
|
{
|
||||||
AttachmentSecret attachmentSecret = AttachmentSecret.fromString(unencryptedSecret);
|
AttachmentSecret attachmentSecret = AttachmentSecret.fromString(unencryptedSecret);
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
|
||||||
return attachmentSecret;
|
|
||||||
} else {
|
|
||||||
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(attachmentSecret.serialize().getBytes());
|
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(attachmentSecret.serialize().getBytes());
|
||||||
|
|
||||||
TextSecurePreferences.setAttachmentEncryptedSecret(context, encryptedSecret.serialize());
|
TextSecurePreferences.setAttachmentEncryptedSecret(context, encryptedSecret.serialize());
|
||||||
@ -69,16 +66,11 @@ public class AttachmentSecretProvider {
|
|||||||
|
|
||||||
return attachmentSecret;
|
return attachmentSecret;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private AttachmentSecret getEncryptedAttachmentSecret(@NonNull String serializedEncryptedSecret) {
|
private AttachmentSecret getEncryptedAttachmentSecret(@NonNull String serializedEncryptedSecret) {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
|
||||||
throw new AssertionError("OS downgrade not supported. KeyStore sealed data exists on platform < M!");
|
|
||||||
} else {
|
|
||||||
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.SealedData.fromString(serializedEncryptedSecret);
|
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.SealedData.fromString(serializedEncryptedSecret);
|
||||||
return AttachmentSecret.fromString(new String(KeyStoreHelper.unseal(encryptedSecret)));
|
return AttachmentSecret.fromString(new String(KeyStoreHelper.unseal(encryptedSecret)));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private AttachmentSecret createAndStoreAttachmentSecret(@NonNull Context context) {
|
private AttachmentSecret createAndStoreAttachmentSecret(@NonNull Context context) {
|
||||||
byte[] secret = new byte[32];
|
byte[] secret = new byte[32];
|
||||||
@ -91,12 +83,8 @@ public class AttachmentSecretProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void storeAttachmentSecret(@NonNull Context context, @NonNull AttachmentSecret attachmentSecret) {
|
private void storeAttachmentSecret(@NonNull Context context, @NonNull AttachmentSecret attachmentSecret) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
||||||
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(attachmentSecret.serialize().getBytes());
|
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(attachmentSecret.serialize().getBytes());
|
||||||
TextSecurePreferences.setAttachmentEncryptedSecret(context, encryptedSecret.serialize());
|
TextSecurePreferences.setAttachmentEncryptedSecret(context, encryptedSecret.serialize());
|
||||||
} else {
|
|
||||||
TextSecurePreferences.setAttachmentUnencryptedSecret(context, attachmentSecret.serialize());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -42,9 +42,8 @@ class BiometricSecretProvider {
|
|||||||
builder.setUnlockedDeviceRequired(true)
|
builder.setUnlockedDeviceRequired(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
||||||
builder.setInvalidatedByBiometricEnrollment(true)
|
builder.setInvalidatedByBiometricEnrollment(true)
|
||||||
}
|
|
||||||
keyGenerator.initialize(builder.build())
|
keyGenerator.initialize(builder.build())
|
||||||
keyGenerator.generateKeyPair()
|
keyGenerator.generateKeyPair()
|
||||||
}
|
}
|
||||||
|
@ -36,29 +36,21 @@ public class DatabaseSecretProvider {
|
|||||||
try {
|
try {
|
||||||
DatabaseSecret databaseSecret = new DatabaseSecret(unencryptedSecret);
|
DatabaseSecret databaseSecret = new DatabaseSecret(unencryptedSecret);
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
|
||||||
return databaseSecret;
|
|
||||||
} else {
|
|
||||||
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(databaseSecret.asBytes());
|
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(databaseSecret.asBytes());
|
||||||
|
|
||||||
TextSecurePreferences.setDatabaseEncryptedSecret(context, encryptedSecret.serialize());
|
TextSecurePreferences.setDatabaseEncryptedSecret(context, encryptedSecret.serialize());
|
||||||
TextSecurePreferences.setDatabaseUnencryptedSecret(context, null);
|
TextSecurePreferences.setDatabaseUnencryptedSecret(context, null);
|
||||||
|
|
||||||
return databaseSecret;
|
return databaseSecret;
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private DatabaseSecret getEncryptedDatabaseSecret(@NonNull String serializedEncryptedSecret) {
|
private DatabaseSecret getEncryptedDatabaseSecret(@NonNull String serializedEncryptedSecret) {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
|
||||||
throw new AssertionError("OS downgrade not supported. KeyStore sealed data exists on platform < M!");
|
|
||||||
} else {
|
|
||||||
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.SealedData.fromString(serializedEncryptedSecret);
|
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.SealedData.fromString(serializedEncryptedSecret);
|
||||||
return new DatabaseSecret(KeyStoreHelper.unseal(encryptedSecret));
|
return new DatabaseSecret(KeyStoreHelper.unseal(encryptedSecret));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private DatabaseSecret createAndStoreDatabaseSecret(@NonNull Context context) {
|
private DatabaseSecret createAndStoreDatabaseSecret(@NonNull Context context) {
|
||||||
byte[] secret = new byte[32];
|
byte[] secret = new byte[32];
|
||||||
@ -66,12 +58,8 @@ public class DatabaseSecretProvider {
|
|||||||
|
|
||||||
DatabaseSecret databaseSecret = new DatabaseSecret(secret);
|
DatabaseSecret databaseSecret = new DatabaseSecret(secret);
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
||||||
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(databaseSecret.asBytes());
|
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(databaseSecret.asBytes());
|
||||||
TextSecurePreferences.setDatabaseEncryptedSecret(context, encryptedSecret.serialize());
|
TextSecurePreferences.setDatabaseEncryptedSecret(context, encryptedSecret.serialize());
|
||||||
} else {
|
|
||||||
TextSecurePreferences.setDatabaseUnencryptedSecret(context, databaseSecret.asString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return databaseSecret;
|
return databaseSecret;
|
||||||
}
|
}
|
||||||
|
@ -129,9 +129,6 @@ public class IdentityKeyUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String getUnencryptedSecret(String key, String unencryptedSecret, Context context) {
|
private static String getUnencryptedSecret(String key, String unencryptedSecret, Context context) {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
|
||||||
return unencryptedSecret;
|
|
||||||
} else {
|
|
||||||
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(unencryptedSecret.getBytes());
|
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(unencryptedSecret.getBytes());
|
||||||
|
|
||||||
// save the encrypted suffix secret "key_encrypted"
|
// save the encrypted suffix secret "key_encrypted"
|
||||||
@ -141,23 +138,17 @@ public class IdentityKeyUtil {
|
|||||||
|
|
||||||
return unencryptedSecret;
|
return unencryptedSecret;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private static String getEncryptedSecret(String encryptedSecret) {
|
private static String getEncryptedSecret(String encryptedSecret) {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
|
||||||
throw new AssertionError("OS downgrade not supported. KeyStore sealed data exists on platform < M!");
|
|
||||||
} else {
|
|
||||||
KeyStoreHelper.SealedData sealedData = KeyStoreHelper.SealedData.fromString(encryptedSecret);
|
KeyStoreHelper.SealedData sealedData = KeyStoreHelper.SealedData.fromString(encryptedSecret);
|
||||||
return new String(KeyStoreHelper.unseal(sealedData));
|
return new String(KeyStoreHelper.unseal(sealedData));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static void save(Context context, String key, String value) {
|
public static void save(Context context, String key, String value) {
|
||||||
SharedPreferences preferences = context.getSharedPreferences(MASTER_SECRET_UTIL_PREFERENCES_NAME, 0);
|
SharedPreferences preferences = context.getSharedPreferences(MASTER_SECRET_UTIL_PREFERENCES_NAME, 0);
|
||||||
Editor preferencesEditor = preferences.edit();
|
Editor preferencesEditor = preferences.edit();
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
||||||
boolean isEncryptedSuffix = key.endsWith(ENCRYPTED_SUFFIX);
|
boolean isEncryptedSuffix = key.endsWith(ENCRYPTED_SUFFIX);
|
||||||
if (isEncryptedSuffix) {
|
if (isEncryptedSuffix) {
|
||||||
preferencesEditor.putString(key, value);
|
preferencesEditor.putString(key, value);
|
||||||
@ -165,9 +156,7 @@ public class IdentityKeyUtil {
|
|||||||
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(value.getBytes());
|
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(value.getBytes());
|
||||||
preferencesEditor.putString(key+ENCRYPTED_SUFFIX, encryptedSecret.serialize());
|
preferencesEditor.putString(key+ENCRYPTED_SUFFIX, encryptedSecret.serialize());
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
preferencesEditor.putString(key, value);
|
|
||||||
}
|
|
||||||
if (!preferencesEditor.commit()) throw new AssertionError("failed to save identity key/value to shared preferences");
|
if (!preferencesEditor.commit()) throw new AssertionError("failed to save identity key/value to shared preferences");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -966,11 +966,6 @@ public class AttachmentDatabase extends Database {
|
|||||||
|
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
private ThumbnailData generateVideoThumbnail(AttachmentId attachmentId) {
|
private ThumbnailData generateVideoThumbnail(AttachmentId attachmentId) {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
|
||||||
Log.w(TAG, "Video thumbnails not supported...");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataInfo dataInfo = getAttachmentDataFileInfo(attachmentId, DATA);
|
DataInfo dataInfo = getAttachmentDataFileInfo(attachmentId, DATA);
|
||||||
|
|
||||||
if (dataInfo == null) {
|
if (dataInfo == null) {
|
||||||
|
@ -252,11 +252,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||||||
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
|
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
|
||||||
String channelId = context.getString(R.string.NotificationChannel_failures);
|
String channelId = context.getString(R.string.NotificationChannel_failures);
|
||||||
|
|
||||||
if (NotificationChannels.supported()) {
|
|
||||||
NotificationChannel channel = new NotificationChannel(channelId, channelId, NotificationManager.IMPORTANCE_HIGH);
|
NotificationChannel channel = new NotificationChannel(channelId, channelId, NotificationManager.IMPORTANCE_HIGH);
|
||||||
channel.enableVibration(true);
|
channel.enableVibration(true);
|
||||||
notificationManager.createNotificationChannel(channel);
|
notificationManager.createNotificationChannel(channel);
|
||||||
}
|
|
||||||
|
|
||||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId)
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId)
|
||||||
.setSmallIcon(R.drawable.ic_notification)
|
.setSmallIcon(R.drawable.ic_notification)
|
||||||
@ -266,10 +264,6 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||||||
.setContentText(context.getString(R.string.ErrorNotifier_migration_downgrade))
|
.setContentText(context.getString(R.string.ErrorNotifier_migration_downgrade))
|
||||||
.setAutoCancel(true);
|
.setAutoCancel(true);
|
||||||
|
|
||||||
if (!NotificationChannels.supported()) {
|
|
||||||
builder.setPriority(NotificationCompat.PRIORITY_HIGH);
|
|
||||||
}
|
|
||||||
|
|
||||||
notificationManager.notify(5874, builder.build());
|
notificationManager.notify(5874, builder.build());
|
||||||
|
|
||||||
// Throw the error (app will crash but there is nothing else we can do unfortunately)
|
// Throw the error (app will crash but there is nothing else we can do unfortunately)
|
||||||
|
@ -15,6 +15,7 @@ import android.widget.RelativeLayout
|
|||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.annotation.ColorRes
|
import androidx.annotation.ColorRes
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
@ -38,7 +39,6 @@ import org.thoughtcrime.securesms.util.disableClipping
|
|||||||
import org.thoughtcrime.securesms.util.fadeIn
|
import org.thoughtcrime.securesms.util.fadeIn
|
||||||
import org.thoughtcrime.securesms.util.fadeOut
|
import org.thoughtcrime.securesms.util.fadeOut
|
||||||
import org.thoughtcrime.securesms.util.getAccentColor
|
import org.thoughtcrime.securesms.util.getAccentColor
|
||||||
import org.thoughtcrime.securesms.util.getColorWithID
|
|
||||||
|
|
||||||
class PathActivity : PassphraseRequiredActionBarActivity() {
|
class PathActivity : PassphraseRequiredActionBarActivity() {
|
||||||
private lateinit var binding: ActivityPathBinding
|
private lateinit var binding: ActivityPathBinding
|
||||||
@ -283,7 +283,7 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
private fun expand() {
|
private fun expand() {
|
||||||
dotView.animateSizeChange(R.dimen.path_row_dot_size, R.dimen.path_row_expanded_dot_size)
|
dotView.animateSizeChange(R.dimen.path_row_dot_size, R.dimen.path_row_expanded_dot_size)
|
||||||
@ColorRes val startColorID = if (UiModeUtilities.isDayUiMode(context)) R.color.transparent_black_30 else R.color.black
|
@ColorRes val startColorID = if (UiModeUtilities.isDayUiMode(context)) R.color.transparent_black_30 else R.color.black
|
||||||
val startColor = context.resources.getColorWithID(startColorID, context.theme)
|
val startColor = ContextCompat.getColor(context, startColorID)
|
||||||
val endColor = context.getAccentColor()
|
val endColor = context.getAccentColor()
|
||||||
GlowViewUtilities.animateShadowColorChange(dotView, startColor, endColor)
|
GlowViewUtilities.animateShadowColorChange(dotView, startColor, endColor)
|
||||||
}
|
}
|
||||||
@ -292,7 +292,7 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
dotView.animateSizeChange(R.dimen.path_row_expanded_dot_size, R.dimen.path_row_dot_size)
|
dotView.animateSizeChange(R.dimen.path_row_expanded_dot_size, R.dimen.path_row_dot_size)
|
||||||
@ColorRes val endColorID = if (UiModeUtilities.isDayUiMode(context)) R.color.transparent_black_30 else R.color.black
|
@ColorRes val endColorID = if (UiModeUtilities.isDayUiMode(context)) R.color.transparent_black_30 else R.color.black
|
||||||
val startColor = context.getAccentColor()
|
val startColor = context.getAccentColor()
|
||||||
val endColor = context.resources.getColorWithID(endColorID, context.theme)
|
val endColor = ContextCompat.getColor(context, endColorID)
|
||||||
GlowViewUtilities.animateShadowColorChange(dotView, startColor, endColor)
|
GlowViewUtilities.animateShadowColorChange(dotView, startColor, endColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import android.graphics.Paint
|
|||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.lifecycle.coroutineScope
|
import androidx.lifecycle.coroutineScope
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -17,7 +18,6 @@ import kotlinx.coroutines.withContext
|
|||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.snode.OnionRequestAPI
|
import org.session.libsession.snode.OnionRequestAPI
|
||||||
import org.thoughtcrime.securesms.conversation.v2.ViewUtil
|
import org.thoughtcrime.securesms.conversation.v2.ViewUtil
|
||||||
import org.thoughtcrime.securesms.util.getColorWithID
|
|
||||||
import org.thoughtcrime.securesms.util.toPx
|
import org.thoughtcrime.securesms.util.toPx
|
||||||
|
|
||||||
class PathStatusView : View {
|
class PathStatusView : View {
|
||||||
@ -104,7 +104,7 @@ class PathStatusView : View {
|
|||||||
sessionShadowColor = hasPathsColor
|
sessionShadowColor = hasPathsColor
|
||||||
} else {
|
} else {
|
||||||
setBackgroundResource(R.drawable.paths_building_dot)
|
setBackgroundResource(R.drawable.paths_building_dot)
|
||||||
val pathsBuildingColor = resources.getColorWithID(R.color.paths_building, context.theme)
|
val pathsBuildingColor = ContextCompat.getColor(context, R.color.paths_building)
|
||||||
mainColor = pathsBuildingColor
|
mainColor = pathsBuildingColor
|
||||||
sessionShadowColor = pathsBuildingColor
|
sessionShadowColor = pathsBuildingColor
|
||||||
}
|
}
|
||||||
|
@ -8,11 +8,6 @@ public interface Constraint {
|
|||||||
|
|
||||||
boolean isMet();
|
boolean isMet();
|
||||||
|
|
||||||
@NonNull String getFactoryKey();
|
|
||||||
|
|
||||||
@RequiresApi(26)
|
|
||||||
void applyToJobInfo(@NonNull JobInfo.Builder jobInfoBuilder);
|
|
||||||
|
|
||||||
interface Factory<T extends Constraint> {
|
interface Factory<T extends Constraint> {
|
||||||
T create();
|
T create();
|
||||||
}
|
}
|
||||||
|
@ -28,17 +28,6 @@ public class NetworkConstraint implements Constraint {
|
|||||||
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
|
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(26)
|
|
||||||
@Override
|
|
||||||
public void applyToJobInfo(@NonNull JobInfo.Builder jobInfoBuilder) {
|
|
||||||
jobInfoBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Factory implements Constraint.Factory<NetworkConstraint> {
|
public static final class Factory implements Constraint.Factory<NetworkConstraint> {
|
||||||
|
|
||||||
private final Application application;
|
private final Application application;
|
||||||
|
@ -32,24 +32,16 @@ class LogSecretProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] parseEncryptedSecret(String secret) {
|
private static byte[] parseEncryptedSecret(String secret) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
||||||
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.SealedData.fromString(secret);
|
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.SealedData.fromString(secret);
|
||||||
return KeyStoreHelper.unseal(encryptedSecret);
|
return KeyStoreHelper.unseal(encryptedSecret);
|
||||||
} else {
|
|
||||||
throw new AssertionError("OS downgrade not supported. KeyStore sealed data exists on platform < M!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] createAndStoreSecret(@NonNull Context context) {
|
private static byte[] createAndStoreSecret(@NonNull Context context) {
|
||||||
byte[] secret = new byte[32];
|
byte[] secret = new byte[32];
|
||||||
SECURE_RANDOM.nextBytes(secret);
|
SECURE_RANDOM.nextBytes(secret);
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
||||||
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(secret);
|
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(secret);
|
||||||
TextSecurePreferences.setLogEncryptedSecret(context, encryptedSecret.serialize());
|
TextSecurePreferences.setLogEncryptedSecret(context, encryptedSecret.serialize());
|
||||||
} else {
|
|
||||||
TextSecurePreferences.setLogUnencryptedSecret(context, Base64.encodeBytes(secret));
|
|
||||||
}
|
|
||||||
|
|
||||||
return secret;
|
return secret;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package org.thoughtcrime.securesms.mediasend;
|
package org.thoughtcrime.securesms.mediasend;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@ -200,14 +199,12 @@ class MediaRepository {
|
|||||||
}).toList();
|
}).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(16)
|
|
||||||
@SuppressWarnings("SuspiciousNameCombination")
|
@SuppressWarnings("SuspiciousNameCombination")
|
||||||
private String getWidthColumn(int orientation) {
|
private String getWidthColumn(int orientation) {
|
||||||
if (orientation == 0 || orientation == 180) return Images.Media.WIDTH;
|
if (orientation == 0 || orientation == 180) return Images.Media.WIDTH;
|
||||||
else return Images.Media.HEIGHT;
|
else return Images.Media.HEIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(16)
|
|
||||||
@SuppressWarnings("SuspiciousNameCombination")
|
@SuppressWarnings("SuspiciousNameCombination")
|
||||||
private String getHeightColumn(int orientation) {
|
private String getHeightColumn(int orientation) {
|
||||||
if (orientation == 0 || orientation == 180) return Images.Media.HEIGHT;
|
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) {
|
public void setAlarms(@Nullable Uri ringtone, VibrateState vibrate) {
|
||||||
Uri defaultRingtone = NotificationChannels.supported() ? NotificationChannels.getMessageRingtone(context) : TextSecurePreferences.getNotificationRingtone(context);
|
Uri defaultRingtone = NotificationChannels.getMessageRingtone(context);
|
||||||
boolean defaultVibrate = NotificationChannels.supported() ? NotificationChannels.getMessageVibrate(context) : TextSecurePreferences.isNotificationVibrateEnabled(context);
|
boolean defaultVibrate = NotificationChannels.getMessageVibrate(context);
|
||||||
|
|
||||||
if (ringtone == null && !TextUtils.isEmpty(defaultRingtone.toString())) setSound(defaultRingtone);
|
if (ringtone == null && !TextUtils.isEmpty(defaultRingtone.toString())) setSound(defaultRingtone);
|
||||||
else if (ringtone != null && !ringtone.toString().isEmpty()) setSound(ringtone);
|
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));
|
setContentIntent(PendingIntent.getActivity(context, 0, new Intent(context, HomeActivity.class), PendingIntent.FLAG_IMMUTABLE));
|
||||||
setCategory(NotificationCompat.CATEGORY_MESSAGE);
|
setCategory(NotificationCompat.CATEGORY_MESSAGE);
|
||||||
setGroupSummary(true);
|
setGroupSummary(true);
|
||||||
|
|
||||||
if (!NotificationChannels.supported()) {
|
|
||||||
setPriority(TextSecurePreferences.getNotificationPriority(context));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMessageCount(int messageCount, int threadCount) {
|
public void setMessageCount(int messageCount, int threadCount) {
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
package org.thoughtcrime.securesms.notifications;
|
package org.thoughtcrime.securesms.notifications;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
import android.app.NotificationChannelGroup;
|
import android.app.NotificationChannelGroup;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
|
||||||
import android.media.AudioAttributes;
|
import android.media.AudioAttributes;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Build;
|
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.text.TextUtils;
|
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.ServiceUtil;
|
||||||
import org.session.libsession.utilities.TextSecurePreferences;
|
import org.session.libsession.utilities.TextSecurePreferences;
|
||||||
import org.session.libsession.utilities.recipients.Recipient;
|
import org.session.libsession.utilities.recipients.Recipient;
|
||||||
import org.session.libsession.utilities.recipients.Recipient.VibrateState;
|
|
||||||
import org.session.libsignal.utilities.Log;
|
import org.session.libsignal.utilities.Log;
|
||||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent;
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent;
|
||||||
@ -63,10 +59,6 @@ public class NotificationChannels {
|
|||||||
* ignored for API < 26.
|
* ignored for API < 26.
|
||||||
*/
|
*/
|
||||||
public static synchronized void create(@NonNull Context context) {
|
public static synchronized void create(@NonNull Context context) {
|
||||||
if (!supported()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
|
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
|
||||||
|
|
||||||
int oldVersion = TextSecurePreferences.getNotificationChannelVersion(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.
|
* @return The channel ID for the default messages channel.
|
||||||
*/
|
*/
|
||||||
@ -115,13 +81,6 @@ public class NotificationChannels {
|
|||||||
return getMessagesChannelId(TextSecurePreferences.getNotificationMessagesChannelVersion(context));
|
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.
|
* @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.
|
* @return The message ringtone set for the default message channel.
|
||||||
*/
|
*/
|
||||||
public static synchronized @NonNull Uri getMessageRingtone(@NonNull Context context) {
|
public static synchronized @NonNull Uri getMessageRingtone(@NonNull Context context) {
|
||||||
if (!supported()) {
|
|
||||||
return Uri.EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
Uri sound = ServiceUtil.getNotificationManager(context).getNotificationChannel(getMessagesChannel(context)).getSound();
|
Uri sound = ServiceUtil.getNotificationManager(context).getNotificationChannel(getMessagesChannel(context)).getSound();
|
||||||
return sound == null ? Uri.EMPTY : sound;
|
return sound == null ? Uri.EMPTY : sound;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static synchronized @Nullable Uri getMessageRingtone(@NonNull Context context, @NonNull Recipient recipient) {
|
public static synchronized @Nullable Uri getMessageRingtone(@NonNull Context context, @NonNull Recipient recipient) {
|
||||||
if (!supported() || recipient.getNotificationChannel() == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
|
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
|
||||||
NotificationChannel channel = notificationManager.getNotificationChannel(recipient.getNotificationChannel());
|
NotificationChannel channel = notificationManager.getNotificationChannel(recipient.getNotificationChannel());
|
||||||
|
|
||||||
@ -265,9 +123,6 @@ public class NotificationChannels {
|
|||||||
* Update the message ringtone for the default message channel.
|
* Update the message ringtone for the default message channel.
|
||||||
*/
|
*/
|
||||||
public static synchronized void updateMessageRingtone(@NonNull Context context, @Nullable Uri uri) {
|
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));
|
Log.i(TAG, "Updating default message ringtone with URI: " + String.valueOf(uri));
|
||||||
|
|
||||||
updateMessageChannel(context, channel -> {
|
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.
|
* @return The vibrate settings for the default message channel.
|
||||||
*/
|
*/
|
||||||
public static synchronized boolean getMessageVibrate(@NonNull Context context) {
|
public static synchronized boolean getMessageVibrate(@NonNull Context context) {
|
||||||
if (!supported()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ServiceUtil.getNotificationManager(context).getNotificationChannel(getMessagesChannel(context)).shouldVibrate();
|
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.
|
* Sets the vibrate property for the default message channel.
|
||||||
*/
|
*/
|
||||||
public static synchronized void updateMessageVibrate(@NonNull Context context, boolean enabled) {
|
public static synchronized void updateMessageVibrate(@NonNull Context context, boolean enabled) {
|
||||||
if (!supported()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Log.i(TAG, "Updating default vibrate with value: " + enabled);
|
Log.i(TAG, "Updating default vibrate with value: " + enabled);
|
||||||
|
|
||||||
updateMessageChannel(context, channel -> channel.enableVibration(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
|
@WorkerThread
|
||||||
public static synchronized void ensureCustomChannelConsistency(@NonNull Context context) {
|
public static synchronized void ensureCustomChannelConsistency(@NonNull Context context) {
|
||||||
NotificationManager notificationManager = ServiceUtil.getNotificationManager(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) {
|
private static void onCreate(@NonNull Context context, @NonNull NotificationManager notificationManager) {
|
||||||
NotificationChannelGroup messagesGroup = new NotificationChannelGroup(CATEGORY_MESSAGES, context.getResources().getString(R.string.NotificationChannel_group_messages));
|
NotificationChannelGroup messagesGroup = new NotificationChannelGroup(CATEGORY_MESSAGES, context.getResources().getString(R.string.NotificationChannel_group_messages));
|
||||||
notificationManager.createNotificationChannelGroup(messagesGroup);
|
notificationManager.createNotificationChannelGroup(messagesGroup);
|
||||||
@ -454,7 +210,6 @@ public class NotificationChannels {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(26)
|
|
||||||
private static void onUpgrade(@NonNull NotificationManager notificationManager, int oldVersion, int newVersion) {
|
private static void onUpgrade(@NonNull NotificationManager notificationManager, int oldVersion, int newVersion) {
|
||||||
Log.i(TAG, "Upgrading channels from " + oldVersion + " to " + 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) {
|
private static void setLedPreference(@NonNull NotificationChannel channel, @NonNull Integer ledColor) {
|
||||||
if ("none".equals(ledColor)) {
|
if ("none".equals(ledColor)) {
|
||||||
channel.enableLights(false);
|
channel.enableLights(false);
|
||||||
@ -484,7 +238,6 @@ public class NotificationChannels {
|
|||||||
return CONTACT_PREFIX + address.serialize() + "_" + System.currentTimeMillis();
|
return CONTACT_PREFIX + address.serialize() + "_" + System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(26)
|
|
||||||
private static @NonNull NotificationChannel copyChannel(@NonNull NotificationChannel original, @NonNull String id) {
|
private static @NonNull NotificationChannel copyChannel(@NonNull NotificationChannel original, @NonNull String id) {
|
||||||
NotificationChannel copy = new NotificationChannel(id, original.getName(), original.getImportance());
|
NotificationChannel copy = new NotificationChannel(id, original.getName(), original.getImportance());
|
||||||
|
|
||||||
@ -505,27 +258,7 @@ public class NotificationChannels {
|
|||||||
return MESSAGES_PREFIX + version;
|
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) {
|
private static void updateMessageChannel(@NonNull Context context, @NonNull ChannelUpdater updater) {
|
||||||
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
|
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
|
||||||
int existingVersion = TextSecurePreferences.getNotificationMessagesChannelVersion(context);
|
int existingVersion = TextSecurePreferences.getNotificationMessagesChannelVersion(context);
|
||||||
@ -539,7 +272,6 @@ public class NotificationChannels {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(26)
|
|
||||||
private static boolean updateExistingChannel(@NonNull NotificationManager notificationManager,
|
private static boolean updateExistingChannel(@NonNull NotificationManager notificationManager,
|
||||||
@NonNull String channelId,
|
@NonNull String channelId,
|
||||||
@NonNull String newChannelId,
|
@NonNull String newChannelId,
|
||||||
@ -559,20 +291,17 @@ public class NotificationChannels {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(21)
|
|
||||||
private static AudioAttributes getRingtoneAudioAttributes() {
|
private static AudioAttributes getRingtoneAudioAttributes() {
|
||||||
return new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
|
return new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
|
||||||
.setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT)
|
.setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(26)
|
|
||||||
private static boolean channelExists(@Nullable NotificationChannel channel) {
|
private static boolean channelExists(@Nullable NotificationChannel channel) {
|
||||||
return channel != null && !NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId());
|
return channel != null && !NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface ChannelUpdater {
|
private interface ChannelUpdater {
|
||||||
@TargetApi(26)
|
|
||||||
void update(@NonNull NotificationChannel channel);
|
void update(@NonNull NotificationChannel channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,8 +51,7 @@ public class NotificationState {
|
|||||||
Recipient recipient = notifications.getFirst().getRecipient();
|
Recipient recipient = notifications.getFirst().getRecipient();
|
||||||
|
|
||||||
if (recipient != null) {
|
if (recipient != null) {
|
||||||
return NotificationChannels.supported() ? NotificationChannels.getMessageRingtone(context, recipient)
|
return NotificationChannels.getMessageRingtone(context, recipient);
|
||||||
: recipient.resolve().getMessageRingtone();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,9 +32,5 @@ public class PendingMessageNotificationBuilder extends AbstractNotificationBuild
|
|||||||
setContentIntent(PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE));
|
setContentIntent(PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE));
|
||||||
setAutoCancel(true);
|
setAutoCancel(true);
|
||||||
setAlarms(null, Recipient.VibrateState.DEFAULT);
|
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.RectF;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@ -24,11 +23,10 @@ import androidx.core.app.NotificationCompat.Action;
|
|||||||
import androidx.core.app.RemoteInput;
|
import androidx.core.app.RemoteInput;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||||
|
|
||||||
import org.session.libsession.avatars.ContactColors;
|
|
||||||
import org.session.libsession.avatars.ContactPhoto;
|
import org.session.libsession.avatars.ContactPhoto;
|
||||||
import org.session.libsession.avatars.ResourceContactPhoto;
|
|
||||||
import org.session.libsession.messaging.contacts.Contact;
|
import org.session.libsession.messaging.contacts.Contact;
|
||||||
import org.session.libsession.utilities.NotificationPrivacyPreference;
|
import org.session.libsession.utilities.NotificationPrivacyPreference;
|
||||||
import org.session.libsession.utilities.TextSecurePreferences;
|
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.database.SessionContactDatabase;
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent;
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent;
|
||||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
|
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
|
||||||
import com.bumptech.glide.Glide;
|
|
||||||
import org.thoughtcrime.securesms.mms.Slide;
|
import org.thoughtcrime.securesms.mms.Slide;
|
||||||
import org.thoughtcrime.securesms.mms.SlideDeck;
|
import org.thoughtcrime.securesms.mms.SlideDeck;
|
||||||
import org.thoughtcrime.securesms.util.AvatarPlaceholderGenerator;
|
import org.thoughtcrime.securesms.util.AvatarPlaceholderGenerator;
|
||||||
@ -69,10 +66,6 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
|
|||||||
setSmallIcon(R.drawable.ic_notification);
|
setSmallIcon(R.drawable.ic_notification);
|
||||||
setColor(ContextCompat.getColor(context, R.color.accent_green));
|
setColor(ContextCompat.getColor(context, R.color.accent_green));
|
||||||
setCategory(NotificationCompat.CATEGORY_MESSAGE);
|
setCategory(NotificationCompat.CATEGORY_MESSAGE);
|
||||||
|
|
||||||
if (!NotificationChannels.supported()) {
|
|
||||||
setPriority(TextSecurePreferences.getNotificationPriority(context));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setThread(@NonNull Recipient recipient) {
|
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);
|
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,
|
replyAction = new Action.Builder(R.drawable.ic_reply_white_36dp,
|
||||||
actionName,
|
actionName,
|
||||||
wearableReplyIntent)
|
wearableReplyIntent)
|
||||||
.addRemoteInput(new RemoteInput.Builder(DefaultMessageNotifier.EXTRA_REMOTE_REPLY).setLabel(label).build())
|
.addRemoteInput(new RemoteInput.Builder(DefaultMessageNotifier.EXTRA_REMOTE_REPLY).setLabel(label).build())
|
||||||
.build();
|
.build();
|
||||||
}
|
|
||||||
|
|
||||||
Action wearableReplyAction = new Action.Builder(R.drawable.ic_reply,
|
Action wearableReplyAction = new Action.Builder(R.drawable.ic_reply,
|
||||||
actionName,
|
actionName,
|
||||||
|
@ -210,15 +210,8 @@ public class Permissions {
|
|||||||
.toArray(new String[0]);
|
.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) {
|
public static boolean hasAll(@NonNull Context context, String... permissions) {
|
||||||
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
|
return Stream.of(permissions).allMatch(permission -> ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED);
|
||||||
Stream.of(permissions).allMatch(permission -> ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,14 +56,14 @@ class NotificationsPreferenceFragment : ListSummaryPreferenceFragment() {
|
|||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
if (NotificationChannels.supported()) {
|
|
||||||
prefs.setNotificationRingtone(
|
prefs.setNotificationRingtone(
|
||||||
NotificationChannels.getMessageRingtone(requireContext()).toString()
|
NotificationChannels.getMessageRingtone(requireContext()).toString()
|
||||||
)
|
)
|
||||||
prefs.setNotificationVibrateEnabled(
|
prefs.setNotificationVibrateEnabled(
|
||||||
NotificationChannels.getMessageVibrate(requireContext())
|
NotificationChannels.getMessageVibrate(requireContext())
|
||||||
)
|
)
|
||||||
}
|
|
||||||
findPreference<Preference>(TextSecurePreferences.RINGTONE_PREF)!!.onPreferenceChangeListener = RingtoneSummaryListener()
|
findPreference<Preference>(TextSecurePreferences.RINGTONE_PREF)!!.onPreferenceChangeListener = RingtoneSummaryListener()
|
||||||
findPreference<Preference>(TextSecurePreferences.NOTIFICATION_PRIVACY_PREF)!!.onPreferenceChangeListener = NotificationPrivacyListener()
|
findPreference<Preference>(TextSecurePreferences.NOTIFICATION_PRIVACY_PREF)!!.onPreferenceChangeListener = NotificationPrivacyListener()
|
||||||
findPreference<Preference>(TextSecurePreferences.VIBRATE_PREF)!!.onPreferenceChangeListener =
|
findPreference<Preference>(TextSecurePreferences.VIBRATE_PREF)!!.onPreferenceChangeListener =
|
||||||
@ -99,7 +99,7 @@ class NotificationsPreferenceFragment : ListSummaryPreferenceFragment() {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
initializeListSummary(findPreference<Preference>(TextSecurePreferences.NOTIFICATION_PRIVACY_PREF) as ListPreference?)
|
initializeListSummary(findPreference<Preference>(TextSecurePreferences.NOTIFICATION_PRIVACY_PREF) as ListPreference?)
|
||||||
if (NotificationChannels.supported()) {
|
|
||||||
findPreference<Preference>(TextSecurePreferences.NOTIFICATION_PRIORITY_PREF)!!.onPreferenceClickListener =
|
findPreference<Preference>(TextSecurePreferences.NOTIFICATION_PRIORITY_PREF)!!.onPreferenceClickListener =
|
||||||
Preference.OnPreferenceClickListener {
|
Preference.OnPreferenceClickListener {
|
||||||
val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
|
val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
|
||||||
@ -110,7 +110,7 @@ class NotificationsPreferenceFragment : ListSummaryPreferenceFragment() {
|
|||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
initializeRingtoneSummary(findPreference(TextSecurePreferences.RINGTONE_PREF))
|
initializeRingtoneSummary(findPreference(TextSecurePreferences.RINGTONE_PREF))
|
||||||
initializeMessageVibrateSummary(findPreference<Preference>(TextSecurePreferences.VIBRATE_PREF) as SwitchPreferenceCompat?)
|
initializeMessageVibrateSummary(findPreference<Preference>(TextSecurePreferences.VIBRATE_PREF) as SwitchPreferenceCompat?)
|
||||||
}
|
}
|
||||||
|
@ -78,14 +78,9 @@ class PrivacySettingsPreferenceFragment : ListSummaryPreferenceFragment() {
|
|||||||
title(R.string.CallNotificationBuilder_system_notification_title)
|
title(R.string.CallNotificationBuilder_system_notification_title)
|
||||||
text(R.string.CallNotificationBuilder_system_notification_message)
|
text(R.string.CallNotificationBuilder_system_notification_message)
|
||||||
button(R.string.activity_notification_settings_title) {
|
button(R.string.activity_notification_settings_title) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
|
Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
|
||||||
.putExtra(Settings.EXTRA_APP_PACKAGE, BuildConfig.APPLICATION_ID)
|
.putExtra(Settings.EXTRA_APP_PACKAGE, BuildConfig.APPLICATION_ID)
|
||||||
} else {
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
|
||||||
.setData(Uri.parse("package:" + BuildConfig.APPLICATION_ID))
|
|
||||||
}
|
|
||||||
.apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }
|
|
||||||
.takeIf { IntentUtils.isResolvable(requireContext(), it) }.let {
|
.takeIf { IntentUtils.isResolvable(requireContext(), it) }.let {
|
||||||
startActivity(it)
|
startActivity(it)
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.util.SparseArray
|
import android.util.SparseArray
|
||||||
@ -210,7 +209,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
menuInflater.inflate(R.menu.settings_general, menu)
|
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)
|
menu.findItem(R.id.action_qr_code)?.contentDescription = resources.getString(R.string.AccessibilityId_view_qr_code)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
@ -2,14 +2,12 @@ package org.thoughtcrime.securesms.preferences.widgets;
|
|||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
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.util.AttributeSet;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.preference.ListPreference;
|
||||||
|
import androidx.preference.PreferenceViewHolder;
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
public class SignalListPreference extends ListPreference {
|
public class SignalListPreference extends ListPreference {
|
||||||
@ -18,13 +16,11 @@ public class SignalListPreference extends ListPreference {
|
|||||||
private CharSequence summary;
|
private CharSequence summary;
|
||||||
private OnPreferenceClickListener clickListener;
|
private OnPreferenceClickListener clickListener;
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
public SignalListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
public SignalListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
super(context, attrs, defStyleAttr, defStyleRes);
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
initialize();
|
initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
public SignalListPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
public SignalListPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
super(context, attrs, defStyleAttr);
|
super(context, attrs, defStyleAttr);
|
||||||
initialize();
|
initialize();
|
||||||
|
@ -23,7 +23,6 @@
|
|||||||
package org.thoughtcrime.securesms.scribbles.widget;
|
package org.thoughtcrime.securesms.scribbles.widget;
|
||||||
|
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
@ -34,7 +33,6 @@ import android.graphics.Paint;
|
|||||||
import android.graphics.Path;
|
import android.graphics.Path;
|
||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
import android.graphics.Shader;
|
import android.graphics.Shader;
|
||||||
import android.os.Build;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -98,12 +96,6 @@ public class VerticalSlideColorPicker extends View {
|
|||||||
init();
|
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() {
|
private void init() {
|
||||||
setWillNotDraw(false);
|
setWillNotDraw(false);
|
||||||
|
|
||||||
|
@ -18,17 +18,15 @@ package org.thoughtcrime.securesms.util;
|
|||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.text.format.DateFormat;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import android.os.Build;
|
|
||||||
import android.text.format.DateFormat;
|
|
||||||
|
|
||||||
import org.session.libsignal.utilities.Log;
|
import org.session.libsignal.utilities.Log;
|
||||||
|
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@ -127,11 +125,7 @@ public class DateUtils extends android.text.format.DateUtils {
|
|||||||
@SuppressLint("ObsoleteSdkInt")
|
@SuppressLint("ObsoleteSdkInt")
|
||||||
public static long parseIso8601(@Nullable String date) {
|
public static long parseIso8601(@Nullable String date) {
|
||||||
SimpleDateFormat format;
|
SimpleDateFormat format;
|
||||||
if (Build.VERSION.SDK_INT >= 24) {
|
|
||||||
format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX", Locale.getDefault());
|
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (date.isEmpty()) {
|
if (date.isEmpty()) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -14,10 +14,7 @@ public class FileProviderUtil {
|
|||||||
private static final String AUTHORITY = "network.loki.securesms.fileprovider";
|
private static final String AUTHORITY = "network.loki.securesms.fileprovider";
|
||||||
|
|
||||||
public static Uri getUriFor(@NonNull Context context, @NonNull File file) {
|
public static Uri getUriFor(@NonNull Context context, @NonNull File file) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
|
||||||
return FileProvider.getUriForFile(context, AUTHORITY, file);
|
return FileProvider.getUriForFile(context, AUTHORITY, file);
|
||||||
else
|
|
||||||
return Uri.fromFile(file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean delete(@NonNull Context context, @NonNull Uri uri) {
|
public static boolean delete(@NonNull Context context, @NonNull Uri uri) {
|
||||||
|
@ -1,20 +1,9 @@
|
|||||||
package org.thoughtcrime.securesms.util
|
package org.thoughtcrime.securesms.util
|
||||||
|
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.os.Build
|
|
||||||
import androidx.annotation.ColorRes
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import kotlin.math.max
|
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
fun Resources.getColorWithID(@ColorRes id: Int, theme: Resources.Theme?): Int {
|
|
||||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
||||||
getColor(id, theme)
|
|
||||||
} else {
|
|
||||||
@Suppress("DEPRECATION") getColor(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toPx(dp: Int, resources: Resources): Int {
|
fun toPx(dp: Int, resources: Resources): Int {
|
||||||
return toPx(dp.toFloat(), resources).roundToInt()
|
return toPx(dp.toFloat(), resources).roundToInt()
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import android.view.animation.AccelerateDecelerateInterpolator
|
|||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.annotation.ColorRes
|
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@ -22,18 +21,6 @@ interface GlowView {
|
|||||||
|
|
||||||
object GlowViewUtilities {
|
object GlowViewUtilities {
|
||||||
|
|
||||||
fun animateColorIdChange(context: Context, view: GlowView, @ColorRes startColorID: Int, @ColorRes endColorID: Int) {
|
|
||||||
val startColor = context.resources.getColorWithID(startColorID, context.theme)
|
|
||||||
val endColor = context.resources.getColorWithID(endColorID, context.theme)
|
|
||||||
val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor)
|
|
||||||
animation.duration = 250
|
|
||||||
animation.addUpdateListener { animator ->
|
|
||||||
val color = animator.animatedValue as Int
|
|
||||||
view.mainColor = color
|
|
||||||
}
|
|
||||||
animation.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun animateColorChange(view: GlowView, @ColorInt startColor: Int, @ColorInt endColor: Int) {
|
fun animateColorChange(view: GlowView, @ColorInt startColor: Int, @ColorInt endColor: Int) {
|
||||||
val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor)
|
val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor)
|
||||||
animation.duration = 250
|
animation.duration = 250
|
||||||
@ -44,18 +31,6 @@ object GlowViewUtilities {
|
|||||||
animation.start()
|
animation.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun animateShadowColorIdChange(context: Context, view: GlowView, @ColorRes startColorID: Int, @ColorRes endColorID: Int) {
|
|
||||||
val startColor = context.resources.getColorWithID(startColorID, context.theme)
|
|
||||||
val endColor = context.resources.getColorWithID(endColorID, context.theme)
|
|
||||||
val animation = ValueAnimator.ofObject(ArgbEvaluator(), startColor, endColor)
|
|
||||||
animation.duration = 250
|
|
||||||
animation.addUpdateListener { animator ->
|
|
||||||
val color = animator.animatedValue as Int
|
|
||||||
view.sessionShadowColor = color
|
|
||||||
}
|
|
||||||
animation.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun animateShadowColorChange(
|
fun animateShadowColorChange(
|
||||||
view: GlowView,
|
view: GlowView,
|
||||||
@ColorInt startColor: Int,
|
@ColorInt startColor: Int,
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
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 network.loki.messenger.R;
|
|
||||||
|
|
||||||
public class LongClickCopySpan extends URLSpan {
|
|
||||||
private static final String PREFIX_MAILTO = "mailto:";
|
|
||||||
private static final String PREFIX_TEL = "tel:";
|
|
||||||
|
|
||||||
private boolean isHighlighted;
|
|
||||||
@ColorInt
|
|
||||||
private int highlightColor;
|
|
||||||
|
|
||||||
public LongClickCopySpan(String url) {
|
|
||||||
super(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onLongClick(View widget) {
|
|
||||||
Context context = widget.getContext();
|
|
||||||
String preparedUrl = prepareUrl(getURL());
|
|
||||||
copyUrl(context, preparedUrl);
|
|
||||||
Toast.makeText(context,
|
|
||||||
context.getString(R.string.ConversationItem_copied_text, preparedUrl), Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateDrawState(@NonNull TextPaint ds) {
|
|
||||||
super.updateDrawState(ds);
|
|
||||||
ds.bgColor = highlightColor;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@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);
|
|
||||||
ClipData clip = ClipData.newPlainText(context.getString(R.string.app_name), url);
|
|
||||||
clipboard.setPrimaryClip(clip);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String prepareUrl(String url) {
|
|
||||||
if (url.startsWith(PREFIX_MAILTO)) {
|
|
||||||
return url.substring(PREFIX_MAILTO.length());
|
|
||||||
} else if (url.startsWith(PREFIX_TEL)) {
|
|
||||||
return url.substring(PREFIX_TEL.length());
|
|
||||||
}
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
}
|
|
@ -131,7 +131,7 @@ public class StickyHeaderDecoration extends RecyclerView.ItemDecoration {
|
|||||||
int layoutPos)
|
int layoutPos)
|
||||||
{
|
{
|
||||||
int headerHeight = getHeaderHeightForLayout(header);
|
int headerHeight = getHeaderHeightForLayout(header);
|
||||||
int top = getChildY(parent, child) - headerHeight;
|
int top = (int)child.getY() - headerHeight;
|
||||||
if (sticky && layoutPos == 0) {
|
if (sticky && layoutPos == 0) {
|
||||||
final int count = parent.getChildCount();
|
final int count = parent.getChildCount();
|
||||||
final long currentId = adapter.getHeaderId(adapterPos);
|
final long currentId = adapter.getHeaderId(adapterPos);
|
||||||
@ -142,7 +142,7 @@ public class StickyHeaderDecoration extends RecyclerView.ItemDecoration {
|
|||||||
long nextId = adapter.getHeaderId(adapterPosHere);
|
long nextId = adapter.getHeaderId(adapterPosHere);
|
||||||
if (nextId != currentId) {
|
if (nextId != currentId) {
|
||||||
final View next = parent.getChildAt(translatedChildPosition(parent, i));
|
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) {
|
if (offset < 0) {
|
||||||
return offset;
|
return offset;
|
||||||
} else {
|
} else {
|
||||||
@ -162,16 +162,6 @@ public class StickyHeaderDecoration extends RecyclerView.ItemDecoration {
|
|||||||
return isReverseLayout(parent) ? parent.getChildCount() - 1 - position : position;
|
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) {
|
protected int getHeaderHeightForLayout(View header) {
|
||||||
return renderInline ? 0 : header.getHeight();
|
return renderInline ? 0 : header.getHeight();
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,20 @@
|
|||||||
package org.thoughtcrime.securesms.video;
|
package org.thoughtcrime.securesms.video;
|
||||||
|
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.media.MediaDataSource;
|
import android.media.MediaDataSource;
|
||||||
import android.os.Build;
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.session.libsession.utilities.Util;
|
||||||
import org.thoughtcrime.securesms.crypto.AttachmentSecret;
|
import org.thoughtcrime.securesms.crypto.AttachmentSecret;
|
||||||
import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream;
|
import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream;
|
||||||
import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream;
|
import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream;
|
||||||
import org.session.libsession.utilities.Util;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.M)
|
|
||||||
public class EncryptedMediaDataSource extends MediaDataSource {
|
public class EncryptedMediaDataSource extends MediaDataSource {
|
||||||
|
|
||||||
private final AttachmentSecret attachmentSecret;
|
private final AttachmentSecret attachmentSecret;
|
||||||
|
@ -8,10 +8,8 @@ import android.media.AudioDeviceInfo;
|
|||||||
import android.media.AudioFocusRequest;
|
import android.media.AudioFocusRequest;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.media.SoundPool;
|
import android.media.SoundPool;
|
||||||
import android.os.Build;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
|
|
||||||
import org.session.libsession.utilities.ServiceUtil;
|
import org.session.libsession.utilities.ServiceUtil;
|
||||||
import org.session.libsignal.utilities.Log;
|
import org.session.libsignal.utilities.Log;
|
||||||
@ -116,14 +114,9 @@ public abstract class AudioManagerCompat {
|
|||||||
abstract public void abandonCallAudioFocus();
|
abstract public void abandonCallAudioFocus();
|
||||||
|
|
||||||
public static AudioManagerCompat create(@NonNull Context context) {
|
public static AudioManagerCompat create(@NonNull Context context) {
|
||||||
if (Build.VERSION.SDK_INT >= 26) {
|
|
||||||
return new Api26AudioManagerCompat(context);
|
return new Api26AudioManagerCompat(context);
|
||||||
} else {
|
|
||||||
return new Api21AudioManagerCompat(context);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(26)
|
|
||||||
private static class Api26AudioManagerCompat extends AudioManagerCompat {
|
private static class Api26AudioManagerCompat extends AudioManagerCompat {
|
||||||
|
|
||||||
private static AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder()
|
private static AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder()
|
||||||
@ -180,44 +173,4 @@ public abstract class AudioManagerCompat {
|
|||||||
audioFocusRequest = null;
|
audioFocusRequest = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(21)
|
|
||||||
private static class Api21AudioManagerCompat extends AudioManagerCompat {
|
|
||||||
|
|
||||||
private static AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder()
|
|
||||||
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
|
|
||||||
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
|
|
||||||
.setLegacyStreamType(AudioManager.STREAM_VOICE_CALL)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private Api21AudioManagerCompat(@NonNull Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SoundPool createSoundPool() {
|
|
||||||
return new SoundPool.Builder()
|
|
||||||
.setAudioAttributes(AUDIO_ATTRIBUTES)
|
|
||||||
.setMaxStreams(1)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void requestCallAudioFocus() {
|
|
||||||
int result = audioManager.requestAudioFocus(onAudioFocusChangeListener, AudioManager.STREAM_VOICE_CALL, AUDIOFOCUS_GAIN);
|
|
||||||
|
|
||||||
if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
|
|
||||||
Log.w(TAG, "Audio focus not granted. Result code: " + result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void abandonCallAudioFocus() {
|
|
||||||
int result = audioManager.abandonAudioFocus(onAudioFocusChangeListener);
|
|
||||||
|
|
||||||
if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
|
|
||||||
Log.w(TAG, "Audio focus abandon failed. Result code: " + result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ allprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
project.ext {
|
project.ext {
|
||||||
androidMinimumSdkVersion = 23
|
androidMinimumSdkVersion = 26
|
||||||
androidTargetSdkVersion = 34
|
androidTargetSdkVersion = 34
|
||||||
androidCompileSdkVersion = 34
|
androidCompileSdkVersion = 34
|
||||||
}
|
}
|
||||||
|
@ -73,8 +73,8 @@ object SnodeAPI {
|
|||||||
private const val maxRetryCount = 6
|
private const val maxRetryCount = 6
|
||||||
private const val minimumSnodePoolCount = 12
|
private const val minimumSnodePoolCount = 12
|
||||||
private const val minimumSwarmSnodeCount = 3
|
private const val minimumSwarmSnodeCount = 3
|
||||||
// Use port 4433 if the API level can handle the network security configuration and enforce pinned certificates
|
// Use port 4433 to enforce pinned certificates
|
||||||
private val seedNodePort = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) 443 else 4443
|
private val seedNodePort = 4443
|
||||||
|
|
||||||
private const val useTestnet = false
|
private const val useTestnet = false
|
||||||
|
|
||||||
|
@ -5,17 +5,12 @@ import android.media.MediaCodec
|
|||||||
import android.media.MediaDataSource
|
import android.media.MediaDataSource
|
||||||
import android.media.MediaExtractor
|
import android.media.MediaExtractor
|
||||||
import android.media.MediaFormat
|
import android.media.MediaFormat
|
||||||
import android.os.Build
|
|
||||||
|
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
|
|
||||||
import java.io.FileDescriptor
|
import java.io.FileDescriptor
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.ByteOrder
|
import java.nio.ByteOrder
|
||||||
import java.nio.ShortBuffer
|
import java.nio.ShortBuffer
|
||||||
import kotlin.jvm.Throws
|
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
import kotlin.math.sqrt
|
import kotlin.math.sqrt
|
||||||
@ -44,7 +39,6 @@ class DecodedAudio {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun create(dataSource: MediaDataSource): DecodedAudio {
|
fun create(dataSource: MediaDataSource): DecodedAudio {
|
||||||
val mediaExtractor = MediaExtractor().apply { setDataSource(dataSource) }
|
val mediaExtractor = MediaExtractor().apply { setDataSource(dataSource) }
|
||||||
@ -69,15 +63,7 @@ class DecodedAudio {
|
|||||||
|
|
||||||
val samples: ShortBuffer
|
val samples: ShortBuffer
|
||||||
get() {
|
get() {
|
||||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
|
return decodedSamples.asReadOnlyBuffer()
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -128,7 +114,6 @@ class DecodedAudio {
|
|||||||
codec.start()
|
codec.start()
|
||||||
|
|
||||||
// Check if the track is in PCM 16 bit encoding.
|
// Check if the track is in PCM 16 bit encoding.
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
||||||
try {
|
try {
|
||||||
val pcmEncoding = codec.outputFormat.getInteger(MediaFormat.KEY_PCM_ENCODING)
|
val pcmEncoding = codec.outputFormat.getInteger(MediaFormat.KEY_PCM_ENCODING)
|
||||||
if (pcmEncoding != AudioFormat.ENCODING_PCM_16BIT) {
|
if (pcmEncoding != AudioFormat.ENCODING_PCM_16BIT) {
|
||||||
@ -137,7 +122,6 @@ class DecodedAudio {
|
|||||||
} catch (e: NullPointerException) {
|
} catch (e: NullPointerException) {
|
||||||
// If KEY_PCM_ENCODING is not specified, means it's ENCODING_PCM_16BIT.
|
// 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.
|
var decodedSamplesSize: Int = 0 // size of the output buffer containing decoded samples.
|
||||||
var decodedSamples: ByteArray? = null
|
var decodedSamples: ByteArray? = null
|
||||||
|
@ -6,13 +6,8 @@ import android.app.NotificationManager;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.os.Vibrator;
|
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.telephony.TelephonyManager;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
@ -54,8 +49,4 @@ public class ServiceUtil {
|
|||||||
return (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
|
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.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build.VERSION;
|
|
||||||
import android.os.Build.VERSION_CODES;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewStub;
|
|
||||||
import android.view.animation.AlphaAnimation;
|
import android.view.animation.AlphaAnimation;
|
||||||
import android.view.animation.Animation;
|
import android.view.animation.Animation;
|
||||||
import android.widget.LinearLayout.LayoutParams;
|
|
||||||
|
|
||||||
import androidx.annotation.IdRes;
|
import androidx.annotation.IdRes;
|
||||||
import androidx.annotation.LayoutRes;
|
import androidx.annotation.LayoutRes;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.view.ViewCompat;
|
|
||||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
||||||
|
|
||||||
import org.session.libsignal.utilities.ListenableFuture;
|
import org.session.libsignal.utilities.ListenableFuture;
|
||||||
@ -42,58 +37,7 @@ import org.session.libsignal.utilities.SettableFuture;
|
|||||||
public class ViewUtil {
|
public class ViewUtil {
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public static void setBackground(final @NonNull View v, final @Nullable Drawable drawable) {
|
public static void setBackground(final @NonNull View v, final @Nullable Drawable drawable) {
|
||||||
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
|
|
||||||
v.setBackground(drawable);
|
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@ -106,10 +50,6 @@ public class ViewUtil {
|
|||||||
return (T) parent.findViewById(resId);
|
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) {
|
private static Animation getAlphaAnimation(float from, float to, int duration) {
|
||||||
final Animation anim = new AlphaAnimation(from, to);
|
final Animation anim = new AlphaAnimation(from, to);
|
||||||
anim.setInterpolator(new FastOutSlowInInterpolator());
|
anim.setInterpolator(new FastOutSlowInInterpolator());
|
||||||
@ -177,58 +117,4 @@ public class ViewUtil {
|
|||||||
return (int)((dp * context.getResources().getDisplayMetrics().density) + 0.5);
|
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() {
|
public synchronized @Nullable String getNotificationChannel() {
|
||||||
return !(Build.VERSION.SDK_INT >= 26) ? null : notificationChannel;
|
return notificationChannel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setNotificationChannel(@Nullable String value) {
|
public void setNotificationChannel(@Nullable String value) {
|
||||||
|
Loading…
Reference in New Issue
Block a user