Update registration flow
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator">
|
||||
<translate
|
||||
android:duration="150"
|
||||
android:fromXDelta="0%"
|
||||
android:toXDelta="-100%" />
|
||||
</set>
|
After Width: | Height: | Size: 297 B |
After Width: | Height: | Size: 311 B |
After Width: | Height: | Size: 207 B |
After Width: | Height: | Size: 224 B |
After Width: | Height: | Size: 335 B |
After Width: | Height: | Size: 342 B |
After Width: | Height: | Size: 563 B |
After Width: | Height: | Size: 545 B |
@ -1,137 +1,203 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:fillViewport="true"
|
||||
android:background="@drawable/background_pattern_repeat">
|
||||
|
||||
<LinearLayout android:padding="16dp"
|
||||
android:paddingBottom="0dp"
|
||||
android:layout_marginTop="20dp"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:animateLayoutChanges="true"
|
||||
android:orientation="vertical">
|
||||
android:layout_height="fill_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:fillViewport="true"
|
||||
android:background="@color/white"
|
||||
tools:context=".RegistrationActivity">
|
||||
|
||||
<Spinner android:id="@+id/country_spinner"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dip" />
|
||||
<RelativeLayout android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout android:orientation="horizontal"
|
||||
android:layoutDirection="ltr"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<LinearLayout android:id="@+id/header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/signal_primary"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView android:layout_width="wrap_content"
|
||||
<TextView android:id="@+id/verify_header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="16dp"
|
||||
android:text="@string/registration_activity__verify_your_number"
|
||||
android:textSize="20sp"
|
||||
style="@style/Registration.Constant"
|
||||
android:text="+" />
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/white"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"/>
|
||||
|
||||
<EditText android:id="@+id/country_code"
|
||||
android:layout_width="55dip"
|
||||
<TextView android:id="@+id/verify_subheader"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:gravity="center"
|
||||
android:inputType="phone"
|
||||
android:digits="0123456789"
|
||||
android:maxLength="3" />
|
||||
|
||||
<EditText android:id="@+id/number"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:inputType="phone"
|
||||
android:hint="@string/registration_activity__phone_number"
|
||||
android:gravity="center"
|
||||
android:singleLine="true"/>
|
||||
android:paddingBottom="25dp"
|
||||
android:textColor="@color/white"
|
||||
android:text="@string/registration_activity__please_enter_your_mobile_number_to_receive_a_verification_code_carrier_rates_may_apply"
|
||||
android:gravity="center"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView style="@style/Registration.Description"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_marginBottom="8dip"
|
||||
android:layout_marginTop="16dip"
|
||||
android:gravity="start"
|
||||
android:text="@string/registration_activity__verify_your_number_to_connect_with_signal"/>
|
||||
|
||||
<TextView style="@style/Registration.Description"
|
||||
android:id="@+id/registration_information"
|
||||
android:layout_width="fill_parent"
|
||||
android:gravity="start"
|
||||
android:visibility="gone"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/registration_activity__registration_will_transmit_some_contact_information_to_the_server_temporariliy"/>
|
||||
|
||||
<LinearLayout android:id="@+id/information_link_container"
|
||||
android:clickable="true"
|
||||
android:orientation="horizontal"
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
app:fabSize="normal"
|
||||
app:backgroundTint="@color/white"
|
||||
app:elevation="1dp"
|
||||
android:id="@+id/fab"
|
||||
android:transitionName="icon"
|
||||
android:src="@drawable/ic_action_name"
|
||||
android:tint="@color/grey_700"
|
||||
android:rotation="15"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignBottom="@id/header"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginBottom="-32dp"/>
|
||||
|
||||
<LinearLayout android:id="@+id/registration_container"
|
||||
android:padding="16dp"
|
||||
android:paddingBottom="0dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_below="@id/header"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:animateLayoutChanges="true"
|
||||
android:orientation="vertical"
|
||||
tools:visibility="invisible">
|
||||
|
||||
<Spinner android:id="@+id/country_spinner"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dip" />
|
||||
|
||||
<LinearLayout android:orientation="horizontal"
|
||||
android:layoutDirection="ltr"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="20sp"
|
||||
style="@style/Registration.Constant"
|
||||
android:text="+" />
|
||||
|
||||
<EditText android:id="@+id/country_code"
|
||||
android:layout_width="55dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:gravity="center"
|
||||
android:inputType="phone"
|
||||
android:digits="0123456789"
|
||||
android:maxLength="3" />
|
||||
|
||||
<EditText android:id="@+id/number"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:inputType="phone"
|
||||
android:hint="@string/registration_activity__phone_number"
|
||||
android:gravity="center"
|
||||
android:singleLine="true"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.dd.CircularProgressButton
|
||||
android:id="@+id/registerButton"
|
||||
app:cpb_textIdle="Register"
|
||||
app:cpb_selectorIdle="@drawable/progress_button_state"
|
||||
app:cpb_colorIndicator="@color/white"
|
||||
app:cpb_colorProgress="@color/textsecure_primary"
|
||||
app:cpb_cornerRadius="50dp"
|
||||
android:background="@color/signal_primary"
|
||||
android:textColor="@color/white"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_gravity="center_horizontal"/>
|
||||
|
||||
<TextView android:id="@+id/skip_button"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dip">
|
||||
|
||||
<ImageView android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clickable="false"
|
||||
android:src="@drawable/ic_info_white_18dp"
|
||||
android:tint="#73B7F0"/>
|
||||
android:layout_marginTop="20dp"
|
||||
android:textColor="@color/gray50"
|
||||
android:text="@android:string/cancel"/>
|
||||
|
||||
<TextView style="@style/Registration.Description"
|
||||
android:id="@+id/information_label"
|
||||
android:clickable="false"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="3dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textColor="#73B7F0"
|
||||
android:text="@string/RegistrationActivity_more_information"/>
|
||||
</LinearLayout>
|
||||
android:id="@+id/registration_information"
|
||||
android:layout_width="fill_parent"
|
||||
android:gravity="start"
|
||||
android:visibility="gone"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/registration_activity__registration_will_transmit_some_contact_information_to_the_server_temporariliy"/>
|
||||
|
||||
<LinearLayout android:id="@+id/information_link_container"
|
||||
android:clickable="true"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="16dip">
|
||||
|
||||
<ImageView android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clickable="false"
|
||||
android:src="@drawable/ic_info_white_18dp"
|
||||
android:tint="#73B7F0"/>
|
||||
|
||||
<TextView style="@style/Registration.Description"
|
||||
android:id="@+id/information_label"
|
||||
android:clickable="false"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="3dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textColor="#73B7F0"
|
||||
android:text="@string/RegistrationActivity_more_information"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dip"
|
||||
android:layout_marginBottom="16dip"
|
||||
android:layout_gravity="right"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView android:id="@+id/skipButton"
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingTop="15dp"
|
||||
android:paddingBottom="15dp"
|
||||
android:text="@android:string/cancel"
|
||||
android:textColor="@color/white"
|
||||
android:background="@drawable/pill_button"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginRight="5dip"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView android:id="@+id/registerButton"
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingTop="15dp"
|
||||
android:paddingBottom="15dp"
|
||||
android:text="@string/registration_activity__register"
|
||||
android:textColor="@color/white"
|
||||
android:background="@drawable/pill_button"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView android:id="@+id/twilio_shoutout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_twilio_shoutout_white"
|
||||
android:layout_gravity="right"
|
||||
android:tint="@color/grey_800"
|
||||
android:contentDescription="@string/registration_activity__powered_by_twilio"/>
|
||||
<RelativeLayout android:id="@+id/verification_container"
|
||||
android:visibility="invisible"
|
||||
android:layout_below="@id/header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:visibility="visible">
|
||||
|
||||
<org.thoughtcrime.securesms.components.registration.VerificationCodeView
|
||||
android:id="@+id/code"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="50dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:vcv_inputWidth="30dp"
|
||||
app:vcv_spacing="10dp"
|
||||
app:vcv_textColor="@color/signal_primary"
|
||||
app:vcv_inputColor="@color/grey_600"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.registration.CallMeCountDownView
|
||||
android:id="@+id/call_me_count_down"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/code"
|
||||
android:layout_marginTop="30dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.registration.VerificationPinKeyboard
|
||||
android:id="@+id/keyboard"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"/>
|
||||
|
||||
</RelativeLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:parentTag="android.widget.RelativeLayout">
|
||||
|
||||
<ImageView android:id="@+id/phone_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:src="@drawable/ic_phone_grey600_32dp"/>
|
||||
|
||||
<TextView android:id="@+id/call_me_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toRightOf="@id/phone_icon"
|
||||
android:layout_toEndOf="@id/phone_icon"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:gravity="center_vertical"
|
||||
android:textSize="16sp"
|
||||
android:text="Call me instead"/>
|
||||
|
||||
<TextView android:id="@+id/available_in_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toLeftOf="@+id/countdown"
|
||||
android:layout_toStartOf="@+id/countdown"
|
||||
android:layout_centerVertical="true"
|
||||
android:gravity="center_vertical"
|
||||
android:textSize="16sp"
|
||||
android:text="Available in: "/>
|
||||
|
||||
<TextView android:id="@+id/countdown"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:textSize="16sp"
|
||||
tools:text="1:04"/>
|
||||
|
||||
</merge>
|
@ -0,0 +1,154 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center">
|
||||
|
||||
<LinearLayout android:id="@+id/container_zero"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView android:id="@+id/code_zero"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="30sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="1"/>
|
||||
|
||||
<View android:id="@+id/space_zero"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/black"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout android:id="@+id/container_one"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_marginLeft="5dp">
|
||||
|
||||
<TextView android:id="@+id/code_one"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="30sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="2"/>
|
||||
|
||||
<View android:id="@+id/space_one"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/black"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout android:id="@+id/container_two"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_marginLeft="5dp">
|
||||
|
||||
<TextView android:id="@+id/code_two"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="30sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="2"/>
|
||||
|
||||
<View android:id="@+id/space_two"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/black"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout android:id="@+id/separator_container"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_marginLeft="5dp">
|
||||
|
||||
<TextView android:id="@+id/separator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="30sp"
|
||||
android:textStyle="bold"
|
||||
android:text="-"/>
|
||||
|
||||
<View
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="1dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout android:id="@+id/container_three"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_marginLeft="5dp">
|
||||
|
||||
<TextView android:id="@+id/code_three"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="30sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="2"/>
|
||||
|
||||
<View android:id="@+id/space_three"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/black"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<LinearLayout android:id="@+id/container_four"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_marginLeft="5dp">
|
||||
|
||||
<TextView android:id="@+id/code_four"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="30sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="2"/>
|
||||
|
||||
<View android:id="@+id/space_four"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/black"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout android:id="@+id/container_five"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_marginLeft="5dp">
|
||||
|
||||
<TextView android:id="@+id/code_five"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="30sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="2"/>
|
||||
|
||||
<View android:id="@+id/space_five"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/black"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<android.inputmethodservice.KeyboardView
|
||||
android:id="@+id/keyboard_view"
|
||||
android:keyBackground="@color/grey_300"
|
||||
android:background="@color/grey_300"
|
||||
android:keyTextColor="@color/black"
|
||||
android:shadowColor="@color/transparent"
|
||||
android:keyTextSize="30sp"
|
||||
android:elevation="3dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<ProgressBar android:id="@+id/progress"
|
||||
android:layout_gravity="center"
|
||||
android:layout_width="96dp"
|
||||
android:layout_height="96dp"
|
||||
android:indeterminate="true"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.SquareImageView
|
||||
android:id="@+id/success"
|
||||
android:layout_width="96dp"
|
||||
android:layout_height="96dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/circle_tintable"
|
||||
android:backgroundTint="@color/green_400"
|
||||
android:gravity="center_vertical"
|
||||
android:src="@drawable/ic_check_white_48dp"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.SquareImageView
|
||||
android:id="@+id/failure"
|
||||
android:layout_width="96dp"
|
||||
android:layout_height="96dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/circle_tintable"
|
||||
android:backgroundTint="@color/green_400"
|
||||
android:gravity="center_vertical"
|
||||
android:src="@drawable/ic_close_white_48dp"/>
|
||||
|
||||
|
||||
|
||||
</merge>
|
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:keyWidth="33.3%p"
|
||||
android:keyHeight="10%p">
|
||||
|
||||
<Row>
|
||||
<Key android:codes="1" android:keyLabel="1" android:keyEdgeFlags="left" />
|
||||
<Key android:codes="2" android:keyLabel="2" />
|
||||
<Key android:codes="3" android:keyLabel="3" />
|
||||
</Row>
|
||||
|
||||
<Row>
|
||||
<Key android:codes="4" android:keyLabel="4" android:keyEdgeFlags="left" />
|
||||
<Key android:codes="5" android:keyLabel="5" />
|
||||
<Key android:codes="6" android:keyLabel="6" />
|
||||
</Row>
|
||||
|
||||
<Row>
|
||||
<Key android:codes="7" android:keyLabel="7" android:keyEdgeFlags="left" />
|
||||
<Key android:codes="8" android:keyLabel="8" />
|
||||
<Key android:codes="9" android:keyLabel="9" />
|
||||
</Row>
|
||||
<Row>
|
||||
<Key android:codes="0" android:keyLabel="0" android:horizontalGap="33.3%p"/>
|
||||
<Key android:codes="-1" android:keyIcon="@drawable/ic_backspace_grey600_24dp" android:isRepeatable="true"/>
|
||||
</Row>
|
||||
</Keyboard>
|
@ -1,653 +0,0 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.ServiceConnection;
|
||||
import android.graphics.Color;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.crypto.SessionUtil;
|
||||
import org.thoughtcrime.securesms.push.AccountManagerFactory;
|
||||
import org.thoughtcrime.securesms.service.RegistrationService;
|
||||
import org.thoughtcrime.securesms.util.Dialogs;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.libsignal.util.KeyHelper;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.ExpectationFailedException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.RateLimitException;
|
||||
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.thoughtcrime.securesms.service.RegistrationService.RegistrationState;
|
||||
|
||||
public class RegistrationProgressActivity extends BaseActionBarActivity {
|
||||
|
||||
private static final String TAG = RegistrationProgressActivity.class.getSimpleName();
|
||||
|
||||
public static final String NUMBER_EXTRA = "e164number";
|
||||
public static final String MASTER_SECRET_EXTRA = "master_secret";
|
||||
public static final String GCM_SUPPORTED_EXTRA = "gcm_supported";
|
||||
|
||||
private static final int FOCUSED_COLOR = Color.parseColor("#ff333333");
|
||||
private static final int UNFOCUSED_COLOR = Color.parseColor("#ff808080");
|
||||
|
||||
private ServiceConnection serviceConnection = new RegistrationServiceConnection();
|
||||
private Handler registrationStateHandler = new RegistrationStateHandler();
|
||||
private RegistrationReceiver registrationReceiver = new RegistrationReceiver();
|
||||
|
||||
private RegistrationService registrationService;
|
||||
|
||||
private LinearLayout registrationLayout;
|
||||
private LinearLayout verificationFailureLayout;
|
||||
private LinearLayout connectivityFailureLayout;
|
||||
private RelativeLayout timeoutProgressLayout;
|
||||
|
||||
private ProgressBar registrationProgress;
|
||||
private ProgressBar connectingProgress;
|
||||
private ProgressBar verificationProgress;
|
||||
private ProgressBar generatingKeysProgress;
|
||||
private ProgressBar gcmRegistrationProgress;
|
||||
|
||||
|
||||
private ImageView connectingCheck;
|
||||
private ImageView verificationCheck;
|
||||
private ImageView generatingKeysCheck;
|
||||
private ImageView gcmRegistrationCheck;
|
||||
|
||||
private TextView connectingText;
|
||||
private TextView verificationText;
|
||||
private TextView registrationTimerText;
|
||||
private TextView generatingKeysText;
|
||||
private TextView gcmRegistrationText;
|
||||
|
||||
private Button verificationFailureButton;
|
||||
private Button connectivityFailureButton;
|
||||
private Button callButton;
|
||||
private Button verifyButton;
|
||||
|
||||
private EditText codeEditText;
|
||||
|
||||
private MasterSecret masterSecret;
|
||||
private boolean gcmSupported;
|
||||
private volatile boolean visible;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
getSupportActionBar().setTitle(getString(R.string.RegistrationProgressActivity_verifying_number));
|
||||
setContentView(R.layout.registration_progress_activity);
|
||||
|
||||
initializeResources();
|
||||
initializeLinks();
|
||||
initializeServiceBinding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
shutdownServiceBinding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
handleActivityVisible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
handleActivityNotVisible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
|
||||
}
|
||||
|
||||
private void initializeServiceBinding() {
|
||||
Intent intent = new Intent(this, RegistrationService.class);
|
||||
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
this.masterSecret = getIntent().getParcelableExtra(MASTER_SECRET_EXTRA);
|
||||
this.gcmSupported = getIntent().getBooleanExtra(GCM_SUPPORTED_EXTRA, true);
|
||||
this.registrationLayout = (LinearLayout)findViewById(R.id.registering_layout);
|
||||
this.verificationFailureLayout = (LinearLayout)findViewById(R.id.verification_failure_layout);
|
||||
this.connectivityFailureLayout = (LinearLayout)findViewById(R.id.connectivity_failure_layout);
|
||||
this.registrationProgress = (ProgressBar) findViewById(R.id.registration_progress);
|
||||
this.connectingProgress = (ProgressBar) findViewById(R.id.connecting_progress);
|
||||
this.verificationProgress = (ProgressBar) findViewById(R.id.verification_progress);
|
||||
this.generatingKeysProgress = (ProgressBar) findViewById(R.id.generating_keys_progress);
|
||||
this.gcmRegistrationProgress = (ProgressBar) findViewById(R.id.gcm_registering_progress);
|
||||
this.connectingCheck = (ImageView) findViewById(R.id.connecting_complete);
|
||||
this.verificationCheck = (ImageView) findViewById(R.id.verification_complete);
|
||||
this.generatingKeysCheck = (ImageView) findViewById(R.id.generating_keys_complete);
|
||||
this.gcmRegistrationCheck = (ImageView) findViewById(R.id.gcm_registering_complete);
|
||||
this.connectingText = (TextView) findViewById(R.id.connecting_text);
|
||||
this.verificationText = (TextView) findViewById(R.id.verification_text);
|
||||
this.registrationTimerText = (TextView) findViewById(R.id.registration_timer);
|
||||
this.generatingKeysText = (TextView) findViewById(R.id.generating_keys_text);
|
||||
this.gcmRegistrationText = (TextView) findViewById(R.id.gcm_registering_text);
|
||||
this.verificationFailureButton = (Button) findViewById(R.id.verification_failure_edit_button);
|
||||
this.connectivityFailureButton = (Button) findViewById(R.id.connectivity_failure_edit_button);
|
||||
this.callButton = (Button) findViewById(R.id.call_button);
|
||||
this.verifyButton = (Button) findViewById(R.id.verify_button);
|
||||
this.codeEditText = (EditText) findViewById(R.id.telephone_code);
|
||||
this.timeoutProgressLayout = (RelativeLayout) findViewById(R.id.timer_progress_layout);
|
||||
Button editButton = (Button) findViewById(R.id.edit_button);
|
||||
|
||||
editButton.setOnClickListener(new EditButtonListener());
|
||||
this.verificationFailureButton.setOnClickListener(new EditButtonListener());
|
||||
this.connectivityFailureButton.setOnClickListener(new EditButtonListener());
|
||||
}
|
||||
|
||||
private void initializeLinks() {
|
||||
TextView failureText = (TextView) findViewById(R.id.sms_failed_text);
|
||||
String pretext = getString(R.string.registration_progress_activity__signal_timed_out_while_waiting_for_a_verification_sms_message);
|
||||
String link = getString(R.string.RegistrationProblemsActivity_possible_problems);
|
||||
SpannableString spannableString = new SpannableString(pretext + " " + link);
|
||||
|
||||
spannableString.setSpan(new ClickableSpan() {
|
||||
@Override
|
||||
public void onClick(View widget) {
|
||||
new AlertDialog.Builder(RegistrationProgressActivity.this)
|
||||
.setTitle(R.string.RegistrationProblemsActivity_possible_problems)
|
||||
.setView(R.layout.registration_problems)
|
||||
.setNeutralButton(android.R.string.ok, null)
|
||||
.show();
|
||||
}
|
||||
}, pretext.length() + 1, spannableString.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
failureText.setText(spannableString);
|
||||
failureText.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
|
||||
private void handleActivityVisible() {
|
||||
IntentFilter filter = new IntentFilter(RegistrationService.REGISTRATION_EVENT);
|
||||
filter.setPriority(1000);
|
||||
registerReceiver(registrationReceiver, filter);
|
||||
visible = true;
|
||||
}
|
||||
|
||||
private void handleActivityNotVisible() {
|
||||
unregisterReceiver(registrationReceiver);
|
||||
visible = false;
|
||||
}
|
||||
|
||||
private void handleStateIdle() {
|
||||
if (hasNumberDirective()) {
|
||||
Intent intent = new Intent(this, RegistrationService.class);
|
||||
intent.setAction(RegistrationService.REGISTER_NUMBER_ACTION);
|
||||
intent.putExtra(RegistrationService.NUMBER_EXTRA, getNumberDirective());
|
||||
intent.putExtra(RegistrationService.MASTER_SECRET_EXTRA, masterSecret);
|
||||
intent.putExtra(RegistrationService.GCM_SUPPORTED_EXTRA, gcmSupported);
|
||||
startService(intent);
|
||||
} else {
|
||||
Intent intent = new Intent(this, RegistrationActivity.class);
|
||||
intent.putExtra("master_secret", masterSecret);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleStateConnecting() {
|
||||
this.registrationLayout.setVisibility(View.VISIBLE);
|
||||
this.verificationFailureLayout.setVisibility(View.GONE);
|
||||
this.connectivityFailureLayout.setVisibility(View.GONE);
|
||||
this.connectingProgress.setVisibility(View.VISIBLE);
|
||||
this.connectingCheck.setVisibility(View.INVISIBLE);
|
||||
this.verificationProgress.setVisibility(View.INVISIBLE);
|
||||
this.verificationCheck.setVisibility(View.INVISIBLE);
|
||||
this.generatingKeysProgress.setVisibility(View.INVISIBLE);
|
||||
this.generatingKeysCheck.setVisibility(View.INVISIBLE);
|
||||
this.gcmRegistrationProgress.setVisibility(View.INVISIBLE);
|
||||
this.gcmRegistrationCheck.setVisibility(View.INVISIBLE);
|
||||
this.connectingText.setTextColor(FOCUSED_COLOR);
|
||||
this.verificationText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.generatingKeysText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.gcmRegistrationText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.timeoutProgressLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void handleStateVerifying() {
|
||||
this.registrationLayout.setVisibility(View.VISIBLE);
|
||||
this.verificationFailureLayout.setVisibility(View.GONE);
|
||||
this.connectivityFailureLayout.setVisibility(View.GONE);
|
||||
this.connectingProgress.setVisibility(View.INVISIBLE);
|
||||
this.connectingCheck.setVisibility(View.VISIBLE);
|
||||
this.verificationProgress.setVisibility(View.VISIBLE);
|
||||
this.verificationCheck.setVisibility(View.INVISIBLE);
|
||||
this.generatingKeysProgress.setVisibility(View.INVISIBLE);
|
||||
this.generatingKeysCheck.setVisibility(View.INVISIBLE);
|
||||
this.gcmRegistrationProgress.setVisibility(View.INVISIBLE);
|
||||
this.gcmRegistrationCheck.setVisibility(View.INVISIBLE);
|
||||
this.connectingText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.verificationText.setTextColor(FOCUSED_COLOR);
|
||||
this.generatingKeysText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.gcmRegistrationText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.registrationProgress.setVisibility(View.VISIBLE);
|
||||
this.timeoutProgressLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void handleStateGeneratingKeys() {
|
||||
this.registrationLayout.setVisibility(View.VISIBLE);
|
||||
this.verificationFailureLayout.setVisibility(View.GONE);
|
||||
this.connectivityFailureLayout.setVisibility(View.GONE);
|
||||
this.connectingProgress.setVisibility(View.INVISIBLE);
|
||||
this.connectingCheck.setVisibility(View.VISIBLE);
|
||||
this.verificationProgress.setVisibility(View.INVISIBLE);
|
||||
this.verificationCheck.setVisibility(View.VISIBLE);
|
||||
this.generatingKeysProgress.setVisibility(View.VISIBLE);
|
||||
this.generatingKeysCheck.setVisibility(View.INVISIBLE);
|
||||
this.gcmRegistrationProgress.setVisibility(View.INVISIBLE);
|
||||
this.gcmRegistrationCheck.setVisibility(View.INVISIBLE);
|
||||
this.connectingText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.verificationText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.generatingKeysText.setTextColor(FOCUSED_COLOR);
|
||||
this.gcmRegistrationText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.registrationProgress.setVisibility(View.INVISIBLE);
|
||||
this.timeoutProgressLayout.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
private void handleStateGcmRegistering() {
|
||||
this.registrationLayout.setVisibility(View.VISIBLE);
|
||||
this.verificationFailureLayout.setVisibility(View.GONE);
|
||||
this.connectivityFailureLayout.setVisibility(View.GONE);
|
||||
this.connectingProgress.setVisibility(View.INVISIBLE);
|
||||
this.connectingCheck.setVisibility(View.VISIBLE);
|
||||
this.verificationProgress.setVisibility(View.INVISIBLE);
|
||||
this.verificationCheck.setVisibility(View.VISIBLE);
|
||||
this.generatingKeysProgress.setVisibility(View.INVISIBLE);
|
||||
this.generatingKeysCheck.setVisibility(View.VISIBLE);
|
||||
this.gcmRegistrationProgress.setVisibility(View.VISIBLE);
|
||||
this.gcmRegistrationCheck.setVisibility(View.INVISIBLE);
|
||||
this.connectingText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.verificationText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.generatingKeysText.setTextColor(UNFOCUSED_COLOR);
|
||||
this.gcmRegistrationText.setTextColor(FOCUSED_COLOR);
|
||||
this.registrationProgress.setVisibility(View.INVISIBLE);
|
||||
this.timeoutProgressLayout.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
private void handleGcmTimeout(RegistrationState state) {
|
||||
handleConnectivityError(state);
|
||||
}
|
||||
|
||||
private void handleVerificationRequestedVoice(RegistrationState state) {
|
||||
handleVerificationTimeout(state);
|
||||
verifyButton.setOnClickListener(new VerifyClickListener(state.number, state.password, gcmSupported));
|
||||
verifyButton.setEnabled(true);
|
||||
codeEditText.setEnabled(true);
|
||||
}
|
||||
|
||||
private void handleVerificationTimeout(RegistrationState state) {
|
||||
this.callButton.setOnClickListener(new CallClickListener(state.number));
|
||||
this.verifyButton.setEnabled(false);
|
||||
this.codeEditText.setEnabled(false);
|
||||
this.registrationLayout.setVisibility(View.GONE);
|
||||
this.connectivityFailureLayout.setVisibility(View.GONE);
|
||||
this.verificationFailureLayout.setVisibility(View.VISIBLE);
|
||||
this.verificationFailureButton.setText(String.format(getString(R.string.RegistrationProgressActivity_edit_s),
|
||||
PhoneNumberFormatter.formatNumberInternational(state.number)));
|
||||
}
|
||||
|
||||
private void handleConnectivityError(RegistrationState state) {
|
||||
this.registrationLayout.setVisibility(View.GONE);
|
||||
this.verificationFailureLayout.setVisibility(View.GONE);
|
||||
this.connectivityFailureLayout.setVisibility(View.VISIBLE);
|
||||
this.connectivityFailureButton.setText(String.format(getString(R.string.RegistrationProgressActivity_edit_s),
|
||||
PhoneNumberFormatter.formatNumberInternational(state.number)));
|
||||
}
|
||||
|
||||
private void handleMultiRegistrationError(RegistrationState state) {
|
||||
handleVerificationTimeout(state);
|
||||
Dialogs.showAlertDialog(this, getString(R.string.RegistrationProgressActivity_registration_conflict),
|
||||
getString(R.string.RegistrationProgressActivity_this_number_is_already_registered_on_a_different));
|
||||
}
|
||||
|
||||
private void handleVerificationComplete() {
|
||||
if (visible) {
|
||||
Toast.makeText(this,
|
||||
R.string.RegistrationProgressActivity_registration_complete,
|
||||
Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
shutdownService();
|
||||
Intent intent = new Intent(this, CreateProfileActivity.class);
|
||||
intent.putExtra(CreateProfileActivity.NEXT_INTENT, new Intent(this, ConversationListActivity.class));
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
private void handleTimerUpdate() {
|
||||
if (registrationService == null)
|
||||
return;
|
||||
|
||||
int totalSecondsRemaining = registrationService.getSecondsRemaining();
|
||||
int minutesRemaining = totalSecondsRemaining / 60;
|
||||
int secondsRemaining = totalSecondsRemaining - (minutesRemaining * 60);
|
||||
double percentageComplete = (double)((60 * 2) - totalSecondsRemaining) / (double)(60 * 2);
|
||||
int progress = (int)Math.round(((double)registrationProgress.getMax()) * percentageComplete);
|
||||
|
||||
this.registrationProgress.setProgress(progress);
|
||||
this.registrationTimerText.setText(String.format("%02d:%02d", minutesRemaining, secondsRemaining));
|
||||
|
||||
registrationStateHandler.sendEmptyMessageDelayed(RegistrationState.STATE_TIMER, 1000);
|
||||
}
|
||||
|
||||
private boolean hasNumberDirective() {
|
||||
return getIntent().getStringExtra(NUMBER_EXTRA) != null;
|
||||
}
|
||||
|
||||
private String getNumberDirective() {
|
||||
return getIntent().getStringExtra(NUMBER_EXTRA);
|
||||
}
|
||||
|
||||
private void shutdownServiceBinding() {
|
||||
if (serviceConnection != null) {
|
||||
unbindService(serviceConnection);
|
||||
serviceConnection = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void shutdownService() {
|
||||
if (registrationService != null) {
|
||||
registrationService.shutdown();
|
||||
registrationService = null;
|
||||
}
|
||||
|
||||
shutdownServiceBinding();
|
||||
|
||||
Intent serviceIntent = new Intent(RegistrationProgressActivity.this, RegistrationService.class);
|
||||
stopService(serviceIntent);
|
||||
}
|
||||
|
||||
private class RegistrationServiceConnection implements ServiceConnection {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
registrationService = ((RegistrationService.RegistrationServiceBinder)service).getService();
|
||||
registrationService.setRegistrationStateHandler(registrationStateHandler);
|
||||
|
||||
RegistrationState state = registrationService.getRegistrationState();
|
||||
registrationStateHandler.obtainMessage(state.state, state).sendToTarget();
|
||||
|
||||
handleTimerUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
registrationService.setRegistrationStateHandler(null);
|
||||
}
|
||||
}
|
||||
|
||||
private class RegistrationStateHandler extends Handler {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
RegistrationState state = (RegistrationState)message.obj;
|
||||
|
||||
switch (message.what) {
|
||||
case RegistrationState.STATE_IDLE: handleStateIdle(); break;
|
||||
case RegistrationState.STATE_CONNECTING: handleStateConnecting(); break;
|
||||
case RegistrationState.STATE_VERIFYING: handleStateVerifying(); break;
|
||||
case RegistrationState.STATE_TIMER: handleTimerUpdate(); break;
|
||||
case RegistrationState.STATE_GENERATING_KEYS: handleStateGeneratingKeys(); break;
|
||||
case RegistrationState.STATE_GCM_REGISTERING: handleStateGcmRegistering(); break;
|
||||
case RegistrationState.STATE_TIMEOUT: handleVerificationTimeout(state); break;
|
||||
case RegistrationState.STATE_COMPLETE: handleVerificationComplete(); break;
|
||||
case RegistrationState.STATE_GCM_TIMEOUT: handleGcmTimeout(state); break;
|
||||
case RegistrationState.STATE_NETWORK_ERROR: handleConnectivityError(state); break;
|
||||
case RegistrationState.STATE_MULTI_REGISTERED: handleMultiRegistrationError(state); break;
|
||||
case RegistrationState.STATE_VOICE_REQUESTED: handleVerificationRequestedVoice(state); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class EditButtonListener implements View.OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
shutdownService();
|
||||
|
||||
Intent activityIntent = new Intent(RegistrationProgressActivity.this, RegistrationActivity.class);
|
||||
activityIntent.putExtra(RegistrationProgressActivity.MASTER_SECRET_EXTRA, masterSecret);
|
||||
activityIntent.putExtra(RegistrationProgressActivity.GCM_SUPPORTED_EXTRA, gcmSupported);
|
||||
startActivity(activityIntent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
private static class RegistrationReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
abortBroadcast();
|
||||
}
|
||||
}
|
||||
|
||||
private class VerifyClickListener implements View.OnClickListener {
|
||||
|
||||
private static final int SUCCESS = 0;
|
||||
private static final int NETWORK_ERROR = 1;
|
||||
private static final int RATE_LIMIT_ERROR = 2;
|
||||
private static final int VERIFICATION_ERROR = 3;
|
||||
private static final int MULTI_REGISTRATION_ERROR = 4;
|
||||
|
||||
private final String e164number;
|
||||
private final String password;
|
||||
private final String signalingKey;
|
||||
private final boolean gcmSupported;
|
||||
private final Context context;
|
||||
|
||||
private ProgressDialog progressDialog;
|
||||
|
||||
public VerifyClickListener(String e164number, String password, boolean gcmSupported) {
|
||||
this.e164number = e164number;
|
||||
this.password = password;
|
||||
this.signalingKey = Util.getSecret(52);
|
||||
this.gcmSupported = gcmSupported;
|
||||
this.context = RegistrationProgressActivity.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final String code = codeEditText.getText().toString();
|
||||
|
||||
if (TextUtils.isEmpty(code)) {
|
||||
Toast.makeText(context,
|
||||
getString(R.string.RegistrationProgressActivity_you_must_enter_the_code_you_received_first),
|
||||
Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
new AsyncTask<Void, Void, Integer>() {
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
progressDialog = ProgressDialog.show(context,
|
||||
getString(R.string.RegistrationProgressActivity_connecting),
|
||||
getString(R.string.RegistrationProgressActivity_connecting_for_verification),
|
||||
true, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Integer result) {
|
||||
if (progressDialog != null) progressDialog.dismiss();
|
||||
|
||||
switch (result) {
|
||||
case SUCCESS:
|
||||
Intent intent = new Intent(context, RegistrationService.class);
|
||||
intent.setAction(RegistrationService.VOICE_REGISTER_ACTION);
|
||||
intent.putExtra(RegistrationService.NUMBER_EXTRA, e164number);
|
||||
intent.putExtra(RegistrationService.PASSWORD_EXTRA, password);
|
||||
intent.putExtra(RegistrationService.SIGNALING_KEY_EXTRA, signalingKey);
|
||||
intent.putExtra(RegistrationService.MASTER_SECRET_EXTRA, masterSecret);
|
||||
intent.putExtra(RegistrationService.GCM_SUPPORTED_EXTRA, gcmSupported);
|
||||
startService(intent);
|
||||
break;
|
||||
case NETWORK_ERROR:
|
||||
Dialogs.showAlertDialog(context, getString(R.string.RegistrationProgressActivity_network_error),
|
||||
getString(R.string.RegistrationProgressActivity_unable_to_connect));
|
||||
break;
|
||||
case VERIFICATION_ERROR:
|
||||
Dialogs.showAlertDialog(context, getString(R.string.RegistrationProgressActivity_verification_failed),
|
||||
getString(R.string.RegistrationProgressActivity_the_verification_code_you_submitted_is_incorrect));
|
||||
break;
|
||||
case RATE_LIMIT_ERROR:
|
||||
Dialogs.showAlertDialog(context, getString(R.string.RegistrationProgressActivity_too_many_attempts),
|
||||
getString(R.string.RegistrationProgressActivity_youve_submitted_an_incorrect_verification_code_too_many_times));
|
||||
break;
|
||||
case MULTI_REGISTRATION_ERROR:
|
||||
Dialogs.showAlertDialog(context, getString(R.string.RegistrationProgressActivity_registration_conflict),
|
||||
getString(R.string.RegistrationProgressActivity_this_number_is_already_registered_on_a_different));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer doInBackground(Void... params) {
|
||||
try {
|
||||
SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context, e164number, password);
|
||||
int registrationId = KeyHelper.generateRegistrationId(false);
|
||||
|
||||
TextSecurePreferences.setLocalRegistrationId(context, registrationId);
|
||||
SessionUtil.archiveAllSessions(context);
|
||||
|
||||
accountManager.verifyAccountWithCode(code, signalingKey, registrationId, !gcmSupported);
|
||||
|
||||
return SUCCESS;
|
||||
} catch (ExpectationFailedException e) {
|
||||
Log.w(TAG, e);
|
||||
return MULTI_REGISTRATION_ERROR;
|
||||
} catch (RateLimitException e) {
|
||||
Log.w(TAG, e);
|
||||
return RATE_LIMIT_ERROR;
|
||||
} catch (AuthorizationFailedException e) {
|
||||
Log.w(TAG, e);
|
||||
return VERIFICATION_ERROR;
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
return NETWORK_ERROR;
|
||||
}
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
}
|
||||
|
||||
private class CallClickListener implements View.OnClickListener {
|
||||
|
||||
private static final int SUCCESS = 0;
|
||||
private static final int NETWORK_ERROR = 1;
|
||||
private static final int RATE_LIMIT_EXCEEDED = 2;
|
||||
private static final int CREATE_ERROR = 3;
|
||||
|
||||
private final String e164number;
|
||||
private final String password;
|
||||
private final Context context;
|
||||
|
||||
public CallClickListener(String e164number) {
|
||||
this.e164number = e164number;
|
||||
this.password = Util.getSecret(18);
|
||||
this.context = RegistrationProgressActivity.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
new AsyncTask<Void, Void, Integer>() {
|
||||
private ProgressDialog progressDialog;
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
progressDialog = ProgressDialog.show(context,
|
||||
getString(R.string.RegistrationProgressActivity_requesting_call),
|
||||
getString(R.string.RegistrationProgressActivity_requesting_incoming_call),
|
||||
true, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Integer result) {
|
||||
if (progressDialog != null) progressDialog.dismiss();
|
||||
|
||||
switch (result) {
|
||||
case SUCCESS:
|
||||
Intent intent = new Intent(context, RegistrationService.class);
|
||||
intent.setAction(RegistrationService.VOICE_REQUESTED_ACTION);
|
||||
intent.putExtra(RegistrationService.NUMBER_EXTRA, e164number);
|
||||
intent.putExtra(RegistrationService.PASSWORD_EXTRA, password);
|
||||
intent.putExtra(RegistrationService.MASTER_SECRET_EXTRA, masterSecret);
|
||||
intent.putExtra(RegistrationService.GCM_SUPPORTED_EXTRA, gcmSupported);
|
||||
startService(intent);
|
||||
|
||||
callButton.setEnabled(false);
|
||||
new Handler().postDelayed(new Runnable(){
|
||||
@Override
|
||||
public void run() {
|
||||
callButton.setEnabled(true);
|
||||
}
|
||||
}, 15000);
|
||||
break;
|
||||
case NETWORK_ERROR:
|
||||
Dialogs.showAlertDialog(context,
|
||||
getString(R.string.RegistrationProgressActivity_network_error),
|
||||
getString(R.string.RegistrationProgressActivity_unable_to_connect));
|
||||
break;
|
||||
case CREATE_ERROR:
|
||||
Dialogs.showAlertDialog(context,
|
||||
getString(R.string.RegistrationProgressActivity_server_error),
|
||||
getString(R.string.RegistrationProgressActivity_the_server_encountered_an_error));
|
||||
break;
|
||||
case RATE_LIMIT_EXCEEDED:
|
||||
Dialogs.showAlertDialog(context,
|
||||
getString(R.string.RegistrationProgressActivity_too_many_requests),
|
||||
getString(R.string.RegistrationProgressActivity_youve_already_requested_a_voice_call));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer doInBackground(Void... params) {
|
||||
try {
|
||||
SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context, e164number, password);
|
||||
accountManager.requestVoiceVerificationCode();
|
||||
|
||||
return SUCCESS;
|
||||
} catch (RateLimitException e) {
|
||||
Log.w(TAG, e);
|
||||
return RATE_LIMIT_EXCEEDED;
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
return NETWORK_ERROR;
|
||||
}
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package org.thoughtcrime.securesms.animation;
|
||||
|
||||
|
||||
import android.animation.Animator;
|
||||
|
||||
public abstract class AnimationCompleteListener implements Animator.AnimatorListener {
|
||||
@Override
|
||||
public final void onAnimationStart(Animator animation) {}
|
||||
|
||||
@Override
|
||||
public abstract void onAnimationEnd(Animator animation);
|
||||
|
||||
@Override
|
||||
public final void onAnimationCancel(Animator animation) {}
|
||||
@Override
|
||||
public final void onAnimationRepeat(Animator animation) {}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.GoogleApiAvailability;
|
||||
|
||||
public class PlayServicesUtil {
|
||||
|
||||
private static final String TAG = PlayServicesUtil.class.getSimpleName();
|
||||
|
||||
public enum PlayServicesStatus {
|
||||
SUCCESS,
|
||||
MISSING,
|
||||
NEEDS_UPDATE,
|
||||
TRANSIENT_ERROR
|
||||
}
|
||||
|
||||
public static PlayServicesStatus getPlayServicesStatus(Context context) {
|
||||
int gcmStatus = 0;
|
||||
|
||||
try {
|
||||
gcmStatus = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context);
|
||||
} catch (Throwable t) {
|
||||
Log.w(TAG, t);
|
||||
return PlayServicesStatus.MISSING;
|
||||
}
|
||||
|
||||
Log.w(TAG, "Play Services: " + gcmStatus);
|
||||
|
||||
switch (gcmStatus) {
|
||||
case ConnectionResult.SUCCESS:
|
||||
return PlayServicesStatus.SUCCESS;
|
||||
case ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED:
|
||||
try {
|
||||
ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo("com.google.android.gms", 0);
|
||||
|
||||
if (applicationInfo != null && !applicationInfo.enabled) {
|
||||
return PlayServicesStatus.MISSING;
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
|
||||
return PlayServicesStatus.NEEDS_UPDATE;
|
||||
case ConnectionResult.SERVICE_DISABLED:
|
||||
case ConnectionResult.SERVICE_MISSING:
|
||||
case ConnectionResult.SERVICE_INVALID:
|
||||
case ConnectionResult.API_UNAVAILABLE:
|
||||
case ConnectionResult.SERVICE_MISSING_PERMISSION:
|
||||
return PlayServicesStatus.MISSING;
|
||||
default:
|
||||
return PlayServicesStatus.TRANSIENT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
}
|