Add Device to Device Transfer UI.

This commit is contained in:
Cody Henthorne
2021-03-11 13:27:25 -05:00
committed by Greyson Parrelli
parent 6f8be3260c
commit 75aab4c031
75 changed files with 3494 additions and 200 deletions

View File

@@ -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();

View File

@@ -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;
}
}

View File

@@ -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";

View File

@@ -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.

View File

@@ -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);
}
}