Update registration flow
@ -289,12 +289,11 @@
|
|||||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||||
|
|
||||||
<activity android:name=".RegistrationActivity"
|
<activity android:name=".RegistrationActivity"
|
||||||
|
android:launchMode="singleTask"
|
||||||
|
android:theme="@style/TextSecure.LightNoActionBar"
|
||||||
android:windowSoftInputMode="stateUnchanged"
|
android:windowSoftInputMode="stateUnchanged"
|
||||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||||
|
|
||||||
<activity android:name=".RegistrationProgressActivity"
|
|
||||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
|
||||||
|
|
||||||
<activity android:name=".DeviceActivity"
|
<activity android:name=".DeviceActivity"
|
||||||
android:label="@string/AndroidManifest__linked_devices"
|
android:label="@string/AndroidManifest__linked_devices"
|
||||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||||
@ -400,10 +399,8 @@
|
|||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<service android:enabled="true" android:name="org.thoughtcrime.securesms.service.WebRtcCallService"/>
|
<service android:enabled="true" android:name="org.thoughtcrime.securesms.service.WebRtcCallService"/>
|
||||||
|
|
||||||
<service android:enabled="true" android:name=".service.ApplicationMigrationService"/>
|
<service android:enabled="true" android:name=".service.ApplicationMigrationService"/>
|
||||||
<service android:enabled="true" android:name=".service.KeyCachingService"/>
|
<service android:enabled="true" android:name=".service.KeyCachingService"/>
|
||||||
<service android:enabled="true" android:name=".service.RegistrationService"/>
|
|
||||||
<service android:enabled="true" android:name=".service.MessageRetrievalService"/>
|
<service android:enabled="true" android:name=".service.MessageRetrievalService"/>
|
||||||
|
|
||||||
<service android:name=".service.QuickResponseService"
|
<service android:name=".service.QuickResponseService"
|
||||||
|
9
res/anim/slide_to_left.xml
Normal file
@ -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>
|
BIN
res/drawable-hdpi/ic_action_name.png
Normal file
After Width: | Height: | Size: 297 B |
BIN
res/drawable-hdpi/ic_text_sms.png
Normal file
After Width: | Height: | Size: 311 B |
BIN
res/drawable-mdpi/ic_action_name.png
Normal file
After Width: | Height: | Size: 207 B |
BIN
res/drawable-mdpi/ic_text_sms.png
Normal file
After Width: | Height: | Size: 224 B |
BIN
res/drawable-xhdpi/ic_action_name.png
Normal file
After Width: | Height: | Size: 335 B |
BIN
res/drawable-xhdpi/ic_text_sms.png
Normal file
After Width: | Height: | Size: 342 B |
BIN
res/drawable-xxhdpi/ic_action_name.png
Normal file
After Width: | Height: | Size: 563 B |
BIN
res/drawable-xxhdpi/ic_text_sms.png
Normal file
After Width: | Height: | Size: 545 B |
@ -1,137 +1,203 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="fill_parent"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
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"
|
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="fill_parent"
|
||||||
android:animateLayoutChanges="true"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:orientation="vertical">
|
android:fillViewport="true"
|
||||||
|
android:background="@color/white"
|
||||||
|
tools:context=".RegistrationActivity">
|
||||||
|
|
||||||
<Spinner android:id="@+id/country_spinner"
|
<RelativeLayout android:layout_width="match_parent"
|
||||||
android:layout_width="fill_parent"
|
android:layout_height="wrap_content">
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="16dip" />
|
|
||||||
|
|
||||||
<LinearLayout android:orientation="horizontal"
|
<LinearLayout android:id="@+id/header"
|
||||||
android:layoutDirection="ltr"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<TextView android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@color/signal_primary"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<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"
|
android:textSize="20sp"
|
||||||
style="@style/Registration.Constant"
|
android:textStyle="bold"
|
||||||
android:text="+" />
|
android:textColor="@color/white"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center"/>
|
||||||
|
|
||||||
<EditText android:id="@+id/country_code"
|
<TextView android:id="@+id/verify_subheader"
|
||||||
android:layout_width="55dip"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:singleLine="true"
|
android:paddingBottom="25dp"
|
||||||
android:gravity="center"
|
android:textColor="@color/white"
|
||||||
android:inputType="phone"
|
android:text="@string/registration_activity__please_enter_your_mobile_number_to_receive_a_verification_code_carrier_rates_may_apply"
|
||||||
android:digits="0123456789"
|
android:gravity="center"/>
|
||||||
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>
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView style="@style/Registration.Description"
|
<android.support.design.widget.FloatingActionButton
|
||||||
android:layout_width="fill_parent"
|
app:fabSize="normal"
|
||||||
android:layout_marginBottom="8dip"
|
app:backgroundTint="@color/white"
|
||||||
android:layout_marginTop="16dip"
|
app:elevation="1dp"
|
||||||
android:gravity="start"
|
android:id="@+id/fab"
|
||||||
android:text="@string/registration_activity__verify_your_number_to_connect_with_signal"/>
|
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"/>
|
||||||
|
|
||||||
<TextView style="@style/Registration.Description"
|
<LinearLayout android:id="@+id/registration_container"
|
||||||
android:id="@+id/registration_information"
|
android:padding="16dp"
|
||||||
android:layout_width="fill_parent"
|
android:paddingBottom="0dp"
|
||||||
android:gravity="start"
|
android:layout_marginTop="30dp"
|
||||||
android:visibility="gone"
|
android:layout_below="@id/header"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_width="fill_parent"
|
||||||
android:text="@string/registration_activity__registration_will_transmit_some_contact_information_to_the_server_temporariliy"/>
|
android:layout_height="wrap_content"
|
||||||
|
android:animateLayoutChanges="true"
|
||||||
|
android:orientation="vertical"
|
||||||
|
tools:visibility="invisible">
|
||||||
|
|
||||||
<LinearLayout android:id="@+id/information_link_container"
|
<Spinner android:id="@+id/country_spinner"
|
||||||
android:clickable="true"
|
android:layout_width="fill_parent"
|
||||||
android:orientation="horizontal"
|
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_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="16dip">
|
android:layout_marginTop="20dp"
|
||||||
|
android:textColor="@color/gray50"
|
||||||
<ImageView android:layout_width="wrap_content"
|
android:text="@android:string/cancel"/>
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:clickable="false"
|
|
||||||
android:src="@drawable/ic_info_white_18dp"
|
|
||||||
android:tint="#73B7F0"/>
|
|
||||||
|
|
||||||
<TextView style="@style/Registration.Description"
|
<TextView style="@style/Registration.Description"
|
||||||
android:id="@+id/information_label"
|
android:id="@+id/registration_information"
|
||||||
android:clickable="false"
|
android:layout_width="fill_parent"
|
||||||
android:layout_width="wrap_content"
|
android:gravity="start"
|
||||||
android:layout_height="wrap_content"
|
android:visibility="gone"
|
||||||
android:layout_marginLeft="3dp"
|
android:layout_marginBottom="16dp"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_marginTop="16dp"
|
||||||
android:textColor="#73B7F0"
|
android:text="@string/registration_activity__registration_will_transmit_some_contact_information_to_the_server_temporariliy"/>
|
||||||
android:text="@string/RegistrationActivity_more_information"/>
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout android:layout_width="wrap_content"
|
<LinearLayout android:id="@+id/information_link_container"
|
||||||
android:layout_height="wrap_content"
|
android:clickable="true"
|
||||||
android:layout_marginTop="16dip"
|
android:orientation="horizontal"
|
||||||
android:layout_marginBottom="16dip"
|
android:layout_width="wrap_content"
|
||||||
android:layout_gravity="right"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal">
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginBottom="16dip">
|
||||||
|
|
||||||
<TextView android:id="@+id/skipButton"
|
<ImageView android:layout_width="wrap_content"
|
||||||
android:paddingLeft="20dp"
|
android:layout_height="wrap_content"
|
||||||
android:paddingRight="20dp"
|
android:clickable="false"
|
||||||
android:paddingTop="15dp"
|
android:src="@drawable/ic_info_white_18dp"
|
||||||
android:paddingBottom="15dp"
|
android:tint="#73B7F0"/>
|
||||||
android:text="@android:string/cancel"
|
|
||||||
android:textColor="@color/white"
|
<TextView style="@style/Registration.Description"
|
||||||
android:background="@drawable/pill_button"
|
android:id="@+id/information_label"
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
android:clickable="false"
|
||||||
android:layout_width="0dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginRight="5dip"
|
android:layout_marginLeft="3dp"
|
||||||
android:layout_height="wrap_content"/>
|
android:layout_gravity="center_vertical"
|
||||||
|
android:textColor="#73B7F0"
|
||||||
|
android:text="@string/RegistrationActivity_more_information"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<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>
|
</LinearLayout>
|
||||||
|
|
||||||
<ImageView android:id="@+id/twilio_shoutout"
|
<RelativeLayout android:id="@+id/verification_container"
|
||||||
android:layout_width="wrap_content"
|
android:visibility="invisible"
|
||||||
android:layout_height="wrap_content"
|
android:layout_below="@id/header"
|
||||||
android:src="@drawable/ic_twilio_shoutout_white"
|
android:layout_width="match_parent"
|
||||||
android:layout_gravity="right"
|
android:layout_height="match_parent"
|
||||||
android:tint="@color/grey_800"
|
tools:visibility="visible">
|
||||||
android:contentDescription="@string/registration_activity__powered_by_twilio"/>
|
|
||||||
|
<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>
|
</ScrollView>
|
||||||
|
45
res/layout/registration_call_me_view.xml
Normal file
@ -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>
|
154
res/layout/verification_code_view.xml
Normal file
@ -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>
|
43
res/layout/verification_pin_keyboard_view.xml
Normal file
@ -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>
|
@ -227,4 +227,14 @@
|
|||||||
</attr>
|
</attr>
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
|
<declare-styleable name="VerificationCodeView">
|
||||||
|
<attr name="vcv_spacing" format="dimension"/>
|
||||||
|
<attr name="vcv_inputWidth" format="dimension"/>
|
||||||
|
<attr name="vcv_inputHeight" format="dimension"/>
|
||||||
|
<attr name="vcv_inputColor" format="color"/>
|
||||||
|
<attr name="vcv_textSize" format="dimension"/>
|
||||||
|
<attr name="vcv_textColor" format="color"/>
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1488,6 +1488,8 @@
|
|||||||
<string name="experience_upgrade_preference_fragment__optionally_see_and_share_when_messages_have_been_read">Optionally see and share when messages have been read</string>
|
<string name="experience_upgrade_preference_fragment__optionally_see_and_share_when_messages_have_been_read">Optionally see and share when messages have been read</string>
|
||||||
<string name="experience_upgrade_preference_fragment__enable_read_receipts">Enable read receipts</string>
|
<string name="experience_upgrade_preference_fragment__enable_read_receipts">Enable read receipts</string>
|
||||||
<string name="recipient_preference_activity__shared_media">Shared media</string>
|
<string name="recipient_preference_activity__shared_media">Shared media</string>
|
||||||
|
<string name="registration_activity__verify_your_number">Verify Your Number</string>
|
||||||
|
<string name="registration_activity__please_enter_your_mobile_number_to_receive_a_verification_code_carrier_rates_may_apply">Please enter your mobile number to receive a verification code. Carrier rates may apply.</string>
|
||||||
|
|
||||||
|
|
||||||
<!-- EOF -->
|
<!-- EOF -->
|
||||||
|
27
res/xml/pin_keyboard.xml
Normal file
@ -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,39 +1,79 @@
|
|||||||
package org.thoughtcrime.securesms;
|
package org.thoughtcrime.securesms;
|
||||||
|
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.IntentFilter;
|
||||||
import android.content.pm.PackageManager;
|
import android.graphics.Color;
|
||||||
import android.graphics.PorterDuff;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.design.widget.FloatingActionButton;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
|
import android.text.SpannableString;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.text.Spanned;
|
||||||
|
import android.text.TextPaint;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
|
import android.text.method.LinkMovementMethod;
|
||||||
|
import android.text.style.ClickableSpan;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.Pair;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.animation.OvershootInterpolator;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.Spinner;
|
import android.widget.Spinner;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.dd.CircularProgressButton;
|
||||||
import com.google.android.gms.common.ConnectionResult;
|
import com.google.android.gms.common.ConnectionResult;
|
||||||
import com.google.android.gms.common.GoogleApiAvailability;
|
import com.google.android.gms.common.GoogleApiAvailability;
|
||||||
|
import com.google.android.gms.gcm.GoogleCloudMessaging;
|
||||||
import com.google.i18n.phonenumbers.AsYouTypeFormatter;
|
import com.google.i18n.phonenumbers.AsYouTypeFormatter;
|
||||||
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||||
import com.google.i18n.phonenumbers.Phonenumber;
|
import com.google.i18n.phonenumbers.Phonenumber;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
import org.thoughtcrime.securesms.animation.AnimationCompleteListener;
|
||||||
|
import org.thoughtcrime.securesms.components.registration.CallMeCountDownView;
|
||||||
|
import org.thoughtcrime.securesms.components.registration.VerificationCodeView;
|
||||||
|
import org.thoughtcrime.securesms.components.registration.VerificationPinKeyboard;
|
||||||
|
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||||
|
import org.thoughtcrime.securesms.crypto.PreKeyUtil;
|
||||||
|
import org.thoughtcrime.securesms.crypto.SessionUtil;
|
||||||
|
import org.thoughtcrime.securesms.database.Address;
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||||
|
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||||
|
import org.thoughtcrime.securesms.jobs.GcmRefreshJob;
|
||||||
|
import org.thoughtcrime.securesms.push.AccountManagerFactory;
|
||||||
|
import org.thoughtcrime.securesms.service.DirectoryRefreshListener;
|
||||||
|
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
|
||||||
import org.thoughtcrime.securesms.util.Dialogs;
|
import org.thoughtcrime.securesms.util.Dialogs;
|
||||||
|
import org.thoughtcrime.securesms.util.PlayServicesUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.PlayServicesUtil.PlayServicesStatus;
|
||||||
|
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener;
|
||||||
|
import org.whispersystems.libsignal.IdentityKeyPair;
|
||||||
|
import org.whispersystems.libsignal.state.PreKeyRecord;
|
||||||
|
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
||||||
|
import org.whispersystems.libsignal.util.KeyHelper;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||||
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The register account activity. Prompts ths user for their registration information
|
* The register account activity. Prompts ths user for their registration information
|
||||||
* and begins the account registration process.
|
* and begins the account registration process.
|
||||||
@ -41,81 +81,106 @@ import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
|||||||
* @author Moxie Marlinspike
|
* @author Moxie Marlinspike
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class RegistrationActivity extends BaseActionBarActivity {
|
public class RegistrationActivity extends BaseActionBarActivity implements VerificationCodeView.OnCodeEnteredListener {
|
||||||
|
|
||||||
|
private static final int PICK_COUNTRY = 1;
|
||||||
|
private static final int SCENE_TRANSITION_DURATION = 250;
|
||||||
|
public static final String CHALLENGE_EVENT = "org.thoughtcrime.securesms.CHALLENGE_EVENT";
|
||||||
|
public static final String CHALLENGE_EXTRA = "CAAChallenge";
|
||||||
|
|
||||||
private static final int PICK_COUNTRY = 1;
|
|
||||||
private static final String TAG = RegistrationActivity.class.getSimpleName();
|
private static final String TAG = RegistrationActivity.class.getSimpleName();
|
||||||
|
|
||||||
private enum PlayServicesStatus {
|
private AsYouTypeFormatter countryFormatter;
|
||||||
SUCCESS,
|
private ArrayAdapter<String> countrySpinnerAdapter;
|
||||||
MISSING,
|
private Spinner countrySpinner;
|
||||||
NEEDS_UPDATE,
|
private TextView countryCode;
|
||||||
TRANSIENT_ERROR
|
private TextView number;
|
||||||
}
|
private CircularProgressButton createButton;
|
||||||
|
private TextView informationView;
|
||||||
|
private TextView informationToggleText;
|
||||||
|
private TextView title;
|
||||||
|
private TextView subtitle;
|
||||||
|
private View registrationContainer;
|
||||||
|
private View verificationContainer;
|
||||||
|
private FloatingActionButton fab;
|
||||||
|
|
||||||
private AsYouTypeFormatter countryFormatter;
|
private CallMeCountDownView callMeCountDownView;
|
||||||
private ArrayAdapter<String> countrySpinnerAdapter;
|
private VerificationPinKeyboard keyboard;
|
||||||
private Spinner countrySpinner;
|
private VerificationCodeView verificationCodeView;
|
||||||
private TextView countryCode;
|
private RegistrationState registrationState;
|
||||||
private TextView number;
|
private ChallengeReceiver challengeReceiver;
|
||||||
private TextView createButton;
|
private SignalServiceAccountManager accountManager;
|
||||||
private TextView skipButton;
|
|
||||||
private TextView informationView;
|
|
||||||
private View informationToggle;
|
|
||||||
private TextView informationToggleText;
|
|
||||||
|
|
||||||
private MasterSecret masterSecret;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle icicle) {
|
public void onCreate(Bundle icicle) {
|
||||||
super.onCreate(icicle);
|
super.onCreate(icicle);
|
||||||
setContentView(R.layout.registration_activity);
|
setContentView(R.layout.registration_activity);
|
||||||
|
|
||||||
getSupportActionBar().setTitle(getString(R.string.RegistrationActivity_connect_with_signal));
|
|
||||||
|
|
||||||
initializeResources();
|
initializeResources();
|
||||||
initializeSpinner();
|
initializeSpinner();
|
||||||
initializeNumber();
|
initializeNumber();
|
||||||
|
initializeChallengeListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
shutdownChallengeListener();
|
||||||
|
markAsVerifying(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
if (requestCode == PICK_COUNTRY && resultCode == RESULT_OK && data != null) {
|
if (requestCode == PICK_COUNTRY && resultCode == RESULT_OK && data != null) {
|
||||||
this.countryCode.setText(data.getIntExtra("country_code", 1)+"");
|
this.countryCode.setText(String.valueOf(data.getIntExtra("country_code", 1)));
|
||||||
setCountryDisplay(data.getStringExtra("country_name"));
|
setCountryDisplay(data.getStringExtra("country_name"));
|
||||||
setCountryFormatter(data.getIntExtra("country_code", 1));
|
setCountryFormatter(data.getIntExtra("country_code", 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeResources() {
|
private void initializeResources() {
|
||||||
this.masterSecret = getIntent().getParcelableExtra("master_secret");
|
TextView skipButton = findViewById(R.id.skip_button);
|
||||||
this.countrySpinner = (Spinner) findViewById(R.id.country_spinner);
|
View informationToggle = findViewById(R.id.information_link_container);
|
||||||
this.countryCode = (TextView) findViewById(R.id.country_code);
|
|
||||||
this.number = (TextView) findViewById(R.id.number);
|
|
||||||
this.createButton = (TextView) findViewById(R.id.registerButton);
|
|
||||||
this.skipButton = (TextView) findViewById(R.id.skipButton);
|
|
||||||
this.informationView = (TextView) findViewById(R.id.registration_information);
|
|
||||||
this.informationToggle = findViewById(R.id.information_link_container);
|
|
||||||
this.informationToggleText = (TextView) findViewById(R.id.information_label);
|
|
||||||
|
|
||||||
this.createButton.getBackground().setColorFilter(ContextCompat.getColor(this, R.color.signal_primary),
|
this.countrySpinner = findViewById(R.id.country_spinner);
|
||||||
PorterDuff.Mode.MULTIPLY);
|
this.countryCode = findViewById(R.id.country_code);
|
||||||
this.skipButton.getBackground().setColorFilter(ContextCompat.getColor(this, R.color.grey_400),
|
this.number = findViewById(R.id.number);
|
||||||
PorterDuff.Mode.MULTIPLY);
|
this.createButton = findViewById(R.id.registerButton);
|
||||||
|
this.informationView = findViewById(R.id.registration_information);
|
||||||
|
this.informationToggleText = findViewById(R.id.information_label);
|
||||||
|
this.title = findViewById(R.id.verify_header);
|
||||||
|
this.subtitle = findViewById(R.id.verify_subheader);
|
||||||
|
this.registrationContainer = findViewById(R.id.registration_container);
|
||||||
|
this.verificationContainer = findViewById(R.id.verification_container);
|
||||||
|
this.fab = findViewById(R.id.fab);
|
||||||
|
|
||||||
|
this.verificationCodeView = findViewById(R.id.code);
|
||||||
|
this.keyboard = findViewById(R.id.keyboard);
|
||||||
|
this.callMeCountDownView = findViewById(R.id.call_me_count_down);
|
||||||
|
this.registrationState = new RegistrationState(RegistrationState.State.INITIAL, null, null, null);
|
||||||
|
|
||||||
this.countryCode.addTextChangedListener(new CountryCodeChangedListener());
|
this.countryCode.addTextChangedListener(new CountryCodeChangedListener());
|
||||||
this.number.addTextChangedListener(new NumberChangedListener());
|
this.number.addTextChangedListener(new NumberChangedListener());
|
||||||
this.createButton.setOnClickListener(new CreateButtonListener());
|
this.createButton.setOnClickListener(v -> handleRegister());
|
||||||
this.skipButton.setOnClickListener(new CancelButtonListener());
|
this.callMeCountDownView.setOnClickListener(v -> handlePhoneCallRequest());
|
||||||
this.informationToggle.setOnClickListener(new InformationToggleListener());
|
skipButton.setOnClickListener(v -> handleCancel());
|
||||||
|
informationToggle.setOnClickListener(new InformationToggleListener());
|
||||||
|
|
||||||
if (getIntent().getBooleanExtra("cancel_button", false)) {
|
if (getIntent().getBooleanExtra("cancel_button", false)) {
|
||||||
this.skipButton.setVisibility(View.VISIBLE);
|
skipButton.setVisibility(View.VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
this.skipButton.setVisibility(View.INVISIBLE);
|
skipButton.setVisibility(View.INVISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.keyboard.setOnKeyPressListener(key -> {
|
||||||
|
if (key >= 0) verificationCodeView.append(key);
|
||||||
|
else verificationCodeView.delete();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.verificationCodeView.setOnCompleteListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
private void initializeSpinner() {
|
private void initializeSpinner() {
|
||||||
this.countrySpinnerAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item);
|
this.countrySpinnerAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item);
|
||||||
this.countrySpinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
this.countrySpinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
@ -123,26 +188,20 @@ public class RegistrationActivity extends BaseActionBarActivity {
|
|||||||
setCountryDisplay(getString(R.string.RegistrationActivity_select_your_country));
|
setCountryDisplay(getString(R.string.RegistrationActivity_select_your_country));
|
||||||
|
|
||||||
this.countrySpinner.setAdapter(this.countrySpinnerAdapter);
|
this.countrySpinner.setAdapter(this.countrySpinnerAdapter);
|
||||||
this.countrySpinner.setOnTouchListener(new View.OnTouchListener() {
|
this.countrySpinner.setOnTouchListener((v, event) -> {
|
||||||
@Override
|
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||||
public boolean onTouch(View v, MotionEvent event) {
|
Intent intent = new Intent(RegistrationActivity.this, CountrySelectionActivity.class);
|
||||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
startActivityForResult(intent, PICK_COUNTRY);
|
||||||
Intent intent = new Intent(RegistrationActivity.this, CountrySelectionActivity.class);
|
}
|
||||||
startActivityForResult(intent, PICK_COUNTRY);
|
return true;
|
||||||
}
|
});
|
||||||
|
this.countrySpinner.setOnKeyListener((v, keyCode, event) -> {
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER && event.getAction() == KeyEvent.ACTION_UP) {
|
||||||
|
Intent intent = new Intent(RegistrationActivity.this, CountrySelectionActivity.class);
|
||||||
|
startActivityForResult(intent, PICK_COUNTRY);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
return false;
|
||||||
this.countrySpinner.setOnKeyListener(new View.OnKeyListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
|
||||||
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER && event.getAction() == KeyEvent.ACTION_UP) {
|
|
||||||
Intent intent = new Intent(RegistrationActivity.this, CountrySelectionActivity.class);
|
|
||||||
startActivityForResult(intent, PICK_COUNTRY);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +215,7 @@ public class RegistrationActivity extends BaseActionBarActivity {
|
|||||||
Optional<String> simCountryIso = Util.getSimCountryIso(this);
|
Optional<String> simCountryIso = Util.getSimCountryIso(this);
|
||||||
|
|
||||||
if (simCountryIso.isPresent() && !TextUtils.isEmpty(simCountryIso.get())) {
|
if (simCountryIso.isPresent() && !TextUtils.isEmpty(simCountryIso.get())) {
|
||||||
this.countryCode.setText(PhoneNumberUtil.getInstance().getCountryCodeForRegion(simCountryIso.get())+"");
|
this.countryCode.setText(String.valueOf(PhoneNumberUtil.getInstance().getCountryCodeForRegion(simCountryIso.get())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -179,119 +238,370 @@ public class RegistrationActivity extends BaseActionBarActivity {
|
|||||||
number.getText().toString());
|
number.getText().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CreateButtonListener implements View.OnClickListener {
|
private void handleRegister() {
|
||||||
@Override
|
if (TextUtils.isEmpty(countryCode.getText())) {
|
||||||
public void onClick(View v) {
|
Toast.makeText(this, getString(R.string.RegistrationActivity_you_must_specify_your_country_code), Toast.LENGTH_LONG).show();
|
||||||
final RegistrationActivity self = RegistrationActivity.this;
|
return;
|
||||||
|
|
||||||
if (TextUtils.isEmpty(countryCode.getText())) {
|
|
||||||
Toast.makeText(self,
|
|
||||||
getString(R.string.RegistrationActivity_you_must_specify_your_country_code),
|
|
||||||
Toast.LENGTH_LONG).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TextUtils.isEmpty(number.getText())) {
|
|
||||||
Toast.makeText(self,
|
|
||||||
getString(R.string.RegistrationActivity_you_must_specify_your_phone_number),
|
|
||||||
Toast.LENGTH_LONG).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String e164number = getConfiguredE164Number();
|
|
||||||
|
|
||||||
if (!PhoneNumberFormatter.isValidNumber(e164number)) {
|
|
||||||
Dialogs.showAlertDialog(self,
|
|
||||||
getString(R.string.RegistrationActivity_invalid_number),
|
|
||||||
String.format(getString(R.string.RegistrationActivity_the_number_you_specified_s_is_invalid),
|
|
||||||
e164number));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayServicesStatus gcmStatus = checkPlayServices(self);
|
|
||||||
|
|
||||||
if (gcmStatus == PlayServicesStatus.SUCCESS) {
|
|
||||||
promptForRegistrationStart(self, e164number, true);
|
|
||||||
} else if (gcmStatus == PlayServicesStatus.MISSING) {
|
|
||||||
promptForNoPlayServices(self, e164number);
|
|
||||||
} else if (gcmStatus == PlayServicesStatus.NEEDS_UPDATE) {
|
|
||||||
GoogleApiAvailability.getInstance().getErrorDialog(self, ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED, 0).show();
|
|
||||||
} else {
|
|
||||||
Dialogs.showAlertDialog(self, getString(R.string.RegistrationActivity_play_services_error),
|
|
||||||
getString(R.string.RegistrationActivity_google_play_services_is_updating_or_unavailable));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void promptForRegistrationStart(final Context context, final String e164number, final boolean gcmSupported) {
|
if (TextUtils.isEmpty(number.getText())) {
|
||||||
AlertDialog.Builder dialog = new AlertDialog.Builder(context);
|
Toast.makeText(this, getString(R.string.RegistrationActivity_you_must_specify_your_phone_number), Toast.LENGTH_LONG).show();
|
||||||
dialog.setTitle(PhoneNumberFormatter.getInternationalFormatFromE164(e164number));
|
return;
|
||||||
dialog.setMessage(R.string.RegistrationActivity_we_will_now_verify_that_the_following_number_is_associated_with_your_device_s);
|
|
||||||
dialog.setPositiveButton(getString(R.string.RegistrationActivity_continue),
|
|
||||||
new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
Intent intent = new Intent(context, RegistrationProgressActivity.class);
|
|
||||||
intent.putExtra(RegistrationProgressActivity.NUMBER_EXTRA, e164number);
|
|
||||||
intent.putExtra(RegistrationProgressActivity.MASTER_SECRET_EXTRA, masterSecret);
|
|
||||||
intent.putExtra(RegistrationProgressActivity.GCM_SUPPORTED_EXTRA, gcmSupported);
|
|
||||||
startActivity(intent);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
dialog.setNegativeButton(getString(R.string.RegistrationActivity_edit), null);
|
|
||||||
dialog.show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void promptForNoPlayServices(final Context context, final String e164number) {
|
final String e164number = getConfiguredE164Number();
|
||||||
AlertDialog.Builder dialog = new AlertDialog.Builder(context);
|
|
||||||
dialog.setTitle(R.string.RegistrationActivity_missing_google_play_services);
|
if (!PhoneNumberFormatter.isValidNumber(e164number)) {
|
||||||
dialog.setMessage(R.string.RegistrationActivity_this_device_is_missing_google_play_services);
|
Dialogs.showAlertDialog(this, getString(R.string.RegistrationActivity_invalid_number),
|
||||||
dialog.setPositiveButton(R.string.RegistrationActivity_i_understand, new DialogInterface.OnClickListener() {
|
String.format(getString(R.string.RegistrationActivity_the_number_you_specified_s_is_invalid),
|
||||||
@Override
|
e164number));
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
return;
|
||||||
promptForRegistrationStart(context, e164number, false);
|
}
|
||||||
|
|
||||||
|
PlayServicesStatus gcmStatus = PlayServicesUtil.getPlayServicesStatus(this);
|
||||||
|
|
||||||
|
if (gcmStatus == PlayServicesStatus.SUCCESS) {
|
||||||
|
handleRequestVerification(e164number, true);
|
||||||
|
} else if (gcmStatus == PlayServicesStatus.MISSING) {
|
||||||
|
handlePromptForNoPlayServices(e164number);
|
||||||
|
} else if (gcmStatus == PlayServicesStatus.NEEDS_UPDATE) {
|
||||||
|
GoogleApiAvailability.getInstance().getErrorDialog(this, ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED, 0).show();
|
||||||
|
} else {
|
||||||
|
Dialogs.showAlertDialog(this, getString(R.string.RegistrationActivity_play_services_error),
|
||||||
|
getString(R.string.RegistrationActivity_google_play_services_is_updating_or_unavailable));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("StaticFieldLeak")
|
||||||
|
private void handleRequestVerification(@NonNull String e164number, boolean gcmSupported) {
|
||||||
|
createButton.setIndeterminateProgressMode(true);
|
||||||
|
createButton.setProgress(50);
|
||||||
|
|
||||||
|
new AsyncTask<Void, Void, Pair<String, Optional<String>>> () {
|
||||||
|
@Override
|
||||||
|
protected @Nullable Pair<String, Optional<String>> doInBackground(Void... voids) {
|
||||||
|
try {
|
||||||
|
markAsVerifying(true);
|
||||||
|
|
||||||
|
String password = Util.getSecret(18);
|
||||||
|
|
||||||
|
Optional<String> gcmToken;
|
||||||
|
|
||||||
|
if (gcmSupported) {
|
||||||
|
gcmToken = Optional.of(GoogleCloudMessaging.getInstance(RegistrationActivity.this).register(GcmRefreshJob.REGISTRATION_ID));
|
||||||
|
} else {
|
||||||
|
gcmToken = Optional.absent();
|
||||||
|
}
|
||||||
|
|
||||||
|
accountManager = AccountManagerFactory.createManager(RegistrationActivity.this, e164number, password);
|
||||||
|
accountManager.requestSmsVerificationCode();
|
||||||
|
|
||||||
|
return new Pair<>(password, gcmToken);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
dialog.setNegativeButton(android.R.string.cancel, null);
|
|
||||||
dialog.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private PlayServicesStatus checkPlayServices(Context context) {
|
protected void onPostExecute(@Nullable Pair<String, Optional<String>> result) {
|
||||||
int gcmStatus = 0;
|
if (result == null) {
|
||||||
|
Toast.makeText(RegistrationActivity.this, "Unable to connect to service. Please check network connection and try again.", Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
registrationState = new RegistrationState(RegistrationState.State.VERIFYING, e164number, result.first, result.second);
|
||||||
|
displayVerificationView(e164number, 64);
|
||||||
|
}
|
||||||
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleChallengeReceived(@Nullable String challenge) {
|
||||||
|
if (challenge != null && challenge.length() == 6 && registrationState.state == RegistrationState.State.VERIFYING) {
|
||||||
|
verificationCodeView.clear();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
gcmStatus = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context);
|
for (int i=0;i<challenge.length();i++) {
|
||||||
} catch (Throwable t) {
|
final int index = i;
|
||||||
Log.w(TAG, t);
|
verificationCodeView.postDelayed(() -> verificationCodeView.append(Integer.parseInt(Character.toString(challenge.charAt(index)))), i * 200);
|
||||||
return PlayServicesStatus.MISSING;
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
verificationCodeView.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("StaticFieldLeak")
|
||||||
|
@Override
|
||||||
|
public void onCodeComplete(@NonNull String code) {
|
||||||
|
this.registrationState = new RegistrationState(RegistrationState.State.CHECKING, this.registrationState);
|
||||||
|
callMeCountDownView.setVisibility(View.INVISIBLE);
|
||||||
|
keyboard.displayProgress();
|
||||||
|
|
||||||
|
new AsyncTask<Void, Void, Boolean>() {
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(Void... voids) {
|
||||||
|
try {
|
||||||
|
int registrationId = KeyHelper.generateRegistrationId(false);
|
||||||
|
TextSecurePreferences.setLocalRegistrationId(RegistrationActivity.this, registrationId);
|
||||||
|
SessionUtil.archiveAllSessions(RegistrationActivity.this);
|
||||||
|
|
||||||
|
String signalingKey = Util.getSecret(52);
|
||||||
|
|
||||||
|
accountManager.verifyAccountWithCode(code, signalingKey, registrationId, !registrationState.gcmToken.isPresent());
|
||||||
|
|
||||||
|
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(RegistrationActivity.this);
|
||||||
|
List<PreKeyRecord> records = PreKeyUtil.generatePreKeys(RegistrationActivity.this);
|
||||||
|
SignedPreKeyRecord signedPreKey = PreKeyUtil.generateSignedPreKey(RegistrationActivity.this, identityKey, true);
|
||||||
|
|
||||||
|
accountManager.setPreKeys(identityKey.getPublicKey(), signedPreKey, records);
|
||||||
|
accountManager.setGcmId(registrationState.gcmToken);
|
||||||
|
|
||||||
|
TextSecurePreferences.setGcmRegistrationId(RegistrationActivity.this, registrationState.gcmToken.orNull());
|
||||||
|
TextSecurePreferences.setGcmDisabled(RegistrationActivity.this, !registrationState.gcmToken.isPresent());
|
||||||
|
TextSecurePreferences.setWebsocketRegistered(RegistrationActivity.this, true);
|
||||||
|
|
||||||
|
DatabaseFactory.getIdentityDatabase(RegistrationActivity.this)
|
||||||
|
.saveIdentity(Address.fromSerialized(registrationState.e164number),
|
||||||
|
identityKey.getPublicKey(), IdentityDatabase.VerifiedStatus.VERIFIED,
|
||||||
|
true, System.currentTimeMillis(), true);
|
||||||
|
|
||||||
|
TextSecurePreferences.setVerifying(RegistrationActivity.this, false);
|
||||||
|
TextSecurePreferences.setPushRegistered(RegistrationActivity.this, true);
|
||||||
|
TextSecurePreferences.setLocalNumber(RegistrationActivity.this, registrationState.e164number);
|
||||||
|
TextSecurePreferences.setPushServerPassword(RegistrationActivity.this, registrationState.password);
|
||||||
|
TextSecurePreferences.setSignalingKey(RegistrationActivity.this, signalingKey);
|
||||||
|
TextSecurePreferences.setSignedPreKeyRegistered(RegistrationActivity.this, true);
|
||||||
|
TextSecurePreferences.setPromptedPushRegistration(RegistrationActivity.this, true);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.w(TAG, "Play Services: " + gcmStatus);
|
@Override
|
||||||
|
protected void onPostExecute(Boolean result) {
|
||||||
|
if (result) {
|
||||||
|
keyboard.displaySuccess().addListener(new AssertedSuccessListener<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Boolean result) {
|
||||||
|
ApplicationContext.getInstance(RegistrationActivity.this).getJobManager().add(new DirectoryRefreshJob(RegistrationActivity.this));
|
||||||
|
|
||||||
switch (gcmStatus) {
|
DirectoryRefreshListener.schedule(RegistrationActivity.this);
|
||||||
case ConnectionResult.SUCCESS:
|
RotateSignedPreKeyListener.schedule(RegistrationActivity.this);
|
||||||
return PlayServicesStatus.SUCCESS;
|
|
||||||
case ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED:
|
|
||||||
try {
|
|
||||||
ApplicationInfo applicationInfo = getPackageManager().getApplicationInfo("com.google.android.gms", 0);
|
|
||||||
|
|
||||||
if (applicationInfo != null && !applicationInfo.enabled) {
|
Intent nextIntent = getIntent().getParcelableExtra("next_intent");
|
||||||
return PlayServicesStatus.MISSING;
|
|
||||||
|
if (nextIntent == null) {
|
||||||
|
nextIntent = new Intent(RegistrationActivity.this, ConversationListActivity.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
startActivity(nextIntent);
|
||||||
|
finish();
|
||||||
}
|
}
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
});
|
||||||
|
} else {
|
||||||
|
keyboard.displayFailure().addListener(new AssertedSuccessListener<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Boolean result) {
|
||||||
|
registrationState = new RegistrationState(RegistrationState.State.VERIFYING, registrationState);
|
||||||
|
callMeCountDownView.setVisibility(View.VISIBLE);
|
||||||
|
verificationCodeView.clear();
|
||||||
|
keyboard.displayKeyboard();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("StaticFieldLeak")
|
||||||
|
private void handlePhoneCallRequest() {
|
||||||
|
if (registrationState.state == RegistrationState.State.VERIFYING) {
|
||||||
|
callMeCountDownView.startCountDown(300);
|
||||||
|
|
||||||
|
new AsyncTask<Void, Void, Void>() {
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(Void... voids) {
|
||||||
|
try {
|
||||||
|
accountManager.requestVoiceVerificationCode();
|
||||||
|
} catch (IOException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlayServicesStatus.NEEDS_UPDATE;
|
return null;
|
||||||
case ConnectionResult.SERVICE_DISABLED:
|
}
|
||||||
case ConnectionResult.SERVICE_MISSING:
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
case ConnectionResult.SERVICE_INVALID:
|
}
|
||||||
case ConnectionResult.API_UNAVAILABLE:
|
}
|
||||||
case ConnectionResult.SERVICE_MISSING_PERMISSION:
|
|
||||||
return PlayServicesStatus.MISSING;
|
private void displayInitialView(@NonNull String e164number) {
|
||||||
default:
|
title.animate().translationX(title.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() {
|
||||||
return PlayServicesStatus.TRANSIENT_ERROR;
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
title.setText(R.string.registration_activity__verify_your_number);
|
||||||
|
title.clearAnimation();
|
||||||
|
title.setTranslationX(-1 * title.getWidth());
|
||||||
|
title.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start();
|
||||||
}
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
subtitle.animate().translationX(subtitle.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
subtitle.setText(R.string.registration_activity__please_enter_your_mobile_number_to_receive_a_verification_code_carrier_rates_may_apply);
|
||||||
|
subtitle.clearAnimation();
|
||||||
|
subtitle.setTranslationX(-1 * subtitle.getWidth());
|
||||||
|
subtitle.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
verificationContainer.animate().translationX(verificationContainer.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
verificationContainer.clearAnimation();
|
||||||
|
verificationContainer.setVisibility(View.INVISIBLE);
|
||||||
|
verificationContainer.setTranslationX(0);
|
||||||
|
|
||||||
|
registrationContainer.setTranslationX(-1 * registrationContainer.getWidth());
|
||||||
|
registrationContainer.setVisibility(View.VISIBLE);
|
||||||
|
createButton.setProgress(0);
|
||||||
|
createButton.setIndeterminateProgressMode(false);
|
||||||
|
registrationContainer.animate().translationX(0).setDuration(SCENE_TRANSITION_DURATION).setListener(null).setInterpolator(new OvershootInterpolator()).start();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
fab.animate().rotationBy(360f).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
fab.clearAnimation();
|
||||||
|
fab.setImageResource(R.drawable.ic_action_name);
|
||||||
|
fab.animate().rotationBy(375f).setDuration(SCENE_TRANSITION_DURATION).setListener(null).start();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void displayVerificationView(@NonNull String e164number, int callCountdown) {
|
||||||
|
ServiceUtil.getInputMethodManager(this)
|
||||||
|
.hideSoftInputFromWindow(countryCode.getWindowToken(), 0);
|
||||||
|
|
||||||
|
ServiceUtil.getInputMethodManager(this)
|
||||||
|
.hideSoftInputFromWindow(number.getWindowToken(), 0);
|
||||||
|
|
||||||
|
title.animate().translationX(-1 * title.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
title.setText(String.format("Verify %s", e164number));
|
||||||
|
title.clearAnimation();
|
||||||
|
title.setTranslationX(title.getWidth());
|
||||||
|
title.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
subtitle.animate().translationX(-1 * subtitle.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
SpannableString subtitleDescription = new SpannableString(String.format("Please enter the verification code sent to %s.", e164number));
|
||||||
|
SpannableString wrongNumber = new SpannableString("Wrong number?");
|
||||||
|
|
||||||
|
ClickableSpan clickableSpan = new ClickableSpan() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View widget) {
|
||||||
|
displayInitialView(e164number);
|
||||||
|
registrationState = new RegistrationState(RegistrationState.State.INITIAL, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateDrawState(TextPaint paint) {
|
||||||
|
paint.setColor(Color.WHITE);
|
||||||
|
paint.setUnderlineText(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
wrongNumber.setSpan(clickableSpan, 0, wrongNumber.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
|
||||||
|
subtitle.setText(new SpannableStringBuilder(subtitleDescription).append(" ").append(wrongNumber));
|
||||||
|
subtitle.setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
|
subtitle.clearAnimation();
|
||||||
|
subtitle.setTranslationX(subtitle.getWidth());
|
||||||
|
subtitle.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
registrationContainer.animate().translationX(-1 * registrationContainer.getWidth()).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
registrationContainer.clearAnimation();
|
||||||
|
registrationContainer.setVisibility(View.INVISIBLE);
|
||||||
|
registrationContainer.setTranslationX(0);
|
||||||
|
|
||||||
|
verificationContainer.setTranslationX(verificationContainer.getWidth());
|
||||||
|
verificationContainer.setVisibility(View.VISIBLE);
|
||||||
|
verificationContainer.animate().translationX(0).setListener(null).setInterpolator(new OvershootInterpolator()).setDuration(SCENE_TRANSITION_DURATION).start();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
fab.animate().rotationBy(-360f).setDuration(SCENE_TRANSITION_DURATION).setListener(new AnimationCompleteListener() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
fab.clearAnimation();
|
||||||
|
fab.setImageResource(R.drawable.ic_textsms_24dp);
|
||||||
|
fab.animate().rotationBy(-375f).setDuration(SCENE_TRANSITION_DURATION).setListener(null).start();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
this.callMeCountDownView.startCountDown(callCountdown);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleCancel() {
|
||||||
|
TextSecurePreferences.setPromptedPushRegistration(RegistrationActivity.this, true);
|
||||||
|
Intent nextIntent = getIntent().getParcelableExtra("next_intent");
|
||||||
|
|
||||||
|
if (nextIntent == null) {
|
||||||
|
nextIntent = new Intent(RegistrationActivity.this, ConversationListActivity.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
startActivity(nextIntent);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handlePromptForNoPlayServices(@NonNull String e164number) {
|
||||||
|
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
|
||||||
|
dialog.setTitle(R.string.RegistrationActivity_missing_google_play_services);
|
||||||
|
dialog.setMessage(R.string.RegistrationActivity_this_device_is_missing_google_play_services);
|
||||||
|
dialog.setPositiveButton(R.string.RegistrationActivity_i_understand, (dialog1, which) -> handleRequestVerification(e164number, false));
|
||||||
|
dialog.setNegativeButton(android.R.string.cancel, null);
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeChallengeListener() {
|
||||||
|
challengeReceiver = new ChallengeReceiver();
|
||||||
|
IntentFilter filter = new IntentFilter(CHALLENGE_EVENT);
|
||||||
|
registerReceiver(challengeReceiver, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shutdownChallengeListener() {
|
||||||
|
if (challengeReceiver != null) {
|
||||||
|
unregisterReceiver(challengeReceiver);
|
||||||
|
challengeReceiver = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markAsVerifying(boolean verifying) {
|
||||||
|
TextSecurePreferences.setVerifying(this, verifying);
|
||||||
|
|
||||||
|
if (verifying) {
|
||||||
|
TextSecurePreferences.setPushRegistered(this, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ChallengeReceiver extends BroadcastReceiver {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
Log.w(TAG, "Got a challenge broadcast...");
|
||||||
|
handleChallengeReceived(intent.getStringExtra(CHALLENGE_EXTRA));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,21 +668,6 @@ public class RegistrationActivity extends BaseActionBarActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CancelButtonListener implements View.OnClickListener {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
TextSecurePreferences.setPromptedPushRegistration(RegistrationActivity.this, true);
|
|
||||||
Intent nextIntent = getIntent().getParcelableExtra("next_intent");
|
|
||||||
|
|
||||||
if (nextIntent == null) {
|
|
||||||
nextIntent = new Intent(RegistrationActivity.this, ConversationListActivity.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
startActivity(nextIntent);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class InformationToggleListener implements View.OnClickListener {
|
private class InformationToggleListener implements View.OnClickListener {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
@ -385,4 +680,29 @@ public class RegistrationActivity extends BaseActionBarActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class RegistrationState {
|
||||||
|
private enum State {
|
||||||
|
INITIAL, VERIFYING, CHECKING
|
||||||
|
}
|
||||||
|
|
||||||
|
private final State state;
|
||||||
|
private final String e164number;
|
||||||
|
private final String password;
|
||||||
|
private final Optional<String> gcmToken;
|
||||||
|
|
||||||
|
RegistrationState(State state, String e164number, String password, Optional<String> gcmToken) {
|
||||||
|
this.state = state;
|
||||||
|
this.e164number = e164number;
|
||||||
|
this.password = password;
|
||||||
|
this.gcmToken = gcmToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
RegistrationState(State state, RegistrationState previous) {
|
||||||
|
this.state = state;
|
||||||
|
this.e164number = previous.e164number;
|
||||||
|
this.password = previous.password;
|
||||||
|
this.gcmToken = previous.gcmToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {}
|
||||||
|
}
|
@ -76,7 +76,7 @@ class Tweener {
|
|||||||
interpolator = (TimeInterpolator) value; // TODO: multiple interpolators?
|
interpolator = (TimeInterpolator) value; // TODO: multiple interpolators?
|
||||||
} else if ("onUpdate".equals(key) || "onUpdateListener".equals(key)) {
|
} else if ("onUpdate".equals(key) || "onUpdateListener".equals(key)) {
|
||||||
updateListener = (AnimatorUpdateListener) value;
|
updateListener = (AnimatorUpdateListener) value;
|
||||||
} else if ("onComplete".equals(key) || "onCompleteListener".equals(key)) {
|
} else if ("onCodeComplete".equals(key) || "onCompleteListener".equals(key)) {
|
||||||
listener = (AnimatorListener) value;
|
listener = (AnimatorListener) value;
|
||||||
} else if ("delay".equals(key)) {
|
} else if ("delay".equals(key)) {
|
||||||
delay = ((Number) value).longValue();
|
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);
|
||||||
|
}
|
||||||
|
}
|
61
src/org/thoughtcrime/securesms/util/PlayServicesUtil.java
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|