mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-23 02:10:44 +01:00
Add Device to Device Transfer UI.
This commit is contained in:
committed by
Greyson Parrelli
parent
6f8be3260c
commit
75aab4c031
@@ -37,7 +37,7 @@ public final class SignalProxyUtil {
|
||||
public static void startListeningToWebsocket() {
|
||||
if (SignalStore.proxy().isProxyEnabled() && ApplicationDependencies.getPipeListener().getState().getValue() == PipeConnectivityListener.State.FAILURE) {
|
||||
Log.w(TAG, "Proxy is in a failed state. Restarting.");
|
||||
ApplicationDependencies.closeConnectionsAfterProxyFailure();
|
||||
ApplicationDependencies.closeConnections();
|
||||
}
|
||||
|
||||
ApplicationDependencies.getIncomingMessageObserver();
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextPaint;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.AbsoluteSizeSpan;
|
||||
import android.text.style.BulletSpan;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.text.style.DynamicDrawableSpan;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.style.ImageSpan;
|
||||
import android.text.style.RelativeSizeSpan;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
|
||||
public class SpanUtil {
|
||||
|
||||
@@ -45,6 +54,17 @@ public class SpanUtil {
|
||||
return spannable;
|
||||
}
|
||||
|
||||
public static CharSequence boldSubstring(CharSequence fullString, CharSequence substring) {
|
||||
SpannableString spannable = new SpannableString(fullString);
|
||||
int start = TextUtils.indexOf(fullString, substring);
|
||||
int end = start + substring.length();
|
||||
|
||||
if (start >= 0 && end <= fullString.length()) {
|
||||
spannable.setSpan(new StyleSpan(Typeface.BOLD), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
return spannable;
|
||||
}
|
||||
|
||||
public static CharSequence color(int color, CharSequence sequence) {
|
||||
SpannableString spannable = new SpannableString(sequence);
|
||||
spannable.setSpan(new ForegroundColorSpan(color), 0, sequence.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
@@ -52,8 +72,12 @@ public class SpanUtil {
|
||||
}
|
||||
|
||||
public static @NonNull CharSequence bullet(@NonNull CharSequence sequence) {
|
||||
return bullet(sequence, BulletSpan.STANDARD_GAP_WIDTH);
|
||||
}
|
||||
|
||||
public static @NonNull CharSequence bullet(@NonNull CharSequence sequence, int gapWidth) {
|
||||
SpannableString spannable = new SpannableString(sequence);
|
||||
spannable.setSpan(new BulletSpan(), 0, sequence.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
spannable.setSpan(new BulletSpan(gapWidth), 0, sequence.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return spannable;
|
||||
}
|
||||
|
||||
@@ -66,4 +90,30 @@ public class SpanUtil {
|
||||
|
||||
return imageSpan;
|
||||
}
|
||||
|
||||
public static CharSequence clickSubstring(@NonNull Context context, @NonNull CharSequence fullString, @NonNull CharSequence substring, @NonNull View.OnClickListener clickListener) {
|
||||
ClickableSpan clickable = new ClickableSpan() {
|
||||
@Override
|
||||
public void updateDrawState(@NonNull TextPaint ds) {
|
||||
super.updateDrawState(ds);
|
||||
ds.setUnderlineText(false);
|
||||
ds.setColor(ContextCompat.getColor(context, R.color.signal_accent_primary));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(@NonNull View widget) {
|
||||
clickListener.onClick(widget);
|
||||
}
|
||||
};
|
||||
|
||||
SpannableString spannable = new SpannableString(fullString);
|
||||
int start = TextUtils.indexOf(fullString, substring);
|
||||
int end = start + substring.length();
|
||||
|
||||
if (start >= 0 && end <= fullString.length()) {
|
||||
spannable.setSpan(clickable, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
|
||||
return spannable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,6 +145,8 @@ public class TextSecurePreferences {
|
||||
private static final String ENCRYPTED_BACKUP_PASSPHRASE = "pref_encrypted_backup_passphrase";
|
||||
private static final String BACKUP_TIME = "pref_backup_next_time";
|
||||
|
||||
public static final String TRANSFER = "pref_transfer";
|
||||
|
||||
public static final String SCREEN_LOCK = "pref_android_screen_lock";
|
||||
public static final String SCREEN_LOCK_TIMEOUT = "pref_android_screen_lock_timeout";
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MediatorLiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.Transformations;
|
||||
|
||||
import com.annimon.stream.function.Predicate;
|
||||
|
||||
@@ -76,6 +77,13 @@ public final class LiveDataUtil {
|
||||
return outputLiveData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a map operation on the source observable and then only emits the mapped item if it has changed since the previous emission.
|
||||
*/
|
||||
public static <A, B> LiveData<B> mapDistinct(@NonNull LiveData<A> source, @NonNull androidx.arch.core.util.Function<A, B> mapFunction) {
|
||||
return Transformations.distinctUntilChanged(Transformations.map(source, mapFunction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Once there is non-null data on both input {@link LiveData}, the {@link Combine} function is run
|
||||
* and produces a live data of the combined data.
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package org.thoughtcrime.securesms.util.livedata;
|
||||
|
||||
import androidx.annotation.AnyThread;
|
||||
import androidx.annotation.MainThread;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MediatorLiveData;
|
||||
|
||||
import com.annimon.stream.function.Function;
|
||||
|
||||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SerialExecutor;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Manages a state to be updated from a view model and provide direct and live access. Updates
|
||||
* occur serially on the same executor to allow updating in a thread safe way. While not
|
||||
* every state update is guaranteed to be emitted, no update action will be dropped and state
|
||||
* that is emitted will be accurate.
|
||||
*/
|
||||
public class Store<State> {
|
||||
private final LiveDataStore liveStore;
|
||||
|
||||
public Store(@NonNull State state) {
|
||||
this.liveStore = new LiveDataStore(state);
|
||||
}
|
||||
|
||||
public @NonNull LiveData<State> getStateLiveData() {
|
||||
return liveStore;
|
||||
}
|
||||
|
||||
public @NonNull State getState() {
|
||||
return liveStore.getState();
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
public void update(@NonNull Function<State, State> updater) {
|
||||
liveStore.update(updater);
|
||||
}
|
||||
|
||||
@MainThread
|
||||
public <Input> void update(@NonNull LiveData<Input> source, @NonNull Action<Input, State> action) {
|
||||
liveStore.update(source, action);
|
||||
}
|
||||
|
||||
private final class LiveDataStore extends MediatorLiveData<State> {
|
||||
private State state;
|
||||
private final Executor stateUpdater;
|
||||
|
||||
LiveDataStore(@NonNull State state) {
|
||||
this.stateUpdater = new SerialExecutor(SignalExecutors.BOUNDED);
|
||||
setState(state);
|
||||
}
|
||||
|
||||
synchronized @NonNull State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
private synchronized void setState(@NonNull State state) {
|
||||
this.state = state;
|
||||
postValue(this.state);
|
||||
}
|
||||
|
||||
<Input> void update(@NonNull LiveData<Input> source, @NonNull Action<Input, State> action) {
|
||||
addSource(source, input -> stateUpdater.execute(() -> setState(action.apply(input, getState()))));
|
||||
}
|
||||
|
||||
void update(@NonNull Function<State, State> updater) {
|
||||
stateUpdater.execute(() -> setState(updater.apply(getState())));
|
||||
}
|
||||
}
|
||||
|
||||
public interface Action<Input, State> {
|
||||
State apply(Input input, State current);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user