mirror of
https://github.com/oxen-io/session-android.git
synced 2025-10-23 17:48:50 +00:00
Update registration flow
This commit is contained in:
@@ -76,7 +76,7 @@ class Tweener {
|
||||
interpolator = (TimeInterpolator) value; // TODO: multiple interpolators?
|
||||
} else if ("onUpdate".equals(key) || "onUpdateListener".equals(key)) {
|
||||
updateListener = (AnimatorUpdateListener) value;
|
||||
} else if ("onComplete".equals(key) || "onCompleteListener".equals(key)) {
|
||||
} else if ("onCodeComplete".equals(key) || "onCompleteListener".equals(key)) {
|
||||
listener = (AnimatorListener) value;
|
||||
} else if ("delay".equals(key)) {
|
||||
delay = ((Number) value).longValue();
|
||||
|
@@ -0,0 +1,107 @@
|
||||
package org.thoughtcrime.securesms.components.registration;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
|
||||
public class CallMeCountDownView extends RelativeLayout {
|
||||
|
||||
private ImageView phone;
|
||||
private TextView callMeText;
|
||||
private TextView availableInText;
|
||||
private TextView countDownText;
|
||||
|
||||
private int countDown;
|
||||
private OnClickListener listener;
|
||||
|
||||
public CallMeCountDownView(Context context) {
|
||||
super(context);
|
||||
initialize();
|
||||
}
|
||||
|
||||
public CallMeCountDownView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initialize();
|
||||
}
|
||||
|
||||
public CallMeCountDownView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initialize();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public CallMeCountDownView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
initialize();
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
inflate(getContext(), R.layout.registration_call_me_view, this);
|
||||
|
||||
this.phone = findViewById(R.id.phone_icon);
|
||||
this.callMeText = findViewById(R.id.call_me_text);
|
||||
this.availableInText = findViewById(R.id.available_in_text);
|
||||
this.countDownText = findViewById(R.id.countdown);
|
||||
}
|
||||
|
||||
public void setOnClickListener(@Nullable OnClickListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void startCountDown(int countDown) {
|
||||
setVisibility(View.VISIBLE);
|
||||
this.phone.setColorFilter(null);
|
||||
this.phone.setOnClickListener(null);
|
||||
|
||||
this.callMeText.setTextColor(getResources().getColor(R.color.grey_700));
|
||||
this.callMeText.setOnClickListener(null);
|
||||
|
||||
this.availableInText.setVisibility(View.VISIBLE);
|
||||
this.countDownText.setVisibility(View.VISIBLE);
|
||||
|
||||
this.countDown = countDown;
|
||||
updateCountDown();
|
||||
}
|
||||
|
||||
public void setCallEnabled() {
|
||||
setVisibility(View.VISIBLE);
|
||||
this.phone.setColorFilter(new PorterDuffColorFilter(getResources().getColor(R.color.signal_primary), PorterDuff.Mode.SRC_IN));
|
||||
this.callMeText.setTextColor(getResources().getColor(R.color.signal_primary));
|
||||
|
||||
this.availableInText.setVisibility(View.GONE);
|
||||
this.countDownText.setVisibility(View.GONE);
|
||||
|
||||
this.phone.setOnClickListener(v -> handlePhoneCallRequest());
|
||||
this.callMeText.setOnClickListener(v -> handlePhoneCallRequest());
|
||||
}
|
||||
|
||||
private void updateCountDown() {
|
||||
if (countDown > 0) {
|
||||
countDown--;
|
||||
|
||||
int minutesRemaining = countDown / 60;
|
||||
int secondsRemaining = countDown - (minutesRemaining * 60);
|
||||
|
||||
countDownText.setText(String.format("%02d:%02d", minutesRemaining, secondsRemaining));
|
||||
countDownText.postDelayed(this::updateCountDown, 1000);
|
||||
} else if (countDown == 0) {
|
||||
setCallEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
private void handlePhoneCallRequest() {
|
||||
if (listener != null) listener.onClick(this);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,162 @@
|
||||
package org.thoughtcrime.securesms.components.registration;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.MainThread;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationSet;
|
||||
import android.view.animation.OvershootInterpolator;
|
||||
import android.view.animation.TranslateAnimation;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.annimon.stream.Collectors;
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class VerificationCodeView extends FrameLayout {
|
||||
|
||||
private final List<View> spaces = new ArrayList<>(6);
|
||||
private final List<TextView> codes = new ArrayList<>(6);
|
||||
private final List<View> containers = new ArrayList<>(7);
|
||||
|
||||
private OnCodeEnteredListener listener;
|
||||
private int index = 0;
|
||||
|
||||
public VerificationCodeView(Context context) {
|
||||
super(context);
|
||||
initialize(context, null);
|
||||
}
|
||||
|
||||
public VerificationCodeView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initialize(context, attrs);
|
||||
}
|
||||
|
||||
public VerificationCodeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initialize(context, attrs);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public VerificationCodeView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
initialize(context, attrs);
|
||||
}
|
||||
|
||||
private void initialize(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
inflate(context, R.layout.verification_code_view, this);
|
||||
|
||||
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.VerificationCodeView);
|
||||
|
||||
try {
|
||||
TextView separator = findViewById(R.id.separator);
|
||||
|
||||
this.spaces.add(findViewById(R.id.space_zero));
|
||||
this.spaces.add(findViewById(R.id.space_one));
|
||||
this.spaces.add(findViewById(R.id.space_two));
|
||||
this.spaces.add(findViewById(R.id.space_three));
|
||||
this.spaces.add(findViewById(R.id.space_four));
|
||||
this.spaces.add(findViewById(R.id.space_five));
|
||||
|
||||
this.codes.add(findViewById(R.id.code_zero));
|
||||
this.codes.add(findViewById(R.id.code_one));
|
||||
this.codes.add(findViewById(R.id.code_two));
|
||||
this.codes.add(findViewById(R.id.code_three));
|
||||
this.codes.add(findViewById(R.id.code_four));
|
||||
this.codes.add(findViewById(R.id.code_five));
|
||||
|
||||
this.containers.add(findViewById(R.id.container_zero));
|
||||
this.containers.add(findViewById(R.id.container_one));
|
||||
this.containers.add(findViewById(R.id.container_two));
|
||||
this.containers.add(findViewById(R.id.separator_container));
|
||||
this.containers.add(findViewById(R.id.container_three));
|
||||
this.containers.add(findViewById(R.id.container_four));
|
||||
this.containers.add(findViewById(R.id.container_five));
|
||||
|
||||
Stream.of(spaces).forEach(view -> view.setBackgroundColor(typedArray.getColor(R.styleable.VerificationCodeView_vcv_inputColor, Color.BLACK)));
|
||||
Stream.of(spaces).forEach(view -> view.setLayoutParams(new LinearLayout.LayoutParams(typedArray.getDimensionPixelSize(R.styleable.VerificationCodeView_vcv_inputWidth, ViewUtil.dpToPx(context, 20)),
|
||||
typedArray.getDimensionPixelSize(R.styleable.VerificationCodeView_vcv_inputHeight, ViewUtil.dpToPx(context, 1)))));
|
||||
Stream.of(codes).forEach(textView -> textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, typedArray.getDimension(R.styleable.VerificationCodeView_vcv_textSize, 30)));
|
||||
Stream.of(codes).forEach(textView -> textView.setTextColor(typedArray.getColor(R.styleable.VerificationCodeView_vcv_textColor, Color.GRAY)));
|
||||
|
||||
Stream.of(containers).forEach(view -> {
|
||||
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)view.getLayoutParams();
|
||||
params.setMargins(typedArray.getDimensionPixelSize(R.styleable.VerificationCodeView_vcv_spacing, ViewUtil.dpToPx(context, 5)),
|
||||
params.topMargin, params.rightMargin, params.bottomMargin);
|
||||
view.setLayoutParams(params);
|
||||
});
|
||||
|
||||
separator.setTextSize(TypedValue.COMPLEX_UNIT_SP, typedArray.getDimension(R.styleable.VerificationCodeView_vcv_textSize, 30));
|
||||
} finally {
|
||||
if (typedArray != null) typedArray.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
@MainThread
|
||||
public void setOnCompleteListener(OnCodeEnteredListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@MainThread
|
||||
public void append(int value) {
|
||||
if (index >= codes.size()) return;
|
||||
|
||||
TextView codeView = codes.get(index++);
|
||||
|
||||
Animation translateIn = new TranslateAnimation(0, 0, codeView.getHeight(), 0);
|
||||
translateIn.setInterpolator(new OvershootInterpolator());
|
||||
translateIn.setDuration(500);
|
||||
|
||||
Animation fadeIn = new AlphaAnimation(0, 1);
|
||||
fadeIn.setDuration(200);
|
||||
|
||||
AnimationSet animationSet = new AnimationSet(false);
|
||||
animationSet.addAnimation(fadeIn);
|
||||
animationSet.addAnimation(translateIn);
|
||||
animationSet.reset();
|
||||
animationSet.setStartTime(0);
|
||||
|
||||
codeView.setText(String.valueOf(value));
|
||||
codeView.clearAnimation();
|
||||
codeView.startAnimation(animationSet);
|
||||
|
||||
if (index == codes.size() && listener != null) {
|
||||
listener.onCodeComplete(Stream.of(codes).map(TextView::getText).collect(Collectors.joining()));
|
||||
}
|
||||
}
|
||||
|
||||
@MainThread
|
||||
public void delete() {
|
||||
if (index <= 0) return;
|
||||
codes.get(--index).setText("");
|
||||
}
|
||||
|
||||
@MainThread
|
||||
public void clear() {
|
||||
if (index != 0) {
|
||||
Stream.of(codes).forEach(code -> code.setText(""));
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnCodeEnteredListener {
|
||||
void onCodeComplete(@NonNull String code);
|
||||
}
|
||||
}
|
@@ -0,0 +1,174 @@
|
||||
package org.thoughtcrime.securesms.components.registration;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.inputmethodservice.Keyboard;
|
||||
import android.inputmethodservice.KeyboardView;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.OvershootInterpolator;
|
||||
import android.view.animation.ScaleAnimation;
|
||||
import android.view.animation.TranslateAnimation;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
|
||||
|
||||
public class VerificationPinKeyboard extends FrameLayout {
|
||||
|
||||
private KeyboardView keyboardView;
|
||||
private ProgressBar progressBar;
|
||||
private ImageView successView;
|
||||
private ImageView failureView;
|
||||
|
||||
private OnKeyPressListener listener;
|
||||
|
||||
public VerificationPinKeyboard(@NonNull Context context) {
|
||||
super(context);
|
||||
initialize();
|
||||
}
|
||||
|
||||
public VerificationPinKeyboard(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initialize();
|
||||
}
|
||||
|
||||
public VerificationPinKeyboard(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initialize();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public VerificationPinKeyboard(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
initialize();
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
inflate(getContext(), R.layout.verification_pin_keyboard_view, this);
|
||||
|
||||
this.keyboardView = findViewById(R.id.keyboard_view);
|
||||
this.progressBar = findViewById(R.id.progress);
|
||||
this.successView = findViewById(R.id.success);
|
||||
this.failureView = findViewById(R.id.failure); ;
|
||||
|
||||
keyboardView.setPreviewEnabled(false);
|
||||
keyboardView.setKeyboard(new Keyboard(getContext(), R.xml.pin_keyboard));
|
||||
keyboardView.setOnKeyboardActionListener(new KeyboardView.OnKeyboardActionListener() {
|
||||
@Override
|
||||
public void onPress(int primaryCode) {
|
||||
if (listener != null) listener.onKeyPress(primaryCode);
|
||||
}
|
||||
@Override
|
||||
public void onRelease(int primaryCode) {}
|
||||
@Override
|
||||
public void onKey(int primaryCode, int[] keyCodes) {}
|
||||
@Override
|
||||
public void onText(CharSequence text) {}
|
||||
@Override
|
||||
public void swipeLeft() {}
|
||||
@Override
|
||||
public void swipeRight() {}
|
||||
@Override
|
||||
public void swipeDown() {}
|
||||
@Override
|
||||
public void swipeUp() {}
|
||||
});
|
||||
|
||||
displayKeyboard();
|
||||
}
|
||||
|
||||
public void setOnKeyPressListener(@Nullable OnKeyPressListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void displayKeyboard() {
|
||||
this.keyboardView.setVisibility(View.VISIBLE);
|
||||
this.progressBar.setVisibility(View.GONE);
|
||||
this.successView.setVisibility(View.GONE);
|
||||
this.failureView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public void displayProgress() {
|
||||
this.keyboardView.setVisibility(View.INVISIBLE);
|
||||
this.progressBar.setVisibility(View.VISIBLE);
|
||||
this.successView.setVisibility(View.GONE);
|
||||
this.failureView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public ListenableFuture<Boolean> displaySuccess() {
|
||||
SettableFuture<Boolean> result = new SettableFuture<>();
|
||||
|
||||
this.keyboardView.setVisibility(View.INVISIBLE);
|
||||
this.progressBar.setVisibility(View.GONE);
|
||||
this.failureView.setVisibility(View.GONE);
|
||||
|
||||
this.successView.getBackground().setColorFilter(getResources().getColor(R.color.green_500), PorterDuff.Mode.SRC_IN);
|
||||
|
||||
ScaleAnimation scaleAnimation = new ScaleAnimation(0, 1, 0, 1,
|
||||
ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
|
||||
ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
|
||||
scaleAnimation.setInterpolator(new OvershootInterpolator());
|
||||
scaleAnimation.setDuration(800);
|
||||
scaleAnimation.setAnimationListener(new Animation.AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
result.set(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {}
|
||||
});
|
||||
|
||||
ViewUtil.animateIn(this.successView, scaleAnimation);
|
||||
return result;
|
||||
}
|
||||
|
||||
public ListenableFuture<Boolean> displayFailure() {
|
||||
SettableFuture<Boolean> result = new SettableFuture<>();
|
||||
|
||||
this.keyboardView.setVisibility(View.INVISIBLE);
|
||||
this.progressBar.setVisibility(View.GONE);
|
||||
this.failureView.setVisibility(View.GONE);
|
||||
|
||||
this.failureView.getBackground().setColorFilter(getResources().getColor(R.color.red_500), PorterDuff.Mode.SRC_IN);
|
||||
this.failureView.setVisibility(View.VISIBLE);
|
||||
|
||||
TranslateAnimation shake = new TranslateAnimation(0, 30, 0, 0);
|
||||
shake.setDuration(50);
|
||||
shake.setRepeatCount(7);
|
||||
shake.setAnimationListener(new Animation.AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
result.set(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {}
|
||||
});
|
||||
|
||||
this.failureView.startAnimation(shake);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public interface OnKeyPressListener {
|
||||
void onKeyPress(int keyCode);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user