Improve passphrase and onboarding UI. Abstract out routing.
1) Update the create, prompt, and change passphrase activities. They are no longer dialog themed, and should look a little less ugly. 2) Update the import DB activity to be less ugly and more robust. 3) Abstract all of the state handling stuff out of ConversationListActivity. This is now handled by RoutingActivity, which all launch intents move through.
@ -35,10 +35,9 @@
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.Sherlock.Light.DarkActionBar">
|
||||
|
||||
<activity android:name=".ConversationListActivity"
|
||||
android:label="@string/app_name"
|
||||
<activity android:name=".RoutingActivity"
|
||||
android:theme="@style/NoAnimation.Theme.Sherlock.Light.DarkActionBar"
|
||||
android:launchMode="singleTask"
|
||||
android:uiOptions="splitActionBarWhenNarrow"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
|
||||
|
||||
<intent-filter>
|
||||
@ -63,21 +62,33 @@
|
||||
|
||||
</activity>
|
||||
|
||||
<activity android:name=".ConversationListActivity"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTask"
|
||||
android:uiOptions="splitActionBarWhenNarrow"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".ConversationActivity"
|
||||
android:windowSoftInputMode="stateUnchanged"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".DatabaseMigrationActivity"
|
||||
android:theme="@style/NoAnimation.Theme.Sherlock.Light.DarkActionBar"
|
||||
android:launchMode="singleTask"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".PassphraseCreateActivity"
|
||||
android:theme="@style/Theme.Sherlock.Light.Dialog"
|
||||
android:label="@string/AndroidManifest__create_passphrase"
|
||||
android:launchMode="singleInstance"
|
||||
android:windowSoftInputMode="stateUnchanged"
|
||||
android:theme="@style/NoAnimation.Theme.Sherlock.Light.DarkActionBar"
|
||||
android:launchMode="singleTop"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".PassphrasePromptActivity"
|
||||
android:theme="@style/Theme.Sherlock.Light.Dialog"
|
||||
android:label="@string/AndroidManifest__enter_passphrase"
|
||||
android:launchMode="singleInstance"
|
||||
android:windowSoftInputMode="stateVisible"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/NoAnimation.Theme.Sherlock.Light.DarkActionBar"
|
||||
android:windowSoftInputMode="stateUnchanged"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".ContactSelectionActivity"
|
||||
@ -95,7 +106,6 @@
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".PassphraseChangeActivity"
|
||||
android:theme="@style/Theme.Sherlock.Light.Dialog"
|
||||
android:label="@string/AndroidManifest__change_passphrase"
|
||||
android:launchMode="singleInstance"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
BIN
res/drawable-hdpi/import_database.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
res/drawable-hdpi/padlock_prompt.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
res/drawable-mdpi/import_database.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
res/drawable-mdpi/padlock_prompt.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
res/drawable-xhdpi/import_database.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
res/drawable-xhdpi/padlock_prompt.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
res/drawable/background_pattern.png
Normal file
After Width: | Height: | Size: 291 B |
3
res/drawable/background_pattern_repeat.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<bitmap android:src="@drawable/background_pattern" android:tileMode="repeat"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android" />
|
@ -1,90 +1,81 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent" >
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:fillViewport="true"
|
||||
android:background="@drawable/background_pattern_repeat">
|
||||
|
||||
<LinearLayout
|
||||
<FrameLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="16dip"
|
||||
android:paddingRight="16dip"
|
||||
android:orientation="vertical" >
|
||||
android:layout_height="fill_parent"
|
||||
android:gravity="center" >
|
||||
|
||||
<TextView
|
||||
android:padding="3dip"
|
||||
android:text="@string/change_passphrase_activity__old_passphrase"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
<LinearLayout android:paddingRight="16dip"
|
||||
android:paddingLeft="16dip"
|
||||
android:paddingTop="10dip"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="visible"
|
||||
android:orientation="vertical">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/old_passphrase"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword"
|
||||
android:layout_marginBottom="5dip" />
|
||||
<TextView style="@style/Registration.Label"
|
||||
android:layout_width="fill_parent"
|
||||
android:textAllCaps="true"
|
||||
android:text="@string/change_passphrase_activity__old_passphrase" />
|
||||
|
||||
<TextView
|
||||
android:padding="3dip"
|
||||
android:text="@string/change_passphrase_activity__new_passphrase"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
<EditText android:id="@+id/old_passphrase"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword"
|
||||
android:layout_marginBottom="10dip"
|
||||
android:singleLine="true"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/new_passphrase"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword"
|
||||
android:layout_marginBottom="5dip" />
|
||||
<TextView style="@style/Registration.Label"
|
||||
android:layout_width="fill_parent"
|
||||
android:textAllCaps="true"
|
||||
android:text="@string/change_passphrase_activity__new_passphrase" />
|
||||
|
||||
<TextView
|
||||
android:padding="3dip"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/change_passphrase_activity__repeat_new_passphrase"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
<EditText android:id="@+id/new_passphrase"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword"
|
||||
android:singleLine="true"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/repeat_passphrase"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword"
|
||||
android:layout_marginBottom="5dip" />
|
||||
<TextView style="@style/Registration.Label"
|
||||
android:layout_width="fill_parent"
|
||||
android:textAllCaps="true"
|
||||
android:text="@string/change_passphrase_activity__repeat_new_passphrase" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dip"
|
||||
android:gravity="right"
|
||||
android:orientation="horizontal" >
|
||||
<EditText android:id="@+id/repeat_passphrase"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword"
|
||||
android:singleLine="true"/>
|
||||
|
||||
<TableLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:stretchColumns="*"
|
||||
tools:ignore="UselessParent" >
|
||||
<LinearLayout android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="30dip">
|
||||
|
||||
<TableRow>
|
||||
|
||||
<Button
|
||||
<Button style="@android:style/Widget.Button"
|
||||
android:id="@+id/cancel_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:text="@android:string/cancel"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dip"
|
||||
android:layout_marginRight="15dip"
|
||||
android:text="@android:string/cancel" />
|
||||
android:layout_marginRight="7dip"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"/>
|
||||
|
||||
<Button
|
||||
<Button style="@android:style/Widget.Button"
|
||||
android:id="@+id/ok_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:text="@android:string/ok"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="16dip"
|
||||
android:text="@android:string/ok" />
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
</ScrollView>
|
@ -1,74 +1,87 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<ScrollView android:id="@+id/ScrollView"
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
android:fillViewport="true"
|
||||
android:background="@drawable/background_pattern_repeat">
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="16dip"
|
||||
android:paddingRight="16dip"
|
||||
android:orientation="vertical">
|
||||
<FrameLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:gravity="center" >
|
||||
|
||||
<TextView android:text="@string/create_passphrase_activity__please_choose_a_passphrase_that_will_be_used_to_locally_encrypt_your_data_this_should_be_a_strong_passphrase"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dip"
|
||||
android:layout_marginBottom="10dip"/>
|
||||
|
||||
<TextView android:text="@string/create_passphrase_activity__passphrase"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dip"/>
|
||||
|
||||
<EditText android:id="@+id/passphrase_edit"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:inputType="textPassword"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/repeat_label"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:layout_marginBottom="5dip"
|
||||
android:layout_marginTop="10dip"
|
||||
android:text="@string/create_passphrase_activity__repeat"/>
|
||||
|
||||
<EditText
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:inputType="textPassword"
|
||||
android:id="@+id/passphrase_edit_repeat"/>
|
||||
|
||||
<LinearLayout android:layout_width="fill_parent"
|
||||
<LinearLayout android:id="@+id/create_layout"
|
||||
android:paddingRight="16dip"
|
||||
android:paddingLeft="16dip"
|
||||
android:paddingTop="10dip"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="visible"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView style="@style/Registration.Description"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_marginBottom="16dip"
|
||||
android:gravity="right">
|
||||
android:layout_marginTop="16dip"
|
||||
android:text="@string/create_passphrase_activity__please_choose_a_passphrase_that_will_be_used_to_locally_encrypt_your_data_this_should_be_a_strong_passphrase"/>
|
||||
|
||||
<TableLayout android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:stretchColumns="*">
|
||||
<TableRow>
|
||||
<Button android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:text="@android:string/cancel"
|
||||
android:id="@+id/cancel_button"
|
||||
android:layout_marginRight="15dip"
|
||||
android:layout_marginLeft="16dip"/>
|
||||
<TextView style="@style/Registration.Label"
|
||||
android:layout_width="fill_parent"
|
||||
android:textAllCaps="true"
|
||||
android:text="@string/create_passphrase_activity__passphrase" />
|
||||
|
||||
<Button android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@android:string/ok"
|
||||
android:id="@+id/ok_button"
|
||||
android:layout_marginRight="16dip"/>
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
<EditText android:id="@+id/passphrase_edit"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword"
|
||||
android:layout_marginBottom="10dip"
|
||||
android:singleLine="true"/>
|
||||
|
||||
<TextView style="@style/Registration.Label"
|
||||
android:layout_width="fill_parent"
|
||||
android:textAllCaps="true"
|
||||
android:text="@string/create_passphrase_activity__repeat" />
|
||||
|
||||
<EditText android:id="@+id/passphrase_edit_repeat"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword"
|
||||
android:singleLine="true"/>
|
||||
|
||||
<Button style="@android:style/Widget.Button"
|
||||
android:id="@+id/ok_button"
|
||||
android:text="@string/create_passphrase_activity__continue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:layout_marginTop="20dip"
|
||||
android:layout_marginBottom="20dip"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout android:id="@+id/progress_layout"
|
||||
android:paddingRight="16dip"
|
||||
android:paddingLeft="16dip"
|
||||
android:paddingTop="10dip"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView style="@style/Registration.BigLabel"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_marginBottom="16dip"
|
||||
android:layout_marginTop="16dip"
|
||||
android:gravity="center"
|
||||
android:text="@string/create_passphrase_activity__generating_secrets"/>
|
||||
|
||||
<ProgressBar android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminate="true"
|
||||
android:layout_gravity="center"/>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
</ScrollView>
|
||||
|
126
res/layout/database_migration_activity.xml
Normal file
@ -0,0 +1,126 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:fillViewport="true"
|
||||
android:background="@drawable/background_pattern_repeat">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:gravity="center" >
|
||||
|
||||
<LinearLayout android:id="@+id/prompt_layout"
|
||||
android:paddingRight="16dip"
|
||||
android:paddingLeft="16dip"
|
||||
android:paddingTop="10dip"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView style="@style/Registration.Description"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_marginBottom="16dip"
|
||||
android:text="@string/database_migration_activity__would_you_like_to_import_your_existing_text_messages"/>
|
||||
|
||||
<ImageView android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/import_database"/>
|
||||
|
||||
<TextView style="@style/Registration.Description"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_marginBottom="16dip"
|
||||
android:layout_marginTop="16dip"
|
||||
android:text="@string/database_migration_activity__the_default_system_database_will_not_be_modified"/>
|
||||
|
||||
<LinearLayout android:orientation="horizontal"
|
||||
android:gravity="center"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dip"
|
||||
android:layout_marginRight="20dip">
|
||||
|
||||
<Button style="@android:style/Widget.Button"
|
||||
android:id="@+id/skip_button"
|
||||
android:text="@string/database_migration_activity__skip"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right"
|
||||
android:layout_marginRight="7dip"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"/>
|
||||
|
||||
<Button style="@android:style/Widget.Button"
|
||||
android:id="@+id/import_button"
|
||||
android:text="@string/database_migration_activity__import"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout android:id="@+id/progress_layout"
|
||||
android:paddingRight="16dip"
|
||||
android:paddingLeft="16dip"
|
||||
android:paddingTop="10dip"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="visible"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/import_database"/>
|
||||
|
||||
|
||||
<TextView style="@style/Registration.Description"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_marginTop="34dip"
|
||||
android:layout_marginBottom="16dip"
|
||||
android:text="@string/database_migration_activity__this_could_take_a_moment_please_be_patient"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="15dip"
|
||||
android:layout_weight="1.0" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/import_status"
|
||||
style="@style/Registration.Constant"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:text="0/100"
|
||||
android:textSize="12.0sp" />
|
||||
|
||||
<TextView
|
||||
style="@style/Registration.Constant"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:text="@string/database_migration_activity__importing"
|
||||
android:textAllCaps="true"
|
||||
android:textSize="12.0sp"
|
||||
android:textStyle="normal" />
|
||||
</RelativeLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/import_progress"
|
||||
style="@android:style/Widget.ProgressBar.Horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="14.0dip"
|
||||
android:layout_marginTop="2.0dip" />
|
||||
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
</ScrollView>
|
@ -1,39 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<EditText android:inputType="textPassword"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:id="@+id/passphrase_edit"
|
||||
android:password="true"
|
||||
android:layout_margin="16dip"/>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:fillViewport="true"
|
||||
android:background="@drawable/background_pattern_repeat">
|
||||
|
||||
<LinearLayout android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="16dip"
|
||||
android:gravity="right">
|
||||
<FrameLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:gravity="center" >
|
||||
|
||||
<TableLayout android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:stretchColumns="*">
|
||||
<TableRow>
|
||||
<Button android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:text="@android:string/cancel"
|
||||
android:id="@+id/cancel_button"
|
||||
android:layout_marginRight="15dip"
|
||||
android:layout_marginLeft="16dip"/>
|
||||
<LinearLayout android:id="@+id/prompt_layout"
|
||||
android:paddingRight="16dip"
|
||||
android:paddingLeft="16dip"
|
||||
android:paddingTop="10dip"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<Button android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@android:string/ok"
|
||||
<ImageView android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginBottom="30dip"
|
||||
android:src="@drawable/padlock_prompt"/>
|
||||
|
||||
<TextView style="@style/Registration.Label"
|
||||
android:layout_width="fill_parent"
|
||||
android:textAllCaps="true"
|
||||
android:text="@string/prompt_passphrase_activity__textsecure_passphrase" />
|
||||
|
||||
<EditText android:id="@+id/passphrase_edit"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword"
|
||||
android:layout_marginBottom="10dip"
|
||||
android:singleLine="true"/>
|
||||
|
||||
<LinearLayout android:orientation="horizontal"
|
||||
android:gravity="right"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dip"
|
||||
android:layout_marginRight="20dip">
|
||||
|
||||
<Button style="@android:style/Widget.Button"
|
||||
android:id="@+id/ok_button"
|
||||
android:layout_marginRight="16dip"/>
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
android:text="@string/prompt_passphrase_activity__unlock"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
</ScrollView>
|
@ -118,9 +118,8 @@
|
||||
<string name="PassphraseChangeActivity_incorrect_old_passphrase_exclamation">Incorrect old passphrase!</string>
|
||||
|
||||
<!-- PassphraseCreateActivity -->
|
||||
<string name="PassphraseCreateActivity_passphrases_dont_match_exclamation">Passphrases Don\'t Match!</string>
|
||||
<string name="PassphraseCreateActivity_generating_keypair">Generating KeyPair</string>
|
||||
<string name="PassphraseCreateActivity_generating_a_local_encryption_keypair">Generating a local encryption keypair...</string>
|
||||
<string name="PassphraseCreateActivity_passphrases_dont_match">Passphrases don\'t match</string>
|
||||
<string name="PassphraseCreateActivity_you_must_specify_a_password">You must specify a password</string>
|
||||
|
||||
<!-- PassphrasePromptActivity -->
|
||||
<string name="PassphrasePromptActivity_invalid_passphrase_exclamation">Invalid Passphrase!</string>
|
||||
@ -225,8 +224,9 @@
|
||||
<string name="MmsSender_currently_unable_to_send_your_mms_message">Currently unable to send your MMS message. It will be sent once service becomes available.</string>
|
||||
|
||||
<!-- ApplicationMigrationService -->
|
||||
<string name="ApplicationMigrationService_migrating">Migrating</string>
|
||||
<string name="ApplicationMigrationService_migrating_system_text_messages">Migrating System Text Messages</string>
|
||||
<string name="ApplicationMigrationService_import_in_progress">Import in progress</string>
|
||||
<string name="ApplicationMigrationService_importing_text_messages">Importing Text Messages</string>
|
||||
<string name="ApplicationMigrationService_import_complete">Import complete!</string>
|
||||
|
||||
<!-- KeyCachingService -->
|
||||
<string name="KeyCachingService_textsecure_passphrase_cached">TextSecure Passphrase Cached</string>
|
||||
@ -251,9 +251,9 @@
|
||||
<string name="auto_initiate_activity__initiate_exchange">Initiate Exchange</string>
|
||||
|
||||
<!-- change_passphrase_activity -->
|
||||
<string name="change_passphrase_activity__old_passphrase">Old passphrase:</string>
|
||||
<string name="change_passphrase_activity__new_passphrase">New passphrase:</string>
|
||||
<string name="change_passphrase_activity__repeat_new_passphrase">Repeat new passphrase:</string>
|
||||
<string name="change_passphrase_activity__old_passphrase">OLD PASSPHRASE:</string>
|
||||
<string name="change_passphrase_activity__new_passphrase">NEW PASSPHRASE:</string>
|
||||
<string name="change_passphrase_activity__repeat_new_passphrase">REPEAT NEW PASSPHRASE:</string>
|
||||
|
||||
<!-- contact_selection_group_activity -->
|
||||
<!-- contact_selection_list_activity -->
|
||||
@ -282,9 +282,23 @@
|
||||
<string name="conversation_fragment_cab__batch_selection_mode">Batch Selection Mode</string>
|
||||
|
||||
<!-- create_passphrase_activity -->
|
||||
<string name="create_passphrase_activity__please_choose_a_passphrase_that_will_be_used_to_locally_encrypt_your_data_this_should_be_a_strong_passphrase">Please choose a passphrase that will be used to locally encrypt your data. This should be a strong passphrase.</string>
|
||||
<string name="create_passphrase_activity__passphrase">Passphrase:</string>
|
||||
<string name="create_passphrase_activity__repeat">Repeat:</string>
|
||||
<string name="create_passphrase_activity__please_choose_a_passphrase_that_will_be_used_to_locally_encrypt_your_data_this_should_be_a_strong_passphrase">Please choose a passphrase that will be used to locally encrypt your data.\n\nThis should be a strong passphrase.</string>
|
||||
<string name="create_passphrase_activity__passphrase">PASSPHRASE:</string>
|
||||
<string name="create_passphrase_activity__repeat">REPEAT:</string>
|
||||
<string name="create_passphrase_activity__continue">Continue</string>
|
||||
<string name="create_passphrase_activity__generating_secrets">GENERATING SECRETS</string>
|
||||
|
||||
<!-- database_migration_activity -->
|
||||
<string name="database_migration_activity__would_you_like_to_import_your_existing_text_messages">Would you like to import your existing text messages into TextSecure\\\'s encrypted database?</string>
|
||||
<string name="database_migration_activity__the_default_system_database_will_not_be_modified">The default system database will not be modified or altered in any way.</string>
|
||||
<string name="database_migration_activity__skip">Skip</string>
|
||||
<string name="database_migration_activity__import">Import</string>
|
||||
<string name="database_migration_activity__this_could_take_a_moment_please_be_patient">This could take a moment. Please be patient, we\'ll notify you when the import is complete.</string>
|
||||
<string name="database_migration_activity__importing">IMPORTING</string>
|
||||
|
||||
<!-- prompt_passphrase_activity -->
|
||||
<string name="prompt_passphrase_activity__textsecure_passphrase">TEXTSECURE PASSPHRASE</string>
|
||||
<string name="prompt_passphrase_activity__unlock">Unlock</string>
|
||||
|
||||
<!-- receive_key_activity -->
|
||||
<string name="receive_key_activity__session">Session</string>
|
||||
|
@ -1,5 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="NoAnimation.Theme.Sherlock.Light.DarkActionBar" parent="@style/Theme.Sherlock.Light.DarkActionBar">
|
||||
<item name="android:windowAnimationStyle">@null</item>
|
||||
</style>
|
||||
|
||||
<style name="transparent_progress">
|
||||
<item name="android:windowFrame">@null</item>
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
@ -22,4 +26,48 @@
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
<style name="Registration.Description" parent="@android:style/TextAppearance">
|
||||
<item name="android:textSize">16.0sp</item>
|
||||
<item name="android:typeface">sans</item>
|
||||
<item name="android:textStyle">normal</item>
|
||||
<item name="android:textColor">#ff333333</item>
|
||||
<item name="android:gravity">left</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:shadowColor">#ffffff</item>
|
||||
<item name="android:shadowDx">1.0</item>
|
||||
<item name="android:shadowDy">1.0</item>
|
||||
<item name="android:shadowRadius">0.0</item>
|
||||
<item name="android:lineSpacingMultiplier">1.25</item>
|
||||
</style>
|
||||
|
||||
<style name="Registration.Label" parent="@android:style/TextAppearance">
|
||||
<item name="android:textSize">12.0sp</item>
|
||||
<item name="android:typeface">sans</item>
|
||||
<item name="android:textStyle">normal</item>
|
||||
<item name="android:textColor">#ff808080</item>
|
||||
<item name="android:gravity">left</item>
|
||||
<item name="android:layout_gravity">left</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:shadowColor">#ffffff</item>
|
||||
<item name="android:shadowDx">1.0</item>
|
||||
<item name="android:shadowDy">1.0</item>
|
||||
<item name="android:shadowRadius">0.0</item>
|
||||
<item name="android:lineSpacingMultiplier">1.25</item>
|
||||
</style>
|
||||
|
||||
<style name="Registration.BigLabel" parent="@style/Registration.Label">
|
||||
<item name="android:textSize">20sp</item>
|
||||
</style>
|
||||
|
||||
<style name="Registration.Constant" parent="@android:style/TextAppearance">
|
||||
<item name="android:typeface">sans</item>
|
||||
<item name="android:textStyle">normal</item>
|
||||
<item name="android:textColor">#ff808080</item>
|
||||
<item name="android:shadowColor">#ffffff</item>
|
||||
<item name="android:shadowDx">1.0</item>
|
||||
<item name="android:shadowDy">1.0</item>
|
||||
<item name="android:shadowRadius">0.0</item>
|
||||
<item name="android:lineSpacingMultiplier">1.25</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
@ -1,121 +0,0 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.service.ApplicationMigrationService;
|
||||
|
||||
public class ApplicationMigrationManager extends Handler {
|
||||
|
||||
private ProgressDialog progressDialog;
|
||||
private ApplicationMigrationListener listener;
|
||||
|
||||
private final Context context;
|
||||
private final MasterSecret masterSecret;
|
||||
|
||||
public ApplicationMigrationManager(Context context,
|
||||
MasterSecret masterSecret)
|
||||
{
|
||||
this.masterSecret = masterSecret;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public void setMigrationListener(ApplicationMigrationListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
private void displayMigrationProgress() {
|
||||
progressDialog = new ProgressDialog(context);
|
||||
progressDialog.setTitle(context.getString(R.string.ApplicationMigrationManager_migrating_database));
|
||||
progressDialog.setMessage(context.getString(R.string.ApplicationMigrationManager_migrating_text_message_database));
|
||||
progressDialog.setMax(10000);
|
||||
progressDialog.setCancelable(false);
|
||||
progressDialog.setIndeterminate(false);
|
||||
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||
progressDialog.show();
|
||||
}
|
||||
|
||||
public void migrate() {
|
||||
context.bindService(new Intent(context, ApplicationMigrationService.class),
|
||||
serviceConnection, Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
|
||||
private void displayMigrationPrompt() {
|
||||
AlertDialog.Builder alertBuilder = new AlertDialog.Builder(context);
|
||||
alertBuilder.setTitle(R.string.ApplicationMigrationManager_copy_system_text_message_database_question);
|
||||
alertBuilder.setMessage(R.string.ApplicationMigrationManager_copy_system_text_message_database_explanation);
|
||||
alertBuilder.setCancelable(false);
|
||||
|
||||
alertBuilder.setPositiveButton(R.string.ApplicationMigrationManager_copy,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
displayMigrationProgress();
|
||||
Intent intent = new Intent(context, ApplicationMigrationService.class);
|
||||
intent.setAction(ApplicationMigrationService.MIGRATE_DATABASE);
|
||||
intent.putExtra("master_secret", masterSecret);
|
||||
context.startService(intent);
|
||||
}
|
||||
});
|
||||
|
||||
alertBuilder.setNegativeButton(R.string.ApplicationMigrationManager_dont_copy,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
context.getSharedPreferences("SecureSMS", Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
.putBoolean("migrated", true).commit();
|
||||
listener.applicationMigrationComplete();
|
||||
}
|
||||
});
|
||||
|
||||
alertBuilder.create().show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
switch (message.what) {
|
||||
case ApplicationMigrationService.PROGRESS_UPDATE:
|
||||
if (progressDialog != null) {
|
||||
progressDialog.setProgress(message.arg1);
|
||||
progressDialog.setSecondaryProgress(message.arg2);
|
||||
}
|
||||
break;
|
||||
case ApplicationMigrationService.PROGRESS_COMPLETE:
|
||||
if (progressDialog != null) {
|
||||
progressDialog.dismiss();
|
||||
}
|
||||
|
||||
if (listener != null) {
|
||||
listener.applicationMigrationComplete();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static interface ApplicationMigrationListener {
|
||||
public void applicationMigrationComplete();
|
||||
}
|
||||
|
||||
private ServiceConnection serviceConnection = new ServiceConnection() {
|
||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
ApplicationMigrationService applicationMigrationService
|
||||
= ((ApplicationMigrationService.ApplicationMigrationBinder)service).getService();
|
||||
|
||||
if (applicationMigrationService.isMigrating()) displayMigrationProgress();
|
||||
else displayMigrationPrompt();
|
||||
|
||||
applicationMigrationService.setHandler(ApplicationMigrationManager.this);
|
||||
}
|
||||
|
||||
public void onServiceDisconnected(ComponentName name) {}
|
||||
};
|
||||
|
||||
}
|
@ -35,7 +35,7 @@ import org.thoughtcrime.securesms.contacts.ContactIdentityManager;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKey;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
|
||||
import org.thoughtcrime.securesms.util.Dialogs;
|
||||
import org.thoughtcrime.securesms.util.MemoryCleaner;
|
||||
import org.thoughtcrime.securesms.util.Trimmer;
|
||||
@ -138,7 +138,12 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPr
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home: finish(); return true;
|
||||
case android.R.id.home:
|
||||
Intent intent = new Intent(this, ConversationListActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -313,9 +318,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPr
|
||||
private class ChangePassphraseClickListener implements Preference.OnPreferenceClickListener {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
SharedPreferences settings = getSharedPreferences(KeyCachingService.PREFERENCES_NAME, 0);
|
||||
|
||||
if (settings.getBoolean("passphrase_initialized", false)) {
|
||||
if (MasterSecretUtil.isPassphraseInitialized(ApplicationPreferencesActivity.this)) {
|
||||
startActivity(new Intent(ApplicationPreferencesActivity.this, PassphraseChangeActivity.class));
|
||||
} else {
|
||||
Toast.makeText(ApplicationPreferencesActivity.this,
|
||||
|
@ -236,7 +236,7 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
|
||||
case R.id.menu_verify_recipient: handleVerifyRecipient(); return true;
|
||||
case R.id.menu_verify_session: handleVerifySession(); return true;
|
||||
case R.id.menu_group_recipients: handleDisplayGroupRecipients(); return true;
|
||||
case android.R.id.home: finish(); return true;
|
||||
case android.R.id.home: handleReturnToConversationList(); return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -261,6 +261,14 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
|
||||
|
||||
//////// Event Handlers
|
||||
|
||||
private void handleReturnToConversationList() {
|
||||
Intent intent = new Intent(this, ConversationListActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
intent.putExtra("master_secret", masterSecret);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
private void handleVerifyRecipient() {
|
||||
Intent verifyIdentityIntent = new Intent(this, VerifyIdentityActivity.class);
|
||||
verifyIdentityIntent.putExtra("recipient", getRecipients().getPrimaryRecipient());
|
||||
|
@ -1,25 +1,16 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.provider.ContactsContract;
|
||||
import android.util.Log;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationExportManager.ApplicationExportListener;
|
||||
import org.thoughtcrime.securesms.crypto.DecryptingQueue;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
import org.thoughtcrime.securesms.service.SendReceiveService;
|
||||
@ -36,11 +27,6 @@ public class ConversationListActivity extends PassphraseRequiredSherlockFragment
|
||||
private ConversationListFragment fragment;
|
||||
private MasterSecret masterSecret;
|
||||
|
||||
private ApplicationMigrationManager migrationManager;
|
||||
|
||||
private boolean havePromptedForPassphrase = false;
|
||||
private boolean isVisible = false;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
@ -52,32 +38,6 @@ public class ConversationListActivity extends PassphraseRequiredSherlockFragment
|
||||
initializeContactUpdatesReceiver();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
this.setIntent(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
isVisible = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
|
||||
isVisible = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
havePromptedForPassphrase = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
Log.w("ConversationListActivity", "onDestroy...");
|
||||
@ -87,47 +47,17 @@ public class ConversationListActivity extends PassphraseRequiredSherlockFragment
|
||||
|
||||
@Override
|
||||
public void onMasterSecretCleared() {
|
||||
this.masterSecret = null;
|
||||
this.fragment.setMasterSecret(null);
|
||||
this.invalidateOptionsMenu();
|
||||
|
||||
if (!havePromptedForPassphrase && isVisible) {
|
||||
promptForPassphrase();
|
||||
}
|
||||
// this.fragment.setMasterSecret(null);
|
||||
startActivity(new Intent(this, RoutingActivity.class));
|
||||
super.onMasterSecretCleared();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewMasterSecret(MasterSecret masterSecret) {
|
||||
this.masterSecret = masterSecret;
|
||||
|
||||
if (masterSecret != null) {
|
||||
if (!IdentityKeyUtil.hasIdentityKey(this)) {
|
||||
new Thread(new IdentityKeyInitializer()).start();
|
||||
}
|
||||
|
||||
if (!MasterSecretUtil.hasAsymmericMasterSecret(this)) {
|
||||
new Thread(new AsymmetricMasteSecretInitializer()).start();
|
||||
}
|
||||
|
||||
if (!isDatabaseMigrated()) initializeDatabaseMigration();
|
||||
else DecryptingQueue.schedulePendingDecrypts(this, masterSecret);
|
||||
}
|
||||
|
||||
this.fragment.setMasterSecret(masterSecret);
|
||||
this.invalidateOptionsMenu();
|
||||
this.havePromptedForPassphrase = false;
|
||||
createConversationIfNecessary(this.getIntent());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
Log.w("ConversationListActivity", "onPrepareOptionsMenu...");
|
||||
MenuInflater inflater = this.getSupportMenuInflater();
|
||||
menu.clear();
|
||||
|
||||
if (this.masterSecret == null) inflater.inflate(R.menu.text_secure_locked, menu);
|
||||
else inflater.inflate(R.menu.text_secure_normal, menu);
|
||||
inflater.inflate(R.menu.text_secure_normal, menu);
|
||||
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
return true;
|
||||
@ -138,12 +68,11 @@ public class ConversationListActivity extends PassphraseRequiredSherlockFragment
|
||||
super.onOptionsItemSelected(item);
|
||||
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_new_message: createConversation(-1, null, null, null, null); return true;
|
||||
case R.id.menu_unlock: promptForPassphrase(); return true;
|
||||
case R.id.menu_settings: handleDisplaySettings(); return true;
|
||||
case R.id.menu_export: handleExportDatabase(); return true;
|
||||
case R.id.menu_import: handleImportDatabase(); return true;
|
||||
case R.id.menu_clear_passphrase: handleClearPassphrase(); return true;
|
||||
case R.id.menu_new_message: createConversation(-1, null); return true;
|
||||
case R.id.menu_settings: handleDisplaySettings(); return true;
|
||||
case R.id.menu_export: handleExportDatabase(); return true;
|
||||
case R.id.menu_import: handleImportDatabase(); return true;
|
||||
case R.id.menu_clear_passphrase: handleClearPassphrase(); return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -151,39 +80,18 @@ public class ConversationListActivity extends PassphraseRequiredSherlockFragment
|
||||
|
||||
@Override
|
||||
public void onCreateConversation(long threadId, Recipients recipients) {
|
||||
createConversation(threadId, recipients, null, null, null);
|
||||
createConversation(threadId, recipients);
|
||||
}
|
||||
|
||||
private void createConversation(long threadId, Recipients recipients,
|
||||
String text, Uri imageUri, Uri audioUri)
|
||||
{
|
||||
if (this.masterSecret == null) {
|
||||
promptForPassphrase();
|
||||
return;
|
||||
}
|
||||
|
||||
private void createConversation(long threadId, Recipients recipients) {
|
||||
Intent intent = new Intent(this, ConversationActivity.class);
|
||||
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients);
|
||||
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
|
||||
intent.putExtra(ConversationActivity.MASTER_SECRET_EXTRA, masterSecret);
|
||||
intent.putExtra(ConversationActivity.DRAFT_TEXT_EXTRA, text);
|
||||
intent.putExtra(ConversationActivity.DRAFT_IMAGE_EXTRA, imageUri);
|
||||
intent.putExtra(ConversationActivity.DRAFT_AUDIO_EXTRA, audioUri);
|
||||
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void promptForPassphrase() {
|
||||
havePromptedForPassphrase = true;
|
||||
if (hasSelectedPassphrase()) startActivity(new Intent(this, PassphrasePromptActivity.class));
|
||||
else startActivity(new Intent(this, PassphraseCreateActivity.class));
|
||||
}
|
||||
|
||||
private boolean hasSelectedPassphrase() {
|
||||
SharedPreferences settings = getSharedPreferences(KeyCachingService.PREFERENCES_NAME, 0);
|
||||
return settings.getBoolean("passphrase_initialized", false);
|
||||
}
|
||||
|
||||
private void handleDisplaySettings() {
|
||||
Intent preferencesIntent = new Intent(this, ApplicationPreferencesActivity.class);
|
||||
preferencesIntent.putExtra("master_secret", masterSecret);
|
||||
@ -237,89 +145,17 @@ public class ConversationListActivity extends PassphraseRequiredSherlockFragment
|
||||
startService(mmsSenderIntent);
|
||||
}
|
||||
|
||||
private void initializeDatabaseMigration() {
|
||||
if (migrationManager == null) {
|
||||
migrationManager = new ApplicationMigrationManager(this, masterSecret);
|
||||
|
||||
ApplicationMigrationManager.ApplicationMigrationListener listener =
|
||||
new ApplicationMigrationManager.ApplicationMigrationListener() {
|
||||
@Override
|
||||
public void applicationMigrationComplete() {
|
||||
if (masterSecret != null)
|
||||
DecryptingQueue.schedulePendingDecrypts(ConversationListActivity.this,
|
||||
masterSecret);
|
||||
}
|
||||
};
|
||||
|
||||
migrationManager.setMigrationListener(listener);
|
||||
migrationManager.migrate();
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE,
|
||||
WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
|
||||
this.masterSecret = (MasterSecret)getIntent().getParcelableExtra("master_secret");
|
||||
|
||||
this.fragment = (ConversationListFragment)this.getSupportFragmentManager()
|
||||
.findFragmentById(R.id.fragment_content);
|
||||
}
|
||||
|
||||
private boolean isDatabaseMigrated() {
|
||||
return this.getSharedPreferences("SecureSMS", Context.MODE_PRIVATE)
|
||||
.getBoolean("migrated", false);
|
||||
}
|
||||
|
||||
private void createConversationIfNecessary(Intent intent) {
|
||||
long thread = intent.getLongExtra("thread_id", -1L);
|
||||
String type = intent.getType();
|
||||
Recipients recipients = null;
|
||||
String draftText = null;
|
||||
Uri draftImage = null;
|
||||
Uri draftAudio = null;
|
||||
|
||||
if (Intent.ACTION_SENDTO.equals(intent.getAction())) {
|
||||
try {
|
||||
recipients = RecipientFactory.getRecipientsFromString(this, intent.getData().getSchemeSpecificPart(), false);
|
||||
thread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipients);
|
||||
} catch (RecipientFormattingException rfe) {
|
||||
recipients = null;
|
||||
}
|
||||
} else if (Intent.ACTION_SEND.equals(intent.getAction())) {
|
||||
if ("text/plain".equals(type)) {
|
||||
draftText = intent.getStringExtra(Intent.EXTRA_TEXT);
|
||||
} else if (type.startsWith("image/")) {
|
||||
draftImage = intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
} else if (type.startsWith("audio/")) {
|
||||
draftAudio = intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
}
|
||||
} else {
|
||||
recipients = intent.getParcelableExtra("recipients");
|
||||
}
|
||||
|
||||
if (recipients != null || Intent.ACTION_SEND.equals(intent.getAction())) {
|
||||
createConversation(thread, recipients, draftText, draftImage, draftAudio);
|
||||
|
||||
intent.putExtra("thread_id", -1L);
|
||||
intent.putExtra("recipients", (Parcelable)null);
|
||||
intent.putExtra(Intent.EXTRA_TEXT, (String)null);
|
||||
intent.putExtra(Intent.EXTRA_STREAM, (Parcelable)null);
|
||||
intent.setAction(null);
|
||||
}
|
||||
}
|
||||
|
||||
private class IdentityKeyInitializer implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
IdentityKeyUtil.generateIdentityKeys(ConversationListActivity.this, masterSecret);
|
||||
}
|
||||
}
|
||||
|
||||
private class AsymmetricMasteSecretInitializer implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
MasterSecretUtil.generateAsymmetricMasterSecret(ConversationListActivity.this, masterSecret);
|
||||
}
|
||||
this.fragment.setMasterSecret(masterSecret);
|
||||
}
|
||||
}
|
||||
|
189
src/org/thoughtcrime/securesms/DatabaseMigrationActivity.java
Normal file
@ -0,0 +1,189 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
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.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.thoughtcrime.securesms.database.SmsMigrator.ProgressDescription;
|
||||
import org.thoughtcrime.securesms.service.ApplicationMigrationService;
|
||||
import org.thoughtcrime.securesms.service.ApplicationMigrationService.ImportState;
|
||||
|
||||
public class DatabaseMigrationActivity extends PassphraseRequiredSherlockActivity {
|
||||
|
||||
private final ImportServiceConnection serviceConnection = new ImportServiceConnection();
|
||||
private final ImportStateHandler importStateHandler = new ImportStateHandler();
|
||||
private final BroadcastReceiver completedReceiver = new NullReceiver();
|
||||
|
||||
private LinearLayout promptLayout;
|
||||
private LinearLayout progressLayout;
|
||||
private Button skipButton;
|
||||
private Button importButton;
|
||||
private ProgressBar progress;
|
||||
private TextView progressLabel;
|
||||
|
||||
private ApplicationMigrationService importService;
|
||||
private boolean isVisible = false;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
setContentView(R.layout.database_migration_activity);
|
||||
|
||||
initializeResources();
|
||||
initializeServiceBinding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
isVisible = true;
|
||||
registerForCompletedNotification();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
isVisible = false;
|
||||
unregisterForCompletedNotification();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
shutdownServiceBinding();
|
||||
}
|
||||
|
||||
private void initializeServiceBinding() {
|
||||
Intent intent = new Intent(this, ApplicationMigrationService.class);
|
||||
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
this.promptLayout = (LinearLayout)findViewById(R.id.prompt_layout);
|
||||
this.progressLayout = (LinearLayout)findViewById(R.id.progress_layout);
|
||||
this.skipButton = (Button) findViewById(R.id.skip_button);
|
||||
this.importButton = (Button) findViewById(R.id.import_button);
|
||||
this.progress = (ProgressBar) findViewById(R.id.import_progress);
|
||||
this.progressLabel = (TextView) findViewById(R.id.import_status);
|
||||
|
||||
this.progressLayout.setVisibility(View.GONE);
|
||||
this.promptLayout.setVisibility(View.GONE);
|
||||
|
||||
this.importButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(DatabaseMigrationActivity.this, ApplicationMigrationService.class);
|
||||
intent.setAction(ApplicationMigrationService.MIGRATE_DATABASE);
|
||||
intent.putExtra("master_secret", getIntent().getParcelableExtra("master_secret"));
|
||||
startService(intent);
|
||||
|
||||
promptLayout.setVisibility(View.GONE);
|
||||
progressLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
|
||||
this.skipButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
ApplicationMigrationService.setDatabaseImported(DatabaseMigrationActivity.this);
|
||||
handleImportComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void registerForCompletedNotification() {
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(ApplicationMigrationService.COMPLETED_ACTION);
|
||||
filter.setPriority(1000);
|
||||
|
||||
registerReceiver(completedReceiver, filter);
|
||||
}
|
||||
|
||||
private void unregisterForCompletedNotification() {
|
||||
unregisterReceiver(completedReceiver);
|
||||
}
|
||||
|
||||
private void shutdownServiceBinding() {
|
||||
unbindService(serviceConnection);
|
||||
}
|
||||
|
||||
private void handleStateIdle() {
|
||||
this.promptLayout.setVisibility(View.VISIBLE);
|
||||
this.progressLayout.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void handleStateProgress(ProgressDescription update) {
|
||||
this.promptLayout.setVisibility(View.GONE);
|
||||
this.progressLayout.setVisibility(View.VISIBLE);
|
||||
this.progressLabel.setText(update.primaryComplete + "/" + update.primaryTotal);
|
||||
|
||||
double max = this.progress.getMax();
|
||||
double primaryTotal = update.primaryTotal;
|
||||
double primaryComplete = update.primaryComplete;
|
||||
double secondaryTotal = update.secondaryTotal;
|
||||
double secondaryComplete = update.secondaryComplete;
|
||||
|
||||
this.progress.setProgress((int)Math.round((primaryComplete / primaryTotal) * max));
|
||||
this.progress.setSecondaryProgress((int)Math.round((secondaryComplete / secondaryTotal) * max));
|
||||
}
|
||||
|
||||
private void handleImportComplete() {
|
||||
if (isVisible) {
|
||||
if (getIntent().hasExtra("next_intent")) {
|
||||
startActivity((Intent)getIntent().getParcelableExtra("next_intent"));
|
||||
} else {
|
||||
startActivity(new Intent(this, ConversationListActivity.class));
|
||||
}
|
||||
}
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
private class ImportStateHandler extends Handler {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
switch (message.what) {
|
||||
case ImportState.STATE_IDLE: handleStateIdle(); break;
|
||||
case ImportState.STATE_MIGRATING_IN_PROGRESS: handleStateProgress((ProgressDescription)message.obj); break;
|
||||
case ImportState.STATE_MIGRATING_COMPLETE: handleImportComplete(); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ImportServiceConnection implements ServiceConnection {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
importService = ((ApplicationMigrationService.ApplicationMigrationBinder)service).getService();
|
||||
importService.setImportStateHandler(importStateHandler);
|
||||
|
||||
ImportState state = importService.getState();
|
||||
importStateHandler.obtainMessage(state.state, state.progress).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
importService.setImportStateHandler(null);
|
||||
}
|
||||
}
|
||||
|
||||
private class NullReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
abortBroadcast();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -22,12 +22,12 @@ import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
|
||||
import com.actionbarsherlock.app.SherlockActivity;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
import org.thoughtcrime.securesms.util.MemoryCleaner;
|
||||
|
||||
import com.actionbarsherlock.app.SherlockActivity;
|
||||
|
||||
/**
|
||||
* Base Activity for changing/prompting local encryption passphrase.
|
||||
*
|
||||
@ -44,9 +44,14 @@ public abstract class PassphraseActivity extends SherlockActivity {
|
||||
bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
|
||||
protected MasterSecret getMasterSecret() {
|
||||
return masterSecret;
|
||||
}
|
||||
|
||||
protected abstract void cleanup();
|
||||
|
||||
private ServiceConnection serviceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
keyCachingService = ((KeyCachingService.KeyCachingBinder)service).getService();
|
||||
keyCachingService.setMasterSecret(masterSecret);
|
||||
@ -55,9 +60,12 @@ public abstract class PassphraseActivity extends SherlockActivity {
|
||||
|
||||
MemoryCleaner.clean(masterSecret);
|
||||
cleanup();
|
||||
|
||||
PassphraseActivity.this.setResult(RESULT_OK);
|
||||
PassphraseActivity.this.finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
keyCachingService = null;
|
||||
}
|
||||
|
@ -16,18 +16,19 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
|
||||
import org.thoughtcrime.securesms.util.MemoryCleaner;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
/**
|
||||
* Activity for creating a user's local encryption passphrase.
|
||||
@ -37,10 +38,12 @@ import org.thoughtcrime.securesms.util.MemoryCleaner;
|
||||
|
||||
public class PassphraseCreateActivity extends PassphraseActivity {
|
||||
|
||||
private LinearLayout createLayout;
|
||||
private LinearLayout progressLayout;
|
||||
|
||||
private EditText passphraseEdit;
|
||||
private EditText passphraseRepeatEdit;
|
||||
private Button okButton;
|
||||
private Button cancelButton;
|
||||
|
||||
public PassphraseCreateActivity() { }
|
||||
|
||||
@ -54,10 +57,11 @@ public class PassphraseCreateActivity extends PassphraseActivity {
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
this.passphraseEdit = (EditText) findViewById(R.id.passphrase_edit);
|
||||
this.passphraseRepeatEdit = (EditText) findViewById(R.id.passphrase_edit_repeat);
|
||||
this.okButton = (Button) findViewById(R.id.ok_button);
|
||||
this.cancelButton = (Button) findViewById(R.id.cancel_button);
|
||||
this.createLayout = (LinearLayout)findViewById(R.id.create_layout);
|
||||
this.progressLayout = (LinearLayout)findViewById(R.id.progress_layout);
|
||||
this.passphraseEdit = (EditText) findViewById(R.id.passphrase_edit);
|
||||
this.passphraseRepeatEdit = (EditText) findViewById(R.id.passphrase_edit_repeat);
|
||||
this.okButton = (Button) findViewById(R.id.ok_button);
|
||||
|
||||
this.okButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
@ -65,45 +69,36 @@ public class PassphraseCreateActivity extends PassphraseActivity {
|
||||
verifyAndSavePassphrases();
|
||||
}
|
||||
});
|
||||
|
||||
this.cancelButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void verifyAndSavePassphrases() {
|
||||
if (Util.isEmpty(this.passphraseEdit) || Util.isEmpty(this.passphraseRepeatEdit)) {
|
||||
Toast.makeText(this, R.string.PassphraseCreateActivity_you_must_specify_a_password, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
String passphrase = this.passphraseEdit.getText().toString();
|
||||
String passphraseRepeat = this.passphraseRepeatEdit.getText().toString();
|
||||
|
||||
if (!passphrase.equals(passphraseRepeat)) {
|
||||
Toast.makeText(getApplicationContext(),
|
||||
R.string.PassphraseCreateActivity_passphrases_dont_match_exclamation,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(this, R.string.PassphraseCreateActivity_passphrases_dont_match, Toast.LENGTH_SHORT).show();
|
||||
this.passphraseEdit.setText("");
|
||||
this.passphraseRepeatEdit.setText("");
|
||||
} else {
|
||||
// We do this, but the edit boxes are basically impossible to clean up.
|
||||
MemoryCleaner.clean(passphraseRepeat);
|
||||
new SecretGenerator().execute(passphrase);
|
||||
return;
|
||||
}
|
||||
|
||||
// We do this, but the edit boxes are basically impossible to clean up.
|
||||
MemoryCleaner.clean(passphraseRepeat);
|
||||
new SecretGenerator().execute(passphrase);
|
||||
}
|
||||
|
||||
private class SecretGenerator extends AsyncTask<String, Void, Void> {
|
||||
private ProgressDialog progressDialog;
|
||||
private MasterSecret masterSecret;
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
progressDialog = new ProgressDialog(PassphraseCreateActivity.this);
|
||||
progressDialog.setTitle(R.string.PassphraseCreateActivity_generating_keypair);
|
||||
progressDialog.setMessage(getString(R.string.PassphraseCreateActivity_generating_a_local_encryption_keypair));
|
||||
progressDialog.setCancelable(false);
|
||||
progressDialog.setIndeterminate(true);
|
||||
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
|
||||
progressDialog.show();
|
||||
createLayout.setVisibility(View.GONE);
|
||||
progressLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -123,7 +118,6 @@ public class PassphraseCreateActivity extends PassphraseActivity {
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void param) {
|
||||
progressDialog.dismiss();
|
||||
setMasterSecret(masterSecret);
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,6 @@ public class PassphrasePromptActivity extends PassphraseActivity {
|
||||
|
||||
private EditText passphraseText;
|
||||
private Button okButton;
|
||||
private Button cancelButton;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
@ -51,13 +50,12 @@ public class PassphrasePromptActivity extends PassphraseActivity {
|
||||
private void initializeResources() {
|
||||
passphraseText = (EditText)findViewById(R.id.passphrase_edit);
|
||||
okButton = (Button)findViewById(R.id.ok_button);
|
||||
cancelButton = (Button)findViewById(R.id.cancel_button);
|
||||
|
||||
okButton.setOnClickListener(new OkButtonClickListener());
|
||||
cancelButton.setOnClickListener(new CancelButtonClickListener());
|
||||
}
|
||||
|
||||
private class OkButtonClickListener implements OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
try {
|
||||
Editable text = passphraseText.getText();
|
||||
@ -74,16 +72,9 @@ public class PassphrasePromptActivity extends PassphraseActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private class CancelButtonClickListener implements OnClickListener {
|
||||
public void onClick(View v) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup() {
|
||||
this.passphraseText = null;
|
||||
System.gc();
|
||||
}
|
||||
|
||||
}
|
||||
|
218
src/org/thoughtcrime/securesms/RoutingActivity.java
Normal file
@ -0,0 +1,218 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.service.ApplicationMigrationService;
|
||||
|
||||
public class RoutingActivity extends PassphraseRequiredSherlockActivity {
|
||||
|
||||
private static final int STATE_CREATE_PASSPHRASE = 1;
|
||||
private static final int STATE_PROMPT_PASSPHRASE = 2;
|
||||
private static final int STATE_IMPORT_DATABASE = 3;
|
||||
private static final int STATE_CONVERSATION_OR_LIST = 4;
|
||||
|
||||
private MasterSecret masterSecret = null;
|
||||
private boolean isVisible = false;
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
setIntent(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
this.isVisible = true;
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
this.isVisible = false;
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewMasterSecret(MasterSecret masterSecret) {
|
||||
this.masterSecret = masterSecret;
|
||||
|
||||
if (isVisible) {
|
||||
routeApplicationState();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMasterSecretCleared() {
|
||||
this.masterSecret = null;
|
||||
|
||||
if (isVisible) {
|
||||
routeApplicationState();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (resultCode == RESULT_CANCELED)
|
||||
finish();
|
||||
}
|
||||
|
||||
private void routeApplicationState() {
|
||||
int state = getApplicationState();
|
||||
|
||||
switch (state) {
|
||||
case STATE_CREATE_PASSPHRASE: handleCreatePassphrase(); break;
|
||||
case STATE_PROMPT_PASSPHRASE: handlePromptPassphrase(); break;
|
||||
case STATE_IMPORT_DATABASE: handleImportDatabase(); break;
|
||||
case STATE_CONVERSATION_OR_LIST: handleDisplayConversationOrList(); break;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCreatePassphrase() {
|
||||
Intent intent = new Intent(this, PassphraseCreateActivity.class);
|
||||
startActivityForResult(intent, 1);
|
||||
}
|
||||
|
||||
private void handlePromptPassphrase() {
|
||||
Intent intent = new Intent(this, PassphrasePromptActivity.class);
|
||||
startActivityForResult(intent, 2);
|
||||
}
|
||||
|
||||
private void handleImportDatabase() {
|
||||
Intent intent = new Intent(this, DatabaseMigrationActivity.class);
|
||||
intent.putExtra("master_secret", masterSecret);
|
||||
intent.putExtra("next_intent", getConversationListIntent());
|
||||
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
private void handleDisplayConversationOrList() {
|
||||
ConversationParameters parameters = getConversationParameters();
|
||||
|
||||
Intent intent;
|
||||
|
||||
if (isShareAction() || parameters.recipients != null) {
|
||||
intent = getConversationIntent(parameters);
|
||||
} else {
|
||||
intent = getConversationListIntent();
|
||||
}
|
||||
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
private Intent getConversationIntent(ConversationParameters parameters) {
|
||||
Intent intent = new Intent(this, ConversationActivity.class);
|
||||
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, parameters.recipients);
|
||||
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, parameters.thread);
|
||||
intent.putExtra(ConversationActivity.MASTER_SECRET_EXTRA, masterSecret);
|
||||
intent.putExtra(ConversationActivity.DRAFT_TEXT_EXTRA, parameters.draftText);
|
||||
intent.putExtra(ConversationActivity.DRAFT_IMAGE_EXTRA, parameters.draftImage);
|
||||
intent.putExtra(ConversationActivity.DRAFT_AUDIO_EXTRA, parameters.draftAudio);
|
||||
|
||||
return intent;
|
||||
}
|
||||
|
||||
private Intent getConversationListIntent() {
|
||||
Intent intent = new Intent(this, ConversationListActivity.class);
|
||||
intent.putExtra("master_secret", masterSecret);
|
||||
|
||||
return intent;
|
||||
}
|
||||
|
||||
private int getApplicationState() {
|
||||
if (!MasterSecretUtil.isPassphraseInitialized(this))
|
||||
return STATE_CREATE_PASSPHRASE;
|
||||
|
||||
if (masterSecret == null)
|
||||
return STATE_PROMPT_PASSPHRASE;
|
||||
|
||||
if (!ApplicationMigrationService.isDatabaseImported(this))
|
||||
return STATE_IMPORT_DATABASE;
|
||||
|
||||
return STATE_CONVERSATION_OR_LIST;
|
||||
}
|
||||
|
||||
private ConversationParameters getConversationParameters() {
|
||||
if (isSendAction()) {
|
||||
return getConversationParametersForSendAction();
|
||||
} else if (isShareAction()) {
|
||||
return getConversationParametersForShareAction();
|
||||
} else {
|
||||
return getConversationParametersForInternalAction();
|
||||
}
|
||||
}
|
||||
|
||||
private ConversationParameters getConversationParametersForSendAction() {
|
||||
Recipients recipients = null;
|
||||
long threadId = getIntent().getLongExtra("thread_id", -1);
|
||||
|
||||
try {
|
||||
String data = getIntent().getData().getSchemeSpecificPart();
|
||||
recipients = RecipientFactory.getRecipientsFromString(this, data, false);
|
||||
threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipients);
|
||||
} catch (RecipientFormattingException rfe) {
|
||||
recipients = null;
|
||||
}
|
||||
|
||||
return new ConversationParameters(threadId, recipients, null, null, null);
|
||||
}
|
||||
|
||||
private ConversationParameters getConversationParametersForShareAction() {
|
||||
String type = getIntent().getType();
|
||||
String draftText = null;
|
||||
Uri draftImage = null;
|
||||
Uri draftAudio = null;
|
||||
|
||||
if ("text/plain".equals(type)) {
|
||||
draftText = getIntent().getStringExtra(Intent.EXTRA_TEXT);
|
||||
} else if (type.startsWith("image/")) {
|
||||
draftImage = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
} else if (type.startsWith("audio/")) {
|
||||
draftAudio = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
}
|
||||
|
||||
return new ConversationParameters(-1, null, draftText, draftImage, draftAudio);
|
||||
}
|
||||
|
||||
private ConversationParameters getConversationParametersForInternalAction() {
|
||||
long threadId = getIntent().getLongExtra("thread_id", -1);
|
||||
Recipients recipients = getIntent().getParcelableExtra("recipients");
|
||||
|
||||
return new ConversationParameters(threadId, recipients, null, null, null);
|
||||
}
|
||||
|
||||
private boolean isShareAction() {
|
||||
return Intent.ACTION_SEND.equals(getIntent().getAction());
|
||||
}
|
||||
|
||||
private boolean isSendAction() {
|
||||
return Intent.ACTION_SENDTO.equals(getIntent().getAction());
|
||||
}
|
||||
|
||||
private static class ConversationParameters {
|
||||
public final long thread;
|
||||
public final Recipients recipients;
|
||||
public final String draftText;
|
||||
public final Uri draftImage;
|
||||
public final Uri draftAudio;
|
||||
|
||||
public ConversationParameters(long thread, Recipients recipients,
|
||||
String draftText, Uri draftImage, Uri draftAudio)
|
||||
{
|
||||
this.thread = thread;
|
||||
this.recipients = recipients;
|
||||
this.draftText = draftText;
|
||||
this.draftImage = draftImage;
|
||||
this.draftAudio = draftAudio;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -16,6 +16,15 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms.crypto;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.util.Log;
|
||||
|
||||
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
@ -31,15 +40,6 @@ import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.crypto.spec.PBEParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Helper class for generating and securely storing a MasterSecret.
|
||||
*
|
||||
@ -134,6 +134,11 @@ public class MasterSecretUtil {
|
||||
return settings.contains(ASYMMETRIC_LOCAL_PUBLIC);
|
||||
}
|
||||
|
||||
public static boolean isPassphraseInitialized(Context context) {
|
||||
SharedPreferences preferences = context.getSharedPreferences(PREFERENCES_NAME, 0);
|
||||
return preferences.getBoolean("passphrase_initialized", false);
|
||||
}
|
||||
|
||||
private static void encryptWithPassphraseAndSave(Context context, byte[] masterSecret, String passphrase) throws GeneralSecurityException {
|
||||
byte[] encryptedMasterSecret = encryptWithPassphrase(context, masterSecret, passphrase);
|
||||
byte[] encryptedAndMacdMasterSecret = macWithPassphrase(context, encryptedMasterSecret, passphrase);
|
||||
|
@ -150,7 +150,7 @@ public class SmsMigrator {
|
||||
|
||||
private static void migrateConversation(Context context, MasterSecret masterSecret,
|
||||
SmsMigrationProgressListener listener,
|
||||
int primaryProgress,
|
||||
ProgressDescription progress,
|
||||
long theirThreadId, long ourThreadId)
|
||||
{
|
||||
SmsDatabase ourSmsDatabase = DatabaseFactory.getSmsDatabase(context);
|
||||
@ -166,11 +166,7 @@ public class SmsMigrator {
|
||||
getContentValuesForRow(context, masterSecret, cursor, ourThreadId, statement);
|
||||
statement.execute();
|
||||
|
||||
double position = cursor.getPosition();
|
||||
double count = cursor.getCount();
|
||||
double progress = position / count;
|
||||
|
||||
listener.progressUpdate(primaryProgress, (int)(progress * 10000));
|
||||
listener.progressUpdate(new ProgressDescription(progress, cursor.getCount(), cursor.getPosition()));
|
||||
}
|
||||
|
||||
ourSmsDatabase.endTransaction(transaction);
|
||||
@ -192,31 +188,26 @@ public class SmsMigrator {
|
||||
|
||||
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||
Cursor cursor = null;
|
||||
int primaryProgress = 0;
|
||||
|
||||
try {
|
||||
Uri threadListUri = Uri.parse("content://mms-sms/conversations?simple=true");
|
||||
cursor = context.getContentResolver().query(threadListUri, null, null, null, "date ASC");
|
||||
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
long theirThreadId = cursor.getLong(cursor.getColumnIndexOrThrow("_id"));
|
||||
String theirRecipients = cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids"));
|
||||
Recipients ourRecipients = getOurRecipients(context, theirRecipients);
|
||||
long theirThreadId = cursor.getLong(cursor.getColumnIndexOrThrow("_id"));
|
||||
String theirRecipients = cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids"));
|
||||
Recipients ourRecipients = getOurRecipients(context, theirRecipients);
|
||||
ProgressDescription progress = new ProgressDescription(cursor.getCount(), cursor.getPosition(), 100, 0);
|
||||
|
||||
if (ourRecipients != null) {
|
||||
long ourThreadId = threadDatabase.getThreadIdFor(ourRecipients);
|
||||
migrateConversation(context, masterSecret,
|
||||
listener, primaryProgress,
|
||||
listener, progress,
|
||||
theirThreadId, ourThreadId);
|
||||
}
|
||||
|
||||
double position = cursor.getPosition() + 1;
|
||||
double count = cursor.getCount();
|
||||
double progress = position / count;
|
||||
|
||||
primaryProgress = (int)(progress * 10000);
|
||||
|
||||
listener.progressUpdate(primaryProgress, 0);
|
||||
progress.incrementPrimaryComplete();
|
||||
listener.progressUpdate(progress);
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
@ -228,6 +219,34 @@ public class SmsMigrator {
|
||||
}
|
||||
|
||||
public interface SmsMigrationProgressListener {
|
||||
public void progressUpdate(int primaryProgress, int secondaryProgress);
|
||||
public void progressUpdate(ProgressDescription description);
|
||||
}
|
||||
|
||||
public static class ProgressDescription {
|
||||
public final int primaryTotal;
|
||||
public int primaryComplete;
|
||||
public final int secondaryTotal;
|
||||
public final int secondaryComplete;
|
||||
|
||||
public ProgressDescription(int primaryTotal, int primaryComplete,
|
||||
int secondaryTotal, int secondaryComplete)
|
||||
{
|
||||
this.primaryTotal = primaryTotal;
|
||||
this.primaryComplete = primaryComplete;
|
||||
this.secondaryTotal = secondaryTotal;
|
||||
this.secondaryComplete = secondaryComplete;
|
||||
}
|
||||
|
||||
public ProgressDescription(ProgressDescription that, int secondaryTotal, int secondaryComplete) {
|
||||
this.primaryComplete = that.primaryComplete;
|
||||
this.primaryTotal = that.primaryTotal;
|
||||
this.secondaryComplete = secondaryComplete;
|
||||
this.secondaryTotal = secondaryTotal;
|
||||
}
|
||||
|
||||
public void incrementPrimaryComplete() {
|
||||
primaryComplete += 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -37,8 +37,8 @@ import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
|
||||
import org.thoughtcrime.securesms.ConversationListActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.RoutingActivity;
|
||||
import org.thoughtcrime.securesms.contacts.ContactPhotoFactory;
|
||||
import org.thoughtcrime.securesms.crypto.MasterCipher;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
@ -79,7 +79,7 @@ public class MessageNotifier {
|
||||
if (visibleThread == threadId) {
|
||||
sendInThreadNotification(context);
|
||||
} else {
|
||||
Intent intent = new Intent(context, ConversationListActivity.class);
|
||||
Intent intent = new Intent(context, RoutingActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
intent.putExtra("recipients", recipients);
|
||||
intent.putExtra("thread_id", threadId);
|
||||
@ -187,7 +187,7 @@ public class MessageNotifier {
|
||||
notificationState.getMessageCount()));
|
||||
builder.setContentText(String.format(context.getString(R.string.MessageNotifier_most_recent_from_s),
|
||||
notifications.get(0).getRecipientName()));
|
||||
builder.setContentIntent(PendingIntent.getActivity(context, 0, new Intent(context, ConversationListActivity.class), 0));
|
||||
builder.setContentIntent(PendingIntent.getActivity(context, 0, new Intent(context, RoutingActivity.class), 0));
|
||||
|
||||
InboxStyle style = new InboxStyle();
|
||||
|
||||
|
@ -6,7 +6,7 @@ import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.text.SpannableStringBuilder;
|
||||
|
||||
import org.thoughtcrime.securesms.ConversationListActivity;
|
||||
import org.thoughtcrime.securesms.RoutingActivity;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
@ -62,7 +62,7 @@ public class NotificationItem {
|
||||
}
|
||||
|
||||
public PendingIntent getPendingIntent(Context context) {
|
||||
Intent intent = new Intent(context, ConversationListActivity.class);
|
||||
Intent intent = new Intent(context, RoutingActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
|
||||
if (recipients.getPrimaryRecipient() != null) {
|
||||
|
@ -4,41 +4,64 @@ import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.PowerManager;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
import android.widget.RemoteViews;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.ConversationListActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.RoutingActivity;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.SmsMigrator;
|
||||
import org.thoughtcrime.securesms.database.SmsMigrator.ProgressDescription;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class ApplicationMigrationService extends Service
|
||||
implements SmsMigrator.SmsMigrationProgressListener
|
||||
{
|
||||
|
||||
public static final int PROGRESS_UPDATE = 1;
|
||||
public static final int PROGRESS_COMPLETE = 2;
|
||||
public static final String MIGRATE_DATABASE = "org.thoughtcrime.securesms.ApplicationMigration.MIGRATE_DATABSE";
|
||||
public static final String COMPLETED_ACTION = "org.thoughtcrime.securesms.ApplicationMigrationService.COMPLETED";
|
||||
private static final String PREFERENCES_NAME = "SecureSMS";
|
||||
private static final String DATABASE_MIGRATED = "migrated";
|
||||
|
||||
public static final String MIGRATE_DATABASE = "org.thoughtcrime.securesms.ApplicationMigration.MIGRATE_DATABSE";
|
||||
private final BroadcastReceiver completedReceiver = new CompletedReceiver();
|
||||
private final Binder binder = new ApplicationMigrationBinder();
|
||||
private final Executor executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
private final Binder binder = new ApplicationMigrationBinder();
|
||||
private boolean isMigrating = false;
|
||||
private Handler handler = null;
|
||||
private Notification notification = null;
|
||||
private Handler handler = null;
|
||||
private NotificationCompat.Builder notification = null;
|
||||
private ImportState state = new ImportState(ImportState.STATE_IDLE, null);
|
||||
|
||||
@Override
|
||||
public void onStart(Intent intent, int startId) {
|
||||
if (intent == null) return;
|
||||
public void onCreate() {
|
||||
registerCompletedReceiver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if (intent == null) return START_NOT_STICKY;
|
||||
|
||||
if (intent.getAction() != null && intent.getAction().equals(MIGRATE_DATABASE)) {
|
||||
handleDatabaseMigration((MasterSecret)intent.getParcelableExtra("master_secret"));
|
||||
executor.execute(new ImportRunnable(intent));
|
||||
}
|
||||
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
unregisterCompletedReceiver();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -46,70 +69,106 @@ public class ApplicationMigrationService extends Service
|
||||
return binder;
|
||||
}
|
||||
|
||||
private void handleDatabaseMigration(final MasterSecret masterSecret) {
|
||||
this.notification = initializeBackgroundNotification();
|
||||
|
||||
final PowerManager power = (PowerManager)getSystemService(Context.POWER_SERVICE);
|
||||
final WakeLock wakeLock = power.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Migration");
|
||||
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
wakeLock.acquire();
|
||||
|
||||
setMigrating(true);
|
||||
SmsMigrator.migrateDatabase(ApplicationMigrationService.this,
|
||||
masterSecret,
|
||||
ApplicationMigrationService.this);
|
||||
setMigrating(false);
|
||||
|
||||
if (handler != null) {
|
||||
handler.obtainMessage(PROGRESS_COMPLETE).sendToTarget();
|
||||
}
|
||||
|
||||
stopForeground(true);
|
||||
} finally {
|
||||
wakeLock.release();
|
||||
stopService(new Intent(ApplicationMigrationService.this,
|
||||
ApplicationMigrationService.class));
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
public void setImportStateHandler(Handler handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
private Notification initializeBackgroundNotification() {
|
||||
Intent intent = new Intent(this, ConversationListActivity.class);
|
||||
Notification notification = new Notification(R.drawable.icon,
|
||||
getString(R.string.ApplicationMigrationService_migrating),
|
||||
System.currentTimeMillis());
|
||||
private void registerCompletedReceiver() {
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(COMPLETED_ACTION);
|
||||
|
||||
notification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT;
|
||||
notification.contentView = new RemoteViews(getApplicationContext().getPackageName(),
|
||||
R.layout.migration_notification_progress);
|
||||
registerReceiver(completedReceiver, filter);
|
||||
}
|
||||
|
||||
notification.contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);
|
||||
notification.contentView.setImageViewResource(R.id.status_icon, R.drawable.icon);
|
||||
notification.contentView.setTextViewText(R.id.status_text,
|
||||
getString(R.string.ApplicationMigrationService_migrating_system_text_messages));
|
||||
notification.contentView.setProgressBar(R.id.status_progress, 10000, 0, false);
|
||||
private void unregisterCompletedReceiver() {
|
||||
unregisterReceiver(completedReceiver);
|
||||
}
|
||||
|
||||
private void notifyImportComplete() {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(COMPLETED_ACTION);
|
||||
|
||||
sendOrderedBroadcast(intent, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void progressUpdate(ProgressDescription progress) {
|
||||
setState(new ImportState(ImportState.STATE_MIGRATING_IN_PROGRESS, progress));
|
||||
}
|
||||
|
||||
public ImportState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
private void setState(ImportState state) {
|
||||
this.state = state;
|
||||
|
||||
if (handler != null) {
|
||||
handler.obtainMessage(state.state, state.progress).sendToTarget();
|
||||
}
|
||||
|
||||
if (state.progress != null && state.progress.secondaryComplete == 0) {
|
||||
updateBackgroundNotification(state.progress.primaryTotal, state.progress.primaryComplete);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBackgroundNotification(int total, int complete) {
|
||||
notification.setProgress(total, complete, false);
|
||||
|
||||
((NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE))
|
||||
.notify(4242, notification.build());
|
||||
}
|
||||
|
||||
private NotificationCompat.Builder initializeBackgroundNotification() {
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
|
||||
|
||||
builder.setSmallIcon(R.drawable.icon_notification);
|
||||
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.icon_notification));
|
||||
builder.setContentTitle(getString(R.string.ApplicationMigrationService_importing_text_messages));
|
||||
builder.setContentText(getString(R.string.ApplicationMigrationService_import_in_progress));
|
||||
builder.setOngoing(true);
|
||||
builder.setProgress(100, 0, false);
|
||||
builder.setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, RoutingActivity.class), 0));
|
||||
|
||||
stopForeground(true);
|
||||
startForeground(4242, notification);
|
||||
startForeground(4242, builder.build());
|
||||
|
||||
return notification;
|
||||
return builder;
|
||||
}
|
||||
|
||||
private synchronized void setMigrating(boolean isMigrating) {
|
||||
this.isMigrating = isMigrating;
|
||||
}
|
||||
private class ImportRunnable implements Runnable {
|
||||
private final MasterSecret masterSecret;
|
||||
|
||||
public synchronized boolean isMigrating() {
|
||||
return isMigrating;
|
||||
}
|
||||
public ImportRunnable(Intent intent) {
|
||||
this.masterSecret = intent.getParcelableExtra("master_secret");
|
||||
Log.w("ApplicationMigrationService", "Service got mastersecret: " + masterSecret);
|
||||
}
|
||||
|
||||
public void setHandler(Handler handler) {
|
||||
this.handler = handler;
|
||||
@Override
|
||||
public void run() {
|
||||
notification = initializeBackgroundNotification();
|
||||
PowerManager powerManager = (PowerManager)getSystemService(Context.POWER_SERVICE);
|
||||
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Migration");
|
||||
|
||||
try {
|
||||
wakeLock.acquire();
|
||||
|
||||
setState(new ImportState(ImportState.STATE_MIGRATING_BEGIN, null));
|
||||
|
||||
SmsMigrator.migrateDatabase(ApplicationMigrationService.this,
|
||||
masterSecret,
|
||||
ApplicationMigrationService.this);
|
||||
|
||||
setState(new ImportState(ImportState.STATE_MIGRATING_COMPLETE, null));
|
||||
|
||||
setDatabaseImported(ApplicationMigrationService.this);
|
||||
stopForeground(true);
|
||||
notifyImportComplete();
|
||||
stopSelf();
|
||||
} finally {
|
||||
wakeLock.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ApplicationMigrationBinder extends Binder {
|
||||
@ -118,20 +177,44 @@ public class ApplicationMigrationService extends Service
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void progressUpdate(int primaryProgress, int secondaryProgress) {
|
||||
if (handler != null) {
|
||||
handler.obtainMessage(PROGRESS_UPDATE, primaryProgress, secondaryProgress).sendToTarget();
|
||||
}
|
||||
private class CompletedReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
|
||||
builder.setSmallIcon(R.drawable.icon_notification);
|
||||
builder.setContentTitle("Import Complete");
|
||||
builder.setContentText("TextSecure system database import is complete.");
|
||||
builder.setContentIntent(PendingIntent.getActivity(context, 0, new Intent(context, RoutingActivity.class), 0));
|
||||
builder.setWhen(System.currentTimeMillis());
|
||||
builder.setDefaults(Notification.DEFAULT_VIBRATE);
|
||||
builder.setAutoCancel(true);
|
||||
|
||||
if (notification != null && secondaryProgress == 0) {
|
||||
notification.contentView.setProgressBar(R.id.status_progress, 10000, primaryProgress, false);
|
||||
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager)getApplicationContext()
|
||||
.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
notificationManager.notify(4242, notification);
|
||||
Notification notification = builder.build();
|
||||
((NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE)).notify(31337, notification);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ImportState {
|
||||
public static final int STATE_IDLE = 0;
|
||||
public static final int STATE_MIGRATING_BEGIN = 1;
|
||||
public static final int STATE_MIGRATING_IN_PROGRESS = 2;
|
||||
public static final int STATE_MIGRATING_COMPLETE = 3;
|
||||
|
||||
public int state;
|
||||
public ProgressDescription progress;
|
||||
|
||||
public ImportState(int state, ProgressDescription progress) {
|
||||
this.state = state;
|
||||
this.progress = progress;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isDatabaseImported(Context context) {
|
||||
return context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE)
|
||||
.getBoolean(DATABASE_MIGRATED, false);
|
||||
}
|
||||
|
||||
public static void setDatabaseImported(Context context) {
|
||||
context.getSharedPreferences(PREFERENCES_NAME, 0).edit().putBoolean(DATABASE_MIGRATED, true).commit();
|
||||
}
|
||||
}
|
||||
|
@ -32,8 +32,9 @@ import android.util.Log;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
|
||||
import org.thoughtcrime.securesms.ConversationListActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.RoutingActivity;
|
||||
import org.thoughtcrime.securesms.crypto.DecryptingQueue;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
|
||||
@ -54,8 +55,6 @@ public class KeyCachingService extends Service {
|
||||
public static final String CLEAR_KEY_ACTION = "org.thoughtcrime.securesms.service.action.CLEAR_KEY";
|
||||
public static final String ACTIVITY_START_EVENT = "org.thoughtcrime.securesms.service.action.ACTIVITY_START_EVENT";
|
||||
public static final String ACTIVITY_STOP_EVENT = "org.thoughtcrime.securesms.service.action.ACTIVITY_STOP_EVENT";
|
||||
public static final String PREFERENCES_NAME = "SecureSMS-Preferences";
|
||||
|
||||
|
||||
private PendingIntent pending;
|
||||
private int activitiesRunning = 0;
|
||||
@ -75,6 +74,7 @@ public class KeyCachingService extends Service {
|
||||
foregroundService();
|
||||
broadcastNewSecret();
|
||||
startTimeoutIfAppropriate();
|
||||
DecryptingQueue.schedulePendingDecrypts(this, masterSecret);
|
||||
|
||||
new Thread() {
|
||||
@Override
|
||||
@ -178,7 +178,9 @@ public class KeyCachingService extends Service {
|
||||
Notification notification = new Notification(R.drawable.icon_cached,
|
||||
getString(R.string.KeyCachingService_textsecure_passphrase_cached),
|
||||
System.currentTimeMillis());
|
||||
Intent intent = new Intent(this, ConversationListActivity.class);
|
||||
Intent intent = new Intent(this, RoutingActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
|
||||
PendingIntent launchIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);
|
||||
notification.setLatestEventInfo(getApplicationContext(),
|
||||
getString(R.string.KeyCachingService_passphrase_cached),
|
||||
|