mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-03 15:05:24 +00:00
Merge pull request #1205 from mcginty/contact-select
refactor and improve contact selection
This commit is contained in:
commit
54d2184c72
@ -135,8 +135,8 @@
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".SingleContactSelectionActivity"
|
||||
android:label="@string/AndroidManifest__select_contact"
|
||||
<activity android:name=".NewConversationActivity"
|
||||
android:label="@string/AndroidManifest__select_contacts"
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
|
@ -23,6 +23,7 @@ dependencies {
|
||||
compile 'com.actionbarsherlock:actionbarsherlock:4.4.0@aar'
|
||||
compile 'com.android.support:support-v4:19.0.1'
|
||||
compile 'com.google.android.gcm:gcm-client:1.0.2'
|
||||
compile 'se.emilsjolander:stickylistheaders:2.2.0'
|
||||
|
||||
compile project(':library')
|
||||
}
|
||||
|
17
res/anim/fade_scale_in.xml
Normal file
17
res/anim/fade_scale_in.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator">
|
||||
<scale
|
||||
android:duration="150"
|
||||
android:fromXScale="0.85"
|
||||
android:fromYScale="0.85"
|
||||
android:toXScale="1.0"
|
||||
android:toYScale="1.0"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%" />
|
||||
<alpha
|
||||
android:duration="150"
|
||||
android:fromAlpha="0.6"
|
||||
android:toAlpha="1.0" />
|
||||
</set>
|
17
res/anim/fade_scale_out.xml
Normal file
17
res/anim/fade_scale_out.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator">
|
||||
<scale
|
||||
android:duration="150"
|
||||
android:fromXScale="1.0"
|
||||
android:fromYScale="1.0"
|
||||
android:toXScale="0.85"
|
||||
android:toYScale="0.85"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%" />
|
||||
<alpha
|
||||
android:duration="150"
|
||||
android:fromAlpha="1.0"
|
||||
android:toAlpha="0.6" />
|
||||
</set>
|
9
res/anim/slide_from_right.xml
Normal file
9
res/anim/slide_from_right.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator">
|
||||
<translate
|
||||
android:duration="150"
|
||||
android:fromXDelta="100%"
|
||||
android:toXDelta="0%" />
|
||||
</set>
|
9
res/anim/slide_to_right.xml
Normal file
9
res/anim/slide_to_right.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator">
|
||||
<translate
|
||||
android:duration="150"
|
||||
android:fromXDelta="0%"
|
||||
android:toXDelta="100%" />
|
||||
</set>
|
16
res/drawable/textlines.xml
Normal file
16
res/drawable/textlines.xml
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#33000000" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:bottom="1dp">
|
||||
<shape
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/white" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</layer-list>
|
16
res/drawable/textlines_dark.xml
Normal file
16
res/drawable/textlines_dark.xml
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#33ffffff" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:bottom="1dp">
|
||||
<shape
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/black" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</layer-list>
|
@ -7,7 +7,7 @@
|
||||
|
||||
<ListView android:id="@android:id/list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:drawSelectorOnTop="false"
|
||||
android:scrollbarStyle="insideOverlay"
|
||||
android:fadingEdgeLength="16dip"
|
||||
|
@ -5,12 +5,16 @@
|
||||
android:orientation="vertical"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<org.thoughtcrime.securesms.components.SingleRecipientPanel android:id="@+id/recipients"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<fragment
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/contact_selection_list_fragment"
|
||||
android:name="org.thoughtcrime.securesms.SingleContactSelectionListFragment">
|
||||
android:name="org.thoughtcrime.securesms.PushContactSelectionListFragment">
|
||||
</fragment>
|
||||
|
||||
</LinearLayout>
|
@ -8,7 +8,7 @@
|
||||
<fragment
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/contact_selection_list_fragment"
|
||||
android:name="org.thoughtcrime.securesms.PushContactSelectionListFragment">
|
||||
</fragment>
|
||||
|
@ -1,13 +1,12 @@
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ListView android:id="@android:id/list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:fastScrollEnabled="true" />
|
||||
<se.emilsjolander.stickylistheaders.StickyListHeadersListView android:id="@android:id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<TextView android:id="@android:id/empty"
|
||||
android:layout_width="match_parent"
|
||||
|
25
res/layout/push_contact_selection_list_header.xml
Normal file
25
res/layout/push_contact_selection_list_header.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="32sp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="25dp">
|
||||
|
||||
<TextView android:id="@+id/text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:textSize="15sp"
|
||||
android:textColor="?conversation_sent_text_secondary_color"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<View android:layout_width="match_parent"
|
||||
android:layout_height="3dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="?conversation_received_text_secondary_color" />
|
||||
</RelativeLayout>
|
@ -1,65 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:attr/listPreferredItemHeight"
|
||||
android:paddingRight="25dip">
|
||||
|
||||
<View android:id="@+id/push_support_label"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_width="3dip"
|
||||
<ImageView android:id="@+id/contact_photo_image"
|
||||
android:layout_width="@dimen/contact_selection_photo_size"
|
||||
android:layout_height="@dimen/contact_selection_photo_size"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:background="#ff64a926"
|
||||
android:visibility="visible"
|
||||
/>
|
||||
|
||||
<TextView android:id="@+id/label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginBottom="8dip"
|
||||
android:layout_marginTop="-8dip"
|
||||
android:layout_marginLeft="14dip"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textStyle="bold"
|
||||
android:visibility = "gone"
|
||||
/>
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:cropToPadding="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:contentDescription="@string/SingleContactSelectionActivity_contact_photo" />
|
||||
|
||||
<TextView android:id="@+id/number"
|
||||
android:visibility = "visible"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dip"
|
||||
android:layout_marginTop="-8dip"
|
||||
android:layout_marginLeft="14dip"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_toRightOf="@id/contact_photo_image"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:fontFamily="sans-serif-light"
|
||||
/>
|
||||
android:fontFamily="sans-serif-light" />
|
||||
|
||||
<TextView android:id="@+id/name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@id/number"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginBottom="1dip"
|
||||
android:layout_marginLeft="14dip"
|
||||
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_toRightOf="@id/contact_photo_image"
|
||||
android:gravity="center_vertical|left"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
/>
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/check_box"
|
||||
<CheckBox android:id="@+id/check_box"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
|
@ -1,26 +0,0 @@
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.thoughtcrime.securesms.components.SingleRecipientPanel android:id="@+id/recipients"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp" />
|
||||
|
||||
<ListView android:id="@android:id/list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:fastScrollEnabled="true" />
|
||||
|
||||
<TextView android:id="@android:id/empty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center|center_vertical"
|
||||
android:layout_marginTop="15dp"
|
||||
android:text="@string/contact_selection_group_activity__finding_contacts"
|
||||
android:textSize="20sp" />
|
||||
|
||||
</LinearLayout>
|
@ -1,61 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="?android:attr/listPreferredItemHeight"
|
||||
android:paddingRight="25dip">
|
||||
|
||||
<View android:id="@+id/push_support_label"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_width="3dip"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:background="#ff64a926"
|
||||
android:visibility="visible"
|
||||
/>
|
||||
|
||||
<TextView android:id="@+id/label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginBottom="8dip"
|
||||
android:layout_marginTop="-8dip"
|
||||
android:layout_marginLeft="14dip"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textStyle="bold"
|
||||
android:visibility = "gone"
|
||||
/>
|
||||
|
||||
<TextView android:id="@+id/number"
|
||||
android:visibility = "visible"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dip"
|
||||
android:layout_marginTop="-8dip"
|
||||
android:layout_marginLeft="14dip"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:fontFamily="sans-serif-light"
|
||||
/>
|
||||
|
||||
<TextView android:id="@+id/name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@id/number"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginBottom="1dip"
|
||||
android:layout_marginLeft="14dip"
|
||||
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:gravity="center_vertical|left"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
/>
|
||||
|
||||
</RelativeLayout>
|
@ -13,7 +13,11 @@
|
||||
android:singleLine="true"
|
||||
android:hint="@string/recipients_panel__to"
|
||||
android:paddingRight="45dip"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:textColor="?conversation_editor_text_color"
|
||||
android:background="?conversation_editor_background"
|
||||
android:layout_width="fill_parent"/>
|
||||
|
||||
</RelativeLayout>
|
@ -21,7 +21,7 @@
|
||||
<attr name="lower_right_divet" format="reference" />
|
||||
|
||||
<attr name="conversation_background" format="reference|color"/>
|
||||
<attr name="conversation_editor_background" format="reference|color"/>
|
||||
<attr name="conversation_editor_background" format="reference"/>
|
||||
<attr name="conversation_editor_text_color" format="reference|color"/>
|
||||
<attr name="conversation_send_button" format="reference"/>
|
||||
<attr name="conversation_send_secure_button" format="reference"/>
|
||||
@ -45,8 +45,7 @@
|
||||
|
||||
<attr name="contact_selection_push_user" format="reference|color" />
|
||||
<attr name="contact_selection_lay_user" format="reference|color" />
|
||||
<attr name="contact_selection_push_label" format="reference|color" />
|
||||
<attr name="contact_selection_lay_label" format="reference|color" />
|
||||
<attr name="contact_selection_label_text" format="reference|color" />
|
||||
|
||||
<attr name="navigation_drawer_background" format="reference|color"/>
|
||||
<attr name="navigation_drawer_text_color" format="color"/>
|
||||
|
@ -3,4 +3,5 @@
|
||||
<dimen name="emoji_drawer_size">40dip</dimen>
|
||||
<dimen name="conversation_item_corner_radius">3dp</dimen>
|
||||
<dimen name="conversation_item_drop_shadow_dist">2dp</dimen>
|
||||
<dimen name="contact_selection_photo_size">50dp</dimen>
|
||||
</resources>
|
5
res/values/ids.xml
Normal file
5
res/values/ids.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<item type="id" name="holder_tag"/>
|
||||
<item type="id" name="contact_info_tag"/>
|
||||
</resources>
|
@ -417,13 +417,14 @@
|
||||
<!-- contact_selection_group_activity -->
|
||||
<!-- contact_selection_list_activity -->
|
||||
<string name="contact_selection_group_activity__no_contacts">No contacts.</string>
|
||||
<string name="contact_selection_group_activity__finding_contacts">Finding contacts…</string>
|
||||
<string name="contact_selection_group_activity__finding_contacts">Loading contacts…</string>
|
||||
|
||||
<!-- single_contact_selection_activity -->
|
||||
<string name="single_contact_selection_group_activity__filter">Type a name to filter…</string>
|
||||
<string name="SingleContactSelectionActivity_you_are_not_registered_with_the_push_service">You are not registered with the push service...</string>
|
||||
<string name="SingleContactSelectionActivity_you_are_not_registered_with_the_push_service">You are not registered with the push service…</string>
|
||||
<string name="SingleContactSelectionActivity_updating_directory">Updating directory</string>
|
||||
<string name="SingleContactSelectionActivity_updating_push_directory">Updating push directory...</string>
|
||||
<string name="SingleContactSelectionActivity_updating_push_directory">Updating push directory…</string>
|
||||
<string name="SingleContactSelectionActivity_contact_photo">Contact Photo</string>
|
||||
|
||||
<!-- ContactSelectionListFragment-->
|
||||
<string name="ContactSelectionlistFragment_select_for">Select for</string>
|
||||
@ -472,6 +473,8 @@
|
||||
<string name="log_submit_activity__copied_to_clipboard">Copied to clipboard</string>
|
||||
<string name="log_submit_activity__loading_logcat">Loading logcat…</string>
|
||||
<string name="log_submit_activity__thanks">Thanks for your help!</string>
|
||||
<string name="log_submit_activity__submitting">Submitting</string>
|
||||
<string name="log_submit_activity__posting_logs">Posting logs to pastebin…</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>
|
||||
@ -606,7 +609,7 @@
|
||||
<string name="registration_progress_activity__generating_keys">Generating keys...</string>
|
||||
|
||||
<!-- recipients_panel -->
|
||||
<string name="recipients_panel__to">To</string>
|
||||
<string name="recipients_panel__to"><small>Enter a name or number</small></string>
|
||||
<string name="recipients_panel__add_member">Add member</string>
|
||||
|
||||
<!-- review_identities -->
|
||||
@ -752,6 +755,8 @@
|
||||
<!-- contact_selection_list -->
|
||||
<string name="contact_selection_list__menu_select_all">Select All</string>
|
||||
<string name="contact_selection_list__menu_unselect_all">Unselect All</string>
|
||||
<string name="contact_selection_list__header_textsecure_users">TEXTSECURE USERS</string>
|
||||
<string name="contact_selection_list__header_other">ALL CONTACTS</string>
|
||||
|
||||
<!-- contact_selection -->
|
||||
<string name="contact_selection__menu_finished">Finished</string>
|
||||
|
@ -23,6 +23,22 @@
|
||||
<item name="background">#ff111111</item>
|
||||
</style>
|
||||
|
||||
<style name="TextSecure.TitleTextStyle" parent="TextAppearance.Sherlock.Widget.ActionBar.Title">
|
||||
<item name="android:textColor">#ff555555</item>
|
||||
<item name="android:textSize">19sp</item>
|
||||
</style>
|
||||
|
||||
<style name="TextSecure.SubtitleTextStyle" parent="TextAppearance.Sherlock.Widget.ActionBar.Subtitle">
|
||||
<item name="android:textColor">#ff555555</item>
|
||||
</style>
|
||||
|
||||
<style name="TextSecure.LightActionBar" parent="Widget.Sherlock.Light.ActionBar.Solid">
|
||||
<item name="android:titleTextStyle">@style/TextSecure.TitleTextStyle</item>
|
||||
<item name="titleTextStyle">@style/TextSecure.TitleTextStyle</item>
|
||||
<item name="android:subtitleTextStyle">@style/TextSecure.SubtitleTextStyle</item>
|
||||
<item name="subtitleTextStyle">@style/TextSecure.SubtitleTextStyle</item>
|
||||
</style>
|
||||
|
||||
<style name="transparent_progress">
|
||||
<item name="android:windowFrame">@null</item>
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
|
@ -1,6 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<style name="TextSecure.LightTheme" parent="@style/Theme.Sherlock.Light">
|
||||
<item name="android:actionBarStyle">@style/TextSecure.LightActionBar</item>
|
||||
<item name="actionBarStyle">@style/TextSecure.LightActionBar</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
<item name="windowContentOverlay">@null</item>
|
||||
<item name="conversation_list_item_background_read">@drawable/conversation_list_item_background_read_light</item>
|
||||
<item name="conversation_list_item_background_unread">@drawable/conversation_list_item_background_unread_light</item>
|
||||
<item name="conversation_list_item_background_selected">@drawable/list_selected_holo_light</item>
|
||||
@ -21,11 +25,10 @@
|
||||
|
||||
<item name="contact_selection_push_user">#ff000000</item>
|
||||
<item name="contact_selection_lay_user">#a0000000</item>
|
||||
<item name="contact_selection_push_label">#ff64a926</item>
|
||||
<item name="contact_selection_lay_label">#99000000</item>
|
||||
<item name="contact_selection_label_text">#66000000</item>
|
||||
|
||||
<item name="conversation_background">#ffdddddd</item>
|
||||
<item name="conversation_editor_background">#eeeeee</item>
|
||||
<item name="conversation_editor_background">@drawable/textlines</item>
|
||||
<item name="conversation_editor_text_color">#ff111111</item>
|
||||
<item name="conversation_send_button">@drawable/ic_send_holo_light</item>
|
||||
<item name="conversation_send_secure_button">@drawable/ic_send_encrypted_holo_light</item>
|
||||
@ -89,10 +92,9 @@
|
||||
<item name="conversation_received_text_primary_color">#ffeeeeee</item>
|
||||
<item name="conversation_received_text_secondary_color">#44eeeeee</item>
|
||||
|
||||
<item name="contact_selection_push_user">#ffdddddd</item>
|
||||
<item name="contact_selection_lay_user">#ffcccccc</item>
|
||||
<item name="contact_selection_push_label">#ff64a926</item>
|
||||
<item name="contact_selection_lay_label">#11ffffff</item>
|
||||
<item name="contact_selection_push_user">#ffeeeeee</item>
|
||||
<item name="contact_selection_lay_user">#afeeeeee</item>
|
||||
<item name="contact_selection_label_text">#66eeeeee</item>
|
||||
|
||||
<item name="conversation_item_received_background">@drawable/conversation_item_received_shape_dark</item>
|
||||
<item name="conversation_item_received_triangle_background">@drawable/conversation_item_received_triangle_shape_dark</item>
|
||||
@ -112,7 +114,7 @@
|
||||
<item name="lower_right_divet">@drawable/divet_lower_right_light</item>
|
||||
|
||||
<item name="conversation_background">@color/black</item>
|
||||
<item name="conversation_editor_background">#ff222222</item>
|
||||
<item name="conversation_editor_background">@drawable/textlines_dark</item>
|
||||
<item name="conversation_editor_text_color">#ffeeeeee</item>
|
||||
<item name="conversation_send_button">@drawable/ic_send_holo_dark</item>
|
||||
<item name="conversation_send_secure_button">@drawable/ic_send_holo_dark_encrypted</item>
|
||||
|
@ -544,39 +544,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPr
|
||||
private class DirectoryUpdateListener implements Preference.OnPreferenceClickListener {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
final Context context = ApplicationPreferencesActivity.this;
|
||||
|
||||
if (!TextSecurePreferences.isPushRegistered(context)) {
|
||||
Toast.makeText(context,
|
||||
getString(R.string.ApplicationPreferencesActivity_you_are_not_registered_with_the_push_service),
|
||||
Toast.LENGTH_LONG).show();
|
||||
return true;
|
||||
}
|
||||
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
private ProgressDialog progress;
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
progress = ProgressDialog.show(context,
|
||||
getString(R.string.ApplicationPreferencesActivity_updating_directory),
|
||||
getString(R.string.ApplicationPreferencesActivity_updating_push_directory),
|
||||
true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
DirectoryHelper.refreshDirectory(context);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
if (progress != null)
|
||||
progress.dismiss();
|
||||
}
|
||||
}.execute();
|
||||
|
||||
DirectoryHelper.refreshDirectoryWithProgressDialog(ApplicationPreferencesActivity.this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -176,6 +176,7 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle state) {
|
||||
overridePendingTransition(R.anim.slide_from_right, R.anim.fade_scale_out);
|
||||
dynamicTheme.onCreate(this);
|
||||
dynamicLanguage.onCreate(this);
|
||||
super.onCreate(state);
|
||||
@ -219,6 +220,7 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
MessageNotifier.setVisibleThread(-1L);
|
||||
overridePendingTransition(R.anim.fade_scale_in, R.anim.slide_to_right);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -170,8 +170,8 @@ public class ConversationListActivity extends PassphraseRequiredSherlockFragment
|
||||
}
|
||||
|
||||
private void openSingleContactSelection() {
|
||||
Intent intent = new Intent(this, SingleContactSelectionActivity.class);
|
||||
intent.putExtra(SingleContactSelectionActivity.MASTER_SECRET_EXTRA, masterSecret);
|
||||
Intent intent = new Intent(this, NewConversationActivity.class);
|
||||
intent.putExtra(NewConversationActivity.MASTER_SECRET_EXTRA, masterSecret);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@ import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.thoughtcrime.securesms.util.ActionBarUtil;
|
||||
import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
@ -159,11 +160,11 @@ public class LogSubmitActivity extends SherlockActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private class SubmitToPastebinAsyncTask extends AsyncTask<Void,Void,String> {
|
||||
private ProgressDialog progressDialog;
|
||||
private class SubmitToPastebinAsyncTask extends ProgressDialogAsyncTask<Void,Void,String> {
|
||||
private final String paste;
|
||||
|
||||
public SubmitToPastebinAsyncTask(String paste) {
|
||||
super(LogSubmitActivity.this, R.string.log_submit_activity__submitting, R.string.log_submit_activity__posting_logs);
|
||||
this.paste = paste;
|
||||
}
|
||||
|
||||
@ -174,7 +175,6 @@ public class LogSubmitActivity extends SherlockActivity {
|
||||
URL url = new URL(HASTEBIN_ENDPOINT);
|
||||
urlConnection = (HttpURLConnection) url.openConnection();
|
||||
urlConnection.setDoOutput(true);
|
||||
urlConnection.setFixedLengthStreamingMode(paste.length());
|
||||
urlConnection.setReadTimeout(10000);
|
||||
urlConnection.connect();
|
||||
|
||||
@ -201,22 +201,9 @@ public class LogSubmitActivity extends SherlockActivity {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
progressDialog = new ProgressDialog(LogSubmitActivity.this);
|
||||
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
|
||||
progressDialog.setCancelable(false);
|
||||
progressDialog.setIndeterminate(true);
|
||||
progressDialog.setTitle("Submitting");
|
||||
progressDialog.setMessage("Posting logs to pastebin...");
|
||||
progressDialog.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(final String response) {
|
||||
super.onPostExecute(response);
|
||||
progressDialog.dismiss();
|
||||
|
||||
if (response != null && !response.startsWith("Bad API request")) {
|
||||
TextView showText = new TextView(LogSubmitActivity.this);
|
||||
|
@ -16,12 +16,8 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.preference.Preference;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
@ -43,28 +39,32 @@ import org.thoughtcrime.securesms.util.ActionBarUtil;
|
||||
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.thoughtcrime.securesms.util.NumberUtil;
|
||||
import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
||||
|
||||
/**
|
||||
* Activity container for selecting a list of contacts. Provides a tab frame for
|
||||
* contact, group, and "recent contact" activity tabs. Used by ComposeMessageActivity
|
||||
* when selecting a list of contacts to address a message to.
|
||||
* Activity container for selecting a list of contacts.
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*
|
||||
*/
|
||||
public class SingleContactSelectionActivity extends PassphraseRequiredSherlockFragmentActivity {
|
||||
private final static String TAG = "SingleContactSelectionActivity";
|
||||
public class NewConversationActivity extends PassphraseRequiredSherlockFragmentActivity {
|
||||
private final static String TAG = "ContactSelectActivity";
|
||||
public final static String MASTER_SECRET_EXTRA = "master_secret";
|
||||
|
||||
private final DynamicTheme dynamicTheme = new DynamicTheme();
|
||||
private MasterSecret masterSecret;
|
||||
|
||||
private SingleRecipientPanel recipientsPanel;
|
||||
private PushContactSelectionListFragment contactsFragment;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle icicle) {
|
||||
dynamicTheme.onCreate(this);
|
||||
@ -74,16 +74,41 @@ public class SingleContactSelectionActivity extends PassphraseRequiredSherlockFr
|
||||
ActionBarUtil.initializeDefaultActionBar(this, actionBar);
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
setContentView(R.layout.single_contact_selection_activity);
|
||||
setContentView(R.layout.new_conversation_activity);
|
||||
initializeResources();
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
dynamicTheme.onResume(this);
|
||||
masterSecret = getIntent().getParcelableExtra(MASTER_SECRET_EXTRA);
|
||||
final SingleRecipientPanel recipientsPanel = (SingleRecipientPanel) findViewById(R.id.recipients);
|
||||
}
|
||||
|
||||
final SingleContactSelectionListFragment listFragment = (SingleContactSelectionListFragment)getSupportFragmentManager().findFragmentById(R.id.contact_selection_list_fragment);
|
||||
listFragment.setOnContactSelectedListener(new SingleContactSelectionListFragment.OnContactSelectedListener() {
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = this.getSupportMenuInflater();
|
||||
menu.clear();
|
||||
|
||||
if (TextSecurePreferences.isPushRegistered(this)) inflater.inflate(R.menu.push_directory, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
super.onOptionsItemSelected(item);
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_refresh_directory: handleDirectoryRefresh(); return true;
|
||||
case R.id.menu_selection_finished: handleSelectionFinished(); return true;
|
||||
case android.R.id.home: finish(); return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
recipientsPanel = (SingleRecipientPanel) findViewById(R.id.recipients);
|
||||
contactsFragment = (PushContactSelectionListFragment) getSupportFragmentManager().findFragmentById(R.id.contact_selection_list_fragment);
|
||||
contactsFragment.setOnContactSelectedListener(new PushContactSelectionListFragment.OnContactSelectedListener() {
|
||||
@Override
|
||||
public void onContactSelected(ContactData contactData) {
|
||||
Log.i(TAG, "Choosing contact from list.");
|
||||
@ -99,15 +124,35 @@ public class SingleContactSelectionActivity extends PassphraseRequiredSherlockFr
|
||||
openNewConversation(recipients);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void handleSelectionFinished() {
|
||||
final Intent resultIntent = getIntent();
|
||||
final List<ContactData> selectedContacts = contactsFragment.getSelectedContacts();
|
||||
if (selectedContacts != null) {
|
||||
resultIntent.putParcelableArrayListExtra("contacts", new ArrayList<ContactData>(contactsFragment.getSelectedContacts()));
|
||||
}
|
||||
setResult(RESULT_OK, resultIntent);
|
||||
finish();
|
||||
}
|
||||
|
||||
private void handleDirectoryRefresh() {
|
||||
DirectoryHelper.refreshDirectoryWithProgressDialog(this, new DirectoryHelper.DirectoryUpdateFinishedListener() {
|
||||
@Override
|
||||
public void onUpdateFinished() {
|
||||
contactsFragment.update();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Recipients contactDataToRecipients(ContactData contactData) {
|
||||
if (contactData == null || contactData.numbers == null) return null;
|
||||
Recipients recipients = new Recipients(new ArrayList<Recipient>());
|
||||
Recipients recipients = new Recipients(new LinkedList<Recipient>());
|
||||
for (ContactAccessor.NumberData numberData : contactData.numbers) {
|
||||
if (NumberUtil.isValidSmsOrEmailOrGroup(numberData.number)) {
|
||||
try {
|
||||
Recipients recipientsForNumber = RecipientFactory.getRecipientsFromString(SingleContactSelectionActivity.this,
|
||||
Recipients recipientsForNumber = RecipientFactory.getRecipientsFromString(NewConversationActivity.this,
|
||||
numberData.number,
|
||||
false);
|
||||
recipients.getRecipientsList().addAll(recipientsForNumber.getRecipientsList());
|
||||
@ -121,83 +166,14 @@ public class SingleContactSelectionActivity extends PassphraseRequiredSherlockFr
|
||||
|
||||
private void openNewConversation(Recipients recipients) {
|
||||
if (recipients != null) {
|
||||
Intent intent = new Intent(SingleContactSelectionActivity.this, ConversationActivity.class);
|
||||
Intent intent = new Intent(this, ConversationActivity.class);
|
||||
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.toIdString());
|
||||
intent.putExtra(ConversationActivity.MASTER_SECRET_EXTRA, masterSecret);
|
||||
long existingThread = DatabaseFactory.getThreadDatabase(SingleContactSelectionActivity.this).getThreadIdIfExistsFor(recipients);
|
||||
long existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipients);
|
||||
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, existingThread);
|
||||
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = this.getSupportMenuInflater();
|
||||
menu.clear();
|
||||
|
||||
if (TextSecurePreferences.isPushRegistered(this)) {
|
||||
inflater.inflate(R.menu.push_directory, menu);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
dynamicTheme.onResume(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_refresh_directory:
|
||||
handleDirectoryRefresh();
|
||||
return true;
|
||||
case android.R.id.home:
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void handleDirectoryRefresh() {
|
||||
if (!TextSecurePreferences.isPushRegistered(SingleContactSelectionActivity.this)) {
|
||||
Toast.makeText(getApplicationContext(),
|
||||
getString(R.string.SingleContactSelectionActivity_you_are_not_registered_with_the_push_service),
|
||||
Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
private ProgressDialog progress;
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
progress = ProgressDialog.show(SingleContactSelectionActivity.this,
|
||||
getString(R.string.SingleContactSelectionActivity_updating_directory),
|
||||
getString(R.string.SingleContactSelectionActivity_updating_push_directory),
|
||||
true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
DirectoryHelper.refreshDirectory(getApplicationContext());
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
final SingleContactSelectionListFragment listFragment = (SingleContactSelectionListFragment)getSupportFragmentManager().findFragmentById(R.id.contact_selection_list_fragment);
|
||||
listFragment.update();
|
||||
if (progress != null)
|
||||
progress.dismiss();
|
||||
}
|
||||
}.execute();
|
||||
|
||||
}
|
||||
}
|
@ -19,11 +19,24 @@ package org.thoughtcrime.securesms;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.thoughtcrime.securesms.components.SingleRecipientPanel;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.util.ActionBarUtil;
|
||||
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.thoughtcrime.securesms.util.NumberUtil;
|
||||
import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||
|
||||
import com.actionbarsherlock.app.ActionBar;
|
||||
import com.actionbarsherlock.view.Menu;
|
||||
@ -31,22 +44,24 @@ import com.actionbarsherlock.view.MenuInflater;
|
||||
import com.actionbarsherlock.view.MenuItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
||||
|
||||
/**
|
||||
* Activity container for selecting a list of contacts. Provides a tab frame for
|
||||
* contact, group, and "recent contact" activity tabs. Used by ComposeMessageActivity
|
||||
* when selecting a list of contacts to address a message to.
|
||||
* Activity container for selecting a list of contacts.
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*
|
||||
*/
|
||||
public class PushContactSelectionActivity extends PassphraseRequiredSherlockFragmentActivity {
|
||||
private final static String TAG = "ContactSelectActivity";
|
||||
|
||||
private final DynamicTheme dynamicTheme = new DynamicTheme();
|
||||
|
||||
private PushContactSelectionListFragment contactsFragment;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle icicle) {
|
||||
dynamicTheme.onCreate(this);
|
||||
@ -57,6 +72,7 @@ public class PushContactSelectionActivity extends PassphraseRequiredSherlockFrag
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
setContentView(R.layout.push_contact_selection_activity);
|
||||
initializeResources();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -66,34 +82,55 @@ public class PushContactSelectionActivity extends PassphraseRequiredSherlockFrag
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = this.getSupportMenuInflater();
|
||||
inflater.inflate(R.menu.contact_selection, menu);
|
||||
menu.clear();
|
||||
|
||||
if (TextSecurePreferences.isPushRegistered(this)) inflater.inflate(R.menu.push_directory, menu);
|
||||
|
||||
inflater.inflate(R.menu.contact_selection, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
super.onOptionsItemSelected(item);
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_selection_finished:
|
||||
case android.R.id.home:
|
||||
handleSelectionFinished(); return true;
|
||||
case R.id.menu_refresh_directory: handleDirectoryRefresh(); return true;
|
||||
case R.id.menu_selection_finished: handleSelectionFinished(); return true;
|
||||
case android.R.id.home: finish(); return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
contactsFragment = (PushContactSelectionListFragment) getSupportFragmentManager().findFragmentById(R.id.contact_selection_list_fragment);
|
||||
contactsFragment.setMultiSelect(true);
|
||||
contactsFragment.setOnContactSelectedListener(new PushContactSelectionListFragment.OnContactSelectedListener() {
|
||||
@Override
|
||||
public void onContactSelected(ContactData contactData) {
|
||||
Log.i(TAG, "Choosing contact from list.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleSelectionFinished() {
|
||||
PushContactSelectionListFragment contactsFragment = (PushContactSelectionListFragment) getSupportFragmentManager().findFragmentById(R.id.contact_selection_list_fragment);
|
||||
List<ContactData> contacts = contactsFragment.getSelectedContacts();
|
||||
|
||||
Intent resultIntent = getIntent();
|
||||
resultIntent.putParcelableArrayListExtra("contacts", new ArrayList<ContactData>(contacts));
|
||||
|
||||
final Intent resultIntent = getIntent();
|
||||
final List<ContactData> selectedContacts = contactsFragment.getSelectedContacts();
|
||||
if (selectedContacts != null) {
|
||||
resultIntent.putParcelableArrayListExtra("contacts", new ArrayList<ContactData>(contactsFragment.getSelectedContacts()));
|
||||
}
|
||||
setResult(RESULT_OK, resultIntent);
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
private void handleDirectoryRefresh() {
|
||||
DirectoryHelper.refreshDirectoryWithProgressDialog(this, new DirectoryHelper.DirectoryUpdateFinishedListener() {
|
||||
@Override
|
||||
public void onUpdateFinished() {
|
||||
contactsFragment.update();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -17,345 +17,167 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.res.TypedArray;
|
||||
import android.database.Cursor;
|
||||
import android.database.MergeCursor;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.provider.ContactsContract;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v4.widget.CursorAdapter;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CursorAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.actionbarsherlock.app.SherlockListFragment;
|
||||
import com.actionbarsherlock.view.Menu;
|
||||
import com.actionbarsherlock.view.MenuInflater;
|
||||
import com.actionbarsherlock.view.MenuItem;
|
||||
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.NumberData;
|
||||
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter;
|
||||
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter.ViewHolder;
|
||||
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter.DataHolder;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
|
||||
|
||||
/**
|
||||
* Activity for selecting a list of contacts. Displayed inside
|
||||
* a PushContactSelectionActivity tab frame, and ultimately called by
|
||||
* ComposeMessageActivity for selecting a list of destination contacts.
|
||||
* Fragment for selecting a one or more contacts from a list.
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*
|
||||
*/
|
||||
|
||||
public class PushContactSelectionListFragment extends SherlockListFragment
|
||||
public class PushContactSelectionListFragment extends Fragment
|
||||
implements LoaderManager.LoaderCallbacks<Cursor>
|
||||
{
|
||||
private final int STYLE_ATTRIBUTES[] = new int[]{R.attr.contact_selection_push_user,
|
||||
R.attr.contact_selection_lay_user,
|
||||
R.attr.contact_selection_push_label,
|
||||
R.attr.contact_selection_lay_label};
|
||||
private static final String TAG = "ContactSelectFragment";
|
||||
|
||||
private final HashMap<Long, ContactData> selectedContacts = new HashMap<Long, ContactData>();
|
||||
private static LayoutInflater li;
|
||||
private TypedArray drawables;
|
||||
private TextView emptyText;
|
||||
|
||||
private Map<Long, ContactData> selectedContacts;
|
||||
private OnContactSelectedListener onContactSelectedListener;
|
||||
private boolean multi = false;
|
||||
private StickyListHeadersListView listView;
|
||||
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
li = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
initializeResources();
|
||||
initializeCursor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.push_contact_selection_list_activity, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.contact_selection_list, menu);
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_select_all:
|
||||
handleSelectAll();
|
||||
return true;
|
||||
case R.id.menu_unselect_all:
|
||||
handleUnselectAll();
|
||||
return true;
|
||||
}
|
||||
|
||||
super.onOptionsItemSelected(item);
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<ContactData> getSelectedContacts() {
|
||||
if (selectedContacts == null) return null;
|
||||
|
||||
List<ContactData> selected = new LinkedList<ContactData>();
|
||||
selected.addAll(selectedContacts.values());
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
private void handleUnselectAll() {
|
||||
selectedContacts.clear();
|
||||
((CursorAdapter) getListView().getAdapter()).notifyDataSetChanged();
|
||||
public void setMultiSelect(boolean multi) {
|
||||
this.multi = multi;
|
||||
}
|
||||
|
||||
private void handleSelectAll() {
|
||||
selectedContacts.clear();
|
||||
|
||||
Cursor cursor = null;
|
||||
|
||||
try {
|
||||
cursor = ContactAccessor.getInstance().getCursorForContactsWithNumbers(getActivity());
|
||||
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
ContactData contactData = ContactAccessor.getInstance().getContactData(getActivity(), cursor);
|
||||
|
||||
if (contactData.numbers.isEmpty()) continue;
|
||||
else if (contactData.numbers.size() == 1) addSingleNumberContact(contactData);
|
||||
else addMultipleNumberContact(contactData, null, null);
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
((CursorAdapter) getListView().getAdapter()).notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void addSingleNumberContact(ContactData contactData) {
|
||||
private void addContact(DataHolder data) {
|
||||
final ContactData contactData = new ContactData(data.id, data.name);
|
||||
final CharSequence label = ContactsContract.CommonDataKinds.Phone.getTypeLabel(getResources(),
|
||||
data.numberType, "");
|
||||
contactData.numbers.add(new ContactAccessor.NumberData(label.toString(), data.number));
|
||||
if (multi) {
|
||||
selectedContacts.put(contactData.id, contactData);
|
||||
}
|
||||
if (onContactSelectedListener != null) {
|
||||
onContactSelectedListener.onContactSelected(contactData);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeContact(ContactData contactData) {
|
||||
private void removeContact(DataHolder contactData) {
|
||||
selectedContacts.remove(contactData.id);
|
||||
}
|
||||
|
||||
private void addMultipleNumberContact(ContactData contactData, TextView textView, CheckBox checkBox) {
|
||||
String[] options = new String[contactData.numbers.size()];
|
||||
int i = 0;
|
||||
|
||||
for (NumberData option : contactData.numbers) {
|
||||
options[i++] = option.type + " " + option.number;
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setTitle(getString(R.string.ContactSelectionlistFragment_select_for) + " " + contactData.name);
|
||||
builder.setMultiChoiceItems(options, null, new DiscriminatorClickedListener(contactData));
|
||||
builder.setPositiveButton(android.R.string.ok, new DiscriminatorFinishedListener(contactData, textView, checkBox));
|
||||
builder.setOnCancelListener(new DiscriminatorFinishedListener(contactData, textView, checkBox));
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void initializeCursor() {
|
||||
setListAdapter(new ContactSelectionListAdapter(getActivity(), null));
|
||||
ContactSelectionListAdapter adapter = new ContactSelectionListAdapter(getActivity(), null, multi);
|
||||
selectedContacts = adapter.getSelectedContacts();
|
||||
listView.setAdapter(adapter);
|
||||
this.getLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
this.getListView().setFocusable(true);
|
||||
this.drawables = getActivity().obtainStyledAttributes(STYLE_ATTRIBUTES);
|
||||
emptyText = (TextView) getView().findViewById(android.R.id.empty);
|
||||
listView = (StickyListHeadersListView) getView().findViewById(android.R.id.list);
|
||||
listView.setFocusable(true);
|
||||
listView.setFastScrollEnabled(true);
|
||||
listView.setFastScrollAlwaysVisible(true);
|
||||
listView.setDrawingListUnderStickyHeader(false);
|
||||
listView.setOnItemClickListener(new ListClickListener());
|
||||
}
|
||||
|
||||
public void update() {
|
||||
this.getLoaderManager().restartLoader(0, null, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onListItemClick(ListView l, View v, int position, long id) {
|
||||
((ContactItemView)v).selected();
|
||||
}
|
||||
|
||||
private class ContactSelectionListAdapter extends CursorAdapter {
|
||||
|
||||
public ContactSelectionListAdapter(Context context, Cursor c) {
|
||||
super(context, c);
|
||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||
return ContactAccessor.getInstance().getCursorLoaderForContacts(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||
ContactItemView view = new ContactItemView(context);
|
||||
bindView(view, context, cursor);
|
||||
|
||||
return view;
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
||||
((CursorAdapter) listView.getAdapter()).changeCursor(data);
|
||||
emptyText.setText(R.string.contact_selection_group_activity__no_contacts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
boolean isPushUser;
|
||||
try {
|
||||
isPushUser = (cursor.getInt(cursor.getColumnIndexOrThrow(ContactAccessor.PUSH_COLUMN)) > 0);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
isPushUser = false;
|
||||
}
|
||||
ContactData contactData = ContactAccessor.getInstance().getContactData(context, cursor);
|
||||
PushContactData pushContactData = new PushContactData(contactData, isPushUser);
|
||||
((ContactItemView)view).set(pushContactData);
|
||||
}
|
||||
public void onLoaderReset(Loader<Cursor> loader) {
|
||||
((CursorAdapter) listView.getAdapter()).changeCursor(null);
|
||||
}
|
||||
|
||||
private class PushContactData {
|
||||
private final ContactData contactData;
|
||||
private final boolean pushSupport;
|
||||
public PushContactData(ContactData contactData, boolean pushSupport) {
|
||||
this.contactData = contactData;
|
||||
this.pushSupport = pushSupport;
|
||||
}
|
||||
private class ListClickListener implements AdapterView.OnItemClickListener {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> l, View v, int position, long id) {
|
||||
final DataHolder contactData = (DataHolder) v.getTag(R.id.contact_info_tag);
|
||||
final ViewHolder holder = (ViewHolder) v.getTag(R.id.holder_tag);
|
||||
|
||||
if (holder == null) {
|
||||
Log.w(TAG, "ViewHolder was null, can't proceed with click logic.");
|
||||
return;
|
||||
}
|
||||
|
||||
private class ContactItemView extends RelativeLayout {
|
||||
private ContactData contactData;
|
||||
private boolean pushSupport;
|
||||
private CheckBox checkBox;
|
||||
private TextView name;
|
||||
private TextView number;
|
||||
private TextView label;
|
||||
private View pushLabel;
|
||||
if (multi) holder.checkBox.toggle();
|
||||
|
||||
public ContactItemView(Context context) {
|
||||
super(context);
|
||||
|
||||
li.inflate(R.layout.push_contact_selection_list_item, this, true);
|
||||
|
||||
this.name = (TextView) findViewById(R.id.name);
|
||||
this.number = (TextView) findViewById(R.id.number);
|
||||
this.label = (TextView) findViewById(R.id.label);
|
||||
this.checkBox = (CheckBox) findViewById(R.id.check_box);
|
||||
this.pushLabel = findViewById(R.id.push_support_label);
|
||||
}
|
||||
|
||||
public void selected() {
|
||||
|
||||
checkBox.toggle();
|
||||
|
||||
if (checkBox.isChecked()) {
|
||||
if (contactData.numbers.size() == 1) addSingleNumberContact(contactData);
|
||||
else addMultipleNumberContact(contactData, name, checkBox);
|
||||
} else {
|
||||
if (!multi || holder.checkBox.isChecked()) {
|
||||
addContact(contactData);
|
||||
} else if (multi) {
|
||||
removeContact(contactData);
|
||||
}
|
||||
}
|
||||
|
||||
public void set(PushContactData pushContactData) {
|
||||
this.contactData = pushContactData.contactData;
|
||||
this.pushSupport = pushContactData.pushSupport;
|
||||
|
||||
if (!pushSupport) {
|
||||
this.name.setTextColor(drawables.getColor(1, 0xff000000));
|
||||
this.number.setTextColor(drawables.getColor(1, 0xff000000));
|
||||
this.pushLabel.setBackgroundColor(drawables.getColor(3, 0x99000000));
|
||||
} else {
|
||||
this.name.setTextColor(drawables.getColor(0, 0xa0000000));
|
||||
this.number.setTextColor(drawables.getColor(0, 0xa0000000));
|
||||
this.pushLabel.setBackgroundColor(drawables.getColor(2, 0xff64a926));
|
||||
}
|
||||
|
||||
if (selectedContacts.containsKey(contactData.id))
|
||||
this.checkBox.setChecked(true);
|
||||
else
|
||||
this.checkBox.setChecked(false);
|
||||
|
||||
this.name.setText(contactData.name);
|
||||
|
||||
if (contactData.numbers.isEmpty()) {
|
||||
this.name.setEnabled(false);
|
||||
this.number.setText("");
|
||||
this.label.setText("");
|
||||
} else {
|
||||
this.number.setText(contactData.numbers.get(0).number);
|
||||
this.label.setText(contactData.numbers.get(0).type);
|
||||
}
|
||||
}
|
||||
public void setOnContactSelectedListener(OnContactSelectedListener onContactSelectedListener) {
|
||||
this.onContactSelectedListener = onContactSelectedListener;
|
||||
}
|
||||
|
||||
private class DiscriminatorFinishedListener implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
|
||||
private final ContactData contactData;
|
||||
private final TextView textView;
|
||||
private final CheckBox checkBox;
|
||||
|
||||
public DiscriminatorFinishedListener(ContactData contactData, TextView textView, CheckBox checkBox) {
|
||||
this.contactData = contactData;
|
||||
this.textView = textView;
|
||||
this.checkBox = checkBox;
|
||||
}
|
||||
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
ContactData selected = selectedContacts.get(contactData.id);
|
||||
|
||||
if (selected == null && textView != null) {
|
||||
if (textView != null) checkBox.setChecked(false);
|
||||
} else if (selected.numbers.size() == 0) {
|
||||
selectedContacts.remove(selected.id);
|
||||
if (textView != null) checkBox.setChecked(false);
|
||||
}
|
||||
|
||||
if (textView == null)
|
||||
((CursorAdapter) getListView().getAdapter()).notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
onClick(dialog, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private class DiscriminatorClickedListener implements DialogInterface.OnMultiChoiceClickListener {
|
||||
private final ContactData contactData;
|
||||
|
||||
public DiscriminatorClickedListener(ContactData contactData) {
|
||||
this.contactData = contactData;
|
||||
}
|
||||
|
||||
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
|
||||
Log.w("ContactSelectionListActivity", "Got checked: " + isChecked);
|
||||
|
||||
ContactData existing = selectedContacts.get(contactData.id);
|
||||
|
||||
if (existing == null) {
|
||||
Log.w("ContactSelectionListActivity", "No existing contact data, creating...");
|
||||
|
||||
if (!isChecked)
|
||||
throw new AssertionError("We shouldn't be unchecking data that doesn't exist.");
|
||||
|
||||
existing = new ContactData(contactData.id, contactData.name);
|
||||
selectedContacts.put(existing.id, existing);
|
||||
}
|
||||
|
||||
NumberData selectedData = contactData.numbers.get(which);
|
||||
|
||||
if (!isChecked) existing.numbers.remove(selectedData);
|
||||
else existing.numbers.add(selectedData);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
|
||||
return ContactAccessor.getInstance().getCursorLoaderForContactsWithNumbers(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor) {
|
||||
Cursor pushCursor = ContactAccessor.getInstance().getCursorForContactsWithPush(getActivity());
|
||||
((CursorAdapter) getListAdapter()).changeCursor(new MergeCursor(new Cursor[]{pushCursor,cursor}));
|
||||
((TextView)getView().findViewById(android.R.id.empty)).setText(R.string.contact_selection_group_activity__no_contacts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> arg0) {
|
||||
((CursorAdapter) getListAdapter()).changeCursor(null);
|
||||
public interface OnContactSelectedListener {
|
||||
public void onContactSelected(ContactData contactData);
|
||||
}
|
||||
}
|
||||
|
@ -1,269 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2011 Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.res.TypedArray;
|
||||
import android.database.Cursor;
|
||||
import android.database.MergeCursor;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CursorAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.actionbarsherlock.app.SherlockListFragment;
|
||||
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.NumberData;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Activity for selecting a list of contacts. Displayed inside
|
||||
* a PushContactSelectionActivity tab frame, and ultimately called by
|
||||
* ComposeMessageActivity for selecting a list of destination contacts.
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*
|
||||
*/
|
||||
|
||||
public class SingleContactSelectionListFragment extends SherlockListFragment
|
||||
implements LoaderManager.LoaderCallbacks<Cursor>
|
||||
{
|
||||
private final String TAG = SingleContactSelectionListFragment.class.getSimpleName();
|
||||
private final int STYLE_ATTRIBUTES[] = new int[]{R.attr.contact_selection_push_user,
|
||||
R.attr.contact_selection_lay_user,
|
||||
R.attr.contact_selection_push_label,
|
||||
R.attr.contact_selection_lay_label};
|
||||
|
||||
private static LayoutInflater li;
|
||||
private OnContactSelectedListener onContactSelectedListener;
|
||||
private TypedArray drawables;
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
li = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
initializeResources();
|
||||
initializeCursor();
|
||||
}
|
||||
|
||||
public void setOnContactSelectedListener(OnContactSelectedListener onContactSelectedListener) {
|
||||
this.onContactSelectedListener = onContactSelectedListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.single_contact_selection_list_activity, container, false);
|
||||
}
|
||||
|
||||
private void addSingleNumberContact(ContactData contactData) {
|
||||
if (onContactSelectedListener != null) {
|
||||
onContactSelectedListener.onContactSelected(contactData);
|
||||
}
|
||||
}
|
||||
|
||||
public void update() {
|
||||
this.getLoaderManager().restartLoader(0, null, this);
|
||||
}
|
||||
|
||||
private void addMultipleNumberContact(ContactData contactData, TextView textView) {
|
||||
String[] options = new String[contactData.numbers.size()];
|
||||
int i = 0;
|
||||
|
||||
for (NumberData option : contactData.numbers) {
|
||||
options[i++] = option.type + " " + option.number;
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setTitle(R.string.ContactSelectionlistFragment_select_for + " " + contactData.name);
|
||||
builder.setSingleChoiceItems(options, -1, new DiscriminatorClickedListener(contactData));
|
||||
//builder.setPositiveButton(android.R.string.ok, new DiscriminatorFinishedListener(contactData, textView));
|
||||
builder.setOnCancelListener(new DiscriminatorFinishedListener(contactData, textView));
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void initializeCursor() {
|
||||
final ContactSelectionListAdapter listAdapter = new ContactSelectionListAdapter(getActivity(), null);
|
||||
setListAdapter(listAdapter);
|
||||
this.getLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
this.getListView().setFocusable(true);
|
||||
this.drawables = getActivity().obtainStyledAttributes(STYLE_ATTRIBUTES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onListItemClick(ListView l, View v, int position, long id) {
|
||||
((ContactItemView)v).selected();
|
||||
}
|
||||
|
||||
private class ContactSelectionListAdapter extends CursorAdapter {
|
||||
|
||||
public ContactSelectionListAdapter(Context context, Cursor c) {
|
||||
super(context, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||
ContactItemView view = new ContactItemView(context);
|
||||
bindView(view, context, cursor);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
boolean isPushUser;
|
||||
try {
|
||||
isPushUser = (cursor.getInt(cursor.getColumnIndexOrThrow(ContactAccessor.PUSH_COLUMN)) > 0);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
isPushUser = false;
|
||||
}
|
||||
ContactData contactData = ContactAccessor.getInstance().getContactData(context, cursor);
|
||||
PushContactData pushContactData = new PushContactData(contactData, isPushUser);
|
||||
((ContactItemView)view).set(pushContactData);
|
||||
}
|
||||
}
|
||||
|
||||
private class PushContactData {
|
||||
private final ContactData contactData;
|
||||
private final boolean pushSupport;
|
||||
public PushContactData(ContactData contactData, boolean pushSupport) {
|
||||
this.contactData = contactData;
|
||||
this.pushSupport = pushSupport;
|
||||
}
|
||||
}
|
||||
|
||||
private class ContactItemView extends RelativeLayout {
|
||||
private ContactData contactData;
|
||||
private boolean pushSupport;
|
||||
private TextView name;
|
||||
private TextView number;
|
||||
private TextView label;
|
||||
private View pushLabel;
|
||||
|
||||
public ContactItemView(Context context) {
|
||||
super(context);
|
||||
|
||||
li.inflate(R.layout.single_contact_selection_list_item, this, true);
|
||||
|
||||
this.name = (TextView) findViewById(R.id.name);
|
||||
this.number = (TextView) findViewById(R.id.number);
|
||||
this.label = (TextView) findViewById(R.id.label);
|
||||
this.pushLabel = findViewById(R.id.push_support_label);
|
||||
}
|
||||
|
||||
public void selected() {
|
||||
if (contactData.numbers.size() == 1) addSingleNumberContact(contactData);
|
||||
else addMultipleNumberContact(contactData, name);
|
||||
}
|
||||
|
||||
public void set(PushContactData pushContactData) {
|
||||
this.contactData = pushContactData.contactData;
|
||||
this.pushSupport = pushContactData.pushSupport;
|
||||
|
||||
if (!pushSupport) {
|
||||
this.name.setTextColor(drawables.getColor(1, 0xff000000));
|
||||
this.number.setTextColor(drawables.getColor(1, 0xff000000));
|
||||
this.pushLabel.setBackgroundColor(drawables.getColor(3, 0x99000000));
|
||||
} else {
|
||||
this.name.setTextColor(drawables.getColor(0, 0xa0000000));
|
||||
this.number.setTextColor(drawables.getColor(0, 0xa0000000));
|
||||
this.pushLabel.setBackgroundColor(drawables.getColor(2, 0xff64a926));
|
||||
}
|
||||
|
||||
this.name.setText(contactData.name);
|
||||
|
||||
if (contactData.numbers.isEmpty()) {
|
||||
this.name.setEnabled(false);
|
||||
this.number.setText("");
|
||||
this.label.setText("");
|
||||
} else {
|
||||
this.number.setText(contactData.numbers.get(0).number);
|
||||
this.label.setText(contactData.numbers.get(0).type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class DiscriminatorFinishedListener implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
|
||||
private final ContactData contactData;
|
||||
private final TextView textView;
|
||||
|
||||
public DiscriminatorFinishedListener(ContactData contactData, TextView textView) {
|
||||
this.contactData = contactData;
|
||||
this.textView = textView;
|
||||
}
|
||||
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
private class DiscriminatorClickedListener implements DialogInterface.OnClickListener {
|
||||
private final ContactData contactData;
|
||||
|
||||
public DiscriminatorClickedListener(ContactData contactData) {
|
||||
this.contactData = contactData;
|
||||
}
|
||||
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
ContactData singlePhoneContact = new ContactData(contactData.id,
|
||||
contactData.name,
|
||||
Collections.singletonList(contactData.numbers.get(which)));
|
||||
addSingleNumberContact(singlePhoneContact);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
|
||||
return ContactAccessor.getInstance().getCursorLoaderForContactsWithNumbers(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor) {
|
||||
Cursor pushCursor = ContactAccessor.getInstance().getCursorForContactsWithPush(getActivity());
|
||||
((CursorAdapter) getListAdapter()).changeCursor(new MergeCursor(new Cursor[]{pushCursor,cursor}));
|
||||
((TextView)getView().findViewById(android.R.id.empty)).setText(R.string.contact_selection_group_activity__no_contacts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> arg0) {
|
||||
((CursorAdapter) getListAdapter()).changeCursor(null);
|
||||
}
|
||||
|
||||
public interface OnContactSelectedListener {
|
||||
public void onContactSelected(ContactData contactData);
|
||||
}
|
||||
}
|
@ -20,9 +20,7 @@ import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.database.MergeCursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
@ -33,6 +31,7 @@ import android.provider.ContactsContract.Contacts;
|
||||
import android.provider.ContactsContract.Data;
|
||||
import android.provider.ContactsContract.PhoneLookup;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.telephony.PhoneNumberUtils;
|
||||
import android.util.Log;
|
||||
|
||||
@ -44,6 +43,7 @@ import org.whispersystems.textsecure.util.Base64;
|
||||
import java.io.IOException;
|
||||
import java.lang.Long;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@ -82,6 +82,10 @@ public class ContactAccessor {
|
||||
null, null, null, ContactsContract.Groups.TITLE + " ASC");
|
||||
}
|
||||
|
||||
public Loader<Cursor> getCursorLoaderForContacts(Context context) {
|
||||
return new ContactsCursorLoader(context);
|
||||
}
|
||||
|
||||
public Cursor getCursorForContactsWithNumbers(Context context) {
|
||||
Uri uri = ContactsContract.Contacts.CONTENT_URI;
|
||||
String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER + " = 1";
|
||||
@ -90,25 +94,28 @@ public class ContactAccessor {
|
||||
ContactsContract.Contacts.DISPLAY_NAME + " ASC");
|
||||
}
|
||||
|
||||
public Cursor getCursorForContactsWithPush(Context context) {
|
||||
public Collection<ContactData> getContactsWithPush(Context context) {
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final String[] inProjection = new String[]{PhoneLookup._ID, PhoneLookup.DISPLAY_NAME};
|
||||
final String[] outProjection = new String[]{PhoneLookup._ID, PhoneLookup.DISPLAY_NAME, PUSH_COLUMN};
|
||||
MatrixCursor cursor = new MatrixCursor(outProjection);
|
||||
|
||||
List<String> pushNumbers = Directory.getInstance(context).getActiveNumbers();
|
||||
final Collection<ContactData> lookupData = new ArrayList<ContactData>(pushNumbers.size());
|
||||
|
||||
for (String pushNumber : pushNumbers) {
|
||||
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(pushNumber));
|
||||
Cursor lookupCursor = resolver.query(uri, inProjection, null, null, null);
|
||||
try {
|
||||
if (lookupCursor != null && lookupCursor.moveToFirst()) {
|
||||
cursor.addRow(new Object[]{lookupCursor.getLong(0), lookupCursor.getString(1), 1});
|
||||
final ContactData contactData = new ContactData(lookupCursor.getLong(0), lookupCursor.getString(1));
|
||||
contactData.numbers.add(new NumberData("TextSecure", pushNumber));
|
||||
lookupData.add(contactData);
|
||||
}
|
||||
} finally {
|
||||
if (lookupCursor != null)
|
||||
lookupCursor.close();
|
||||
}
|
||||
}
|
||||
return cursor;
|
||||
return lookupData;
|
||||
}
|
||||
|
||||
public String getNameFromContact(Context context, Uri uri) {
|
||||
|
@ -10,6 +10,7 @@ import android.provider.ContactsContract.Contacts;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||
import org.thoughtcrime.securesms.util.LRUCache;
|
||||
|
||||
import java.io.InputStream;
|
||||
@ -82,10 +83,13 @@ public class ContactPhotoFactory {
|
||||
localUserContactPhotoCache.remove(recipient.getContactUri());
|
||||
}
|
||||
|
||||
private static Bitmap getContactPhoto(Context context, Uri uri) {
|
||||
public static Bitmap getContactPhoto(Context context, Uri uri) {
|
||||
InputStream inputStream = ContactsContract.Contacts.openContactPhotoInputStream(context.getContentResolver(), uri);
|
||||
|
||||
if (inputStream == null) return ContactPhotoFactory.getDefaultContactPhoto(context);
|
||||
else return BitmapFactory.decodeStream(inputStream);
|
||||
final Bitmap contactPhoto;
|
||||
if (inputStream == null) contactPhoto = ContactPhotoFactory.getDefaultContactPhoto(context);
|
||||
else contactPhoto = BitmapFactory.decodeStream(inputStream);
|
||||
|
||||
return contactPhoto;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,254 @@
|
||||
/**
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.thoughtcrime.securesms.contacts;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.provider.ContactsContract;
|
||||
import android.support.v4.widget.CursorAdapter;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||
import org.thoughtcrime.securesms.util.BitmapWorkerRunnable;
|
||||
import org.thoughtcrime.securesms.util.BitmapWorkerRunnable.AsyncDrawable;
|
||||
import org.thoughtcrime.securesms.util.TaggedFutureTask;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
|
||||
|
||||
/**
|
||||
* List adapter to display all contacts and their related information
|
||||
*
|
||||
* @author Jake McGinty
|
||||
*/
|
||||
public class ContactSelectionListAdapter extends CursorAdapter
|
||||
implements StickyListHeadersAdapter
|
||||
{
|
||||
private final static String TAG = "ContactListAdapter";
|
||||
|
||||
private final static ExecutorService photoResolver = Util.newSingleThreadedLifoExecutor();
|
||||
|
||||
private final static int STYLE_ATTRIBUTES[] = new int[]{R.attr.contact_selection_push_user,
|
||||
R.attr.contact_selection_lay_user,
|
||||
R.attr.contact_selection_label_text};
|
||||
|
||||
private int TYPE_COLUMN = -1;
|
||||
private int NAME_COLUMN = -1;
|
||||
private int NUMBER_COLUMN = -1;
|
||||
private int NUMBER_TYPE_COLUMN = -1;
|
||||
private int ID_COLUMN = -1;
|
||||
|
||||
private final Context context;
|
||||
private final boolean multiSelect;
|
||||
private final LayoutInflater li;
|
||||
private final TypedArray drawables;
|
||||
private final Bitmap defaultPhoto;
|
||||
private final Bitmap defaultCroppedPhoto;
|
||||
private final int scaledPhotoSize;
|
||||
|
||||
private final HashMap<Long, ContactAccessor.ContactData> selectedContacts = new HashMap<Long, ContactAccessor.ContactData>();
|
||||
|
||||
public ContactSelectionListAdapter(Context context, Cursor cursor, boolean multiSelect) {
|
||||
super(context, cursor, 0);
|
||||
this.context = context;
|
||||
this.li = LayoutInflater.from(context);
|
||||
this.drawables = context.obtainStyledAttributes(STYLE_ATTRIBUTES);
|
||||
this.multiSelect = multiSelect;
|
||||
this.defaultPhoto = ContactPhotoFactory.getDefaultContactPhoto(context);
|
||||
this.scaledPhotoSize = context.getResources().getDimensionPixelSize(R.dimen.contact_selection_photo_size);
|
||||
this.defaultCroppedPhoto = BitmapUtil.getScaledCircleCroppedBitmap(defaultPhoto, scaledPhotoSize);
|
||||
}
|
||||
|
||||
public static class ViewHolder {
|
||||
public CheckBox checkBox;
|
||||
public TextView name;
|
||||
public TextView number;
|
||||
public ImageView contactPhoto;
|
||||
public int position;
|
||||
}
|
||||
|
||||
public static class DataHolder {
|
||||
public int type;
|
||||
public String name;
|
||||
public String number;
|
||||
public int numberType;
|
||||
public long id;
|
||||
}
|
||||
|
||||
public static class HeaderViewHolder {
|
||||
TextView text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||
final View v = li.inflate(R.layout.push_contact_selection_list_item, parent, false);
|
||||
final ViewHolder holder = new ViewHolder();
|
||||
|
||||
if (v != null) {
|
||||
holder.name = (TextView) v.findViewById(R.id.name);
|
||||
holder.number = (TextView) v.findViewById(R.id.number);
|
||||
holder.checkBox = (CheckBox) v.findViewById(R.id.check_box);
|
||||
holder.contactPhoto = (ImageView) v.findViewById(R.id.contact_photo_image);
|
||||
|
||||
if (!multiSelect) holder.checkBox.setVisibility(View.GONE);
|
||||
|
||||
v.setTag(R.id.holder_tag, holder);
|
||||
v.setTag(R.id.contact_info_tag, new DataHolder());
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
final DataHolder contactData = (DataHolder) view.getTag(R.id.contact_info_tag);
|
||||
final ViewHolder holder = (ViewHolder) view.getTag(R.id.holder_tag);
|
||||
if (holder == null) {
|
||||
Log.w(TAG, "ViewHolder was null. This should not happen.");
|
||||
return;
|
||||
}
|
||||
if (contactData == null) {
|
||||
Log.w(TAG, "DataHolder was null. This should not happen.");
|
||||
return;
|
||||
}
|
||||
if (ID_COLUMN < 0) {
|
||||
populateColumnIndices(cursor);
|
||||
}
|
||||
|
||||
contactData.type = cursor.getInt(TYPE_COLUMN);
|
||||
contactData.name = cursor.getString(NAME_COLUMN);
|
||||
contactData.number = cursor.getString(NUMBER_COLUMN);
|
||||
contactData.numberType = cursor.getInt(NUMBER_TYPE_COLUMN);
|
||||
contactData.id = cursor.getLong(ID_COLUMN);
|
||||
|
||||
if (contactData.type != ContactsDatabase.PUSH_TYPE) {
|
||||
holder.name.setTextColor(drawables.getColor(1, 0xff000000));
|
||||
holder.number.setTextColor(drawables.getColor(1, 0xff000000));
|
||||
} else {
|
||||
holder.name.setTextColor(drawables.getColor(0, 0xa0000000));
|
||||
holder.number.setTextColor(drawables.getColor(0, 0xa0000000));
|
||||
}
|
||||
|
||||
if (selectedContacts.containsKey(contactData.id)) {
|
||||
holder.checkBox.setChecked(true);
|
||||
} else {
|
||||
holder.checkBox.setChecked(false);
|
||||
}
|
||||
|
||||
holder.name.setText(contactData.name);
|
||||
|
||||
if (contactData.number == null || contactData.number.isEmpty()) {
|
||||
holder.name.setEnabled(false);
|
||||
holder.number.setText("");
|
||||
} else if (contactData.type == ContactsDatabase.PUSH_TYPE) {
|
||||
holder.number.setText(contactData.number);
|
||||
} else {
|
||||
final CharSequence label = ContactsContract.CommonDataKinds.Phone.getTypeLabel(context.getResources(),
|
||||
contactData.numberType, "");
|
||||
final CharSequence numberWithLabel = contactData.number + " " + label;
|
||||
final Spannable numberLabelSpan = new SpannableString(numberWithLabel);
|
||||
numberLabelSpan.setSpan(new ForegroundColorSpan(drawables.getColor(2, 0xff444444)), contactData.number.length(), numberWithLabel.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
holder.number.setText(numberLabelSpan);
|
||||
}
|
||||
holder.contactPhoto.setImageBitmap(defaultCroppedPhoto);
|
||||
loadBitmap(contactData.number, holder.contactPhoto);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getHeaderView(int i, View convertView, ViewGroup viewGroup) {
|
||||
final Cursor c = getCursor();
|
||||
final HeaderViewHolder holder;
|
||||
if (convertView == null) {
|
||||
holder = new HeaderViewHolder();
|
||||
convertView = li.inflate(R.layout.push_contact_selection_list_header, viewGroup, false);
|
||||
holder.text = (TextView) convertView.findViewById(R.id.text);
|
||||
convertView.setTag(holder);
|
||||
} else {
|
||||
holder = (HeaderViewHolder) convertView.getTag();
|
||||
}
|
||||
c.moveToPosition(i);
|
||||
|
||||
final int type = c.getInt(c.getColumnIndexOrThrow(ContactsDatabase.TYPE_COLUMN));
|
||||
final int headerTextRes;
|
||||
switch (type) {
|
||||
case 1: headerTextRes = R.string.contact_selection_list__header_textsecure_users; break;
|
||||
default: headerTextRes = R.string.contact_selection_list__header_other; break;
|
||||
}
|
||||
holder.text.setText(headerTextRes);
|
||||
return convertView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getHeaderId(int i) {
|
||||
final Cursor c = getCursor();
|
||||
c.moveToPosition(i);
|
||||
return c.getInt(c.getColumnIndexOrThrow(ContactsDatabase.TYPE_COLUMN));
|
||||
}
|
||||
|
||||
public boolean cancelPotentialWork(String number, ImageView imageView) {
|
||||
final TaggedFutureTask<?> bitmapWorkerTask = AsyncDrawable.getBitmapWorkerTask(imageView);
|
||||
|
||||
if (bitmapWorkerTask != null) {
|
||||
final Object tag = bitmapWorkerTask.getTag();
|
||||
if (tag != null && !tag.equals(number)) {
|
||||
bitmapWorkerTask.cancel(true);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void loadBitmap(String number, ImageView imageView) {
|
||||
if (cancelPotentialWork(number, imageView)) {
|
||||
final BitmapWorkerRunnable runnable = new BitmapWorkerRunnable(context, imageView, defaultPhoto, number, scaledPhotoSize);
|
||||
final TaggedFutureTask<?> task = new TaggedFutureTask<Void>(runnable, null, number);
|
||||
final AsyncDrawable asyncDrawable = new AsyncDrawable(context.getResources(), defaultCroppedPhoto, task);
|
||||
|
||||
imageView.setImageDrawable(asyncDrawable);
|
||||
if (!task.isCancelled()) photoResolver.execute(new FutureTask<Void>(task, null));
|
||||
}
|
||||
}
|
||||
|
||||
public Map<Long,ContactAccessor.ContactData> getSelectedContacts() {
|
||||
return selectedContacts;
|
||||
}
|
||||
|
||||
private void populateColumnIndices(final Cursor cursor) {
|
||||
this.TYPE_COLUMN = cursor.getColumnIndexOrThrow(ContactsDatabase.TYPE_COLUMN);
|
||||
this.NAME_COLUMN = cursor.getColumnIndexOrThrow(ContactsDatabase.NAME_COLUMN);
|
||||
this.NUMBER_COLUMN = cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_COLUMN);
|
||||
this.NUMBER_TYPE_COLUMN = cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_TYPE_COLUMN);
|
||||
this.ID_COLUMN = cursor.getColumnIndexOrThrow(ContactsDatabase.ID_COLUMN);
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.thoughtcrime.securesms.contacts;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
|
||||
/**
|
||||
* CursorLoader that initializes a ContactsDatabase instance
|
||||
*
|
||||
* @author Jake McGinty
|
||||
*/
|
||||
public class ContactsCursorLoader extends CursorLoader {
|
||||
|
||||
private final Context context;
|
||||
private ContactsDatabase db;
|
||||
|
||||
public ContactsCursorLoader(Context context) {
|
||||
super(context);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor loadInBackground() {
|
||||
db = new ContactsDatabase(context);
|
||||
return db.getAllContacts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReset() {
|
||||
super.onReset();
|
||||
db.close();
|
||||
}
|
||||
}
|
211
src/org/thoughtcrime/securesms/contacts/ContactsDatabase.java
Normal file
211
src/org/thoughtcrime/securesms/contacts/ContactsDatabase.java
Normal file
@ -0,0 +1,211 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.thoughtcrime.securesms.contacts;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.CursorWrapper;
|
||||
import android.database.MergeCursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.net.Uri;
|
||||
import android.provider.ContactsContract;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Database to supply all types of contacts that TextSecure needs to know about
|
||||
*
|
||||
* @author Jake McGinty
|
||||
*/
|
||||
public class ContactsDatabase {
|
||||
private static final String TAG = ContactsDatabase.class.getSimpleName();
|
||||
private final DatabaseOpenHelper dbHelper;
|
||||
private final Context context;
|
||||
|
||||
public static final String TABLE_NAME = "CONTACTS";
|
||||
public static final String ID_COLUMN = ContactsContract.CommonDataKinds.Phone._ID;
|
||||
public static final String NAME_COLUMN = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME;
|
||||
public static final String NUMBER_TYPE_COLUMN = ContactsContract.CommonDataKinds.Phone.TYPE;
|
||||
public static final String NUMBER_COLUMN = ContactsContract.CommonDataKinds.Phone.NUMBER;
|
||||
public static final String TYPE_COLUMN = "type";
|
||||
|
||||
private static final String CONTACT_LIST_SORT = NAME_COLUMN + " ASC";
|
||||
private static final String[] ANDROID_PROJECTION = new String[]{ID_COLUMN,
|
||||
NAME_COLUMN,
|
||||
NUMBER_TYPE_COLUMN,
|
||||
NUMBER_COLUMN};
|
||||
|
||||
public static final int NORMAL_TYPE = 0;
|
||||
public static final int PUSH_TYPE = 1;
|
||||
public static final int GROUP_TYPE = 2;
|
||||
|
||||
public ContactsDatabase(Context context) {
|
||||
this.dbHelper = new DatabaseOpenHelper(context);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
dbHelper.close();
|
||||
}
|
||||
|
||||
public Cursor getAllContacts() {
|
||||
return query(null, null, null);
|
||||
}
|
||||
|
||||
private Cursor query(String selection, String[] selectionArgs, String[] columns) {
|
||||
final Cursor localCursor = queryLocalDb(selection, selectionArgs, columns);
|
||||
final Cursor androidCursor;
|
||||
|
||||
if (TextSecurePreferences.isSmsNonDataOutEnabled(context)) {
|
||||
androidCursor = queryAndroidDb();
|
||||
} else{
|
||||
return localCursor;
|
||||
}
|
||||
|
||||
if (localCursor != null && androidCursor != null) return new MergeCursor(new Cursor[]{localCursor,androidCursor});
|
||||
else if (localCursor != null) return localCursor;
|
||||
else if (androidCursor != null) return androidCursor;
|
||||
else return null;
|
||||
}
|
||||
|
||||
private Cursor queryAndroidDb() {
|
||||
Cursor cursor = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, ANDROID_PROJECTION, null, null, CONTACT_LIST_SORT);
|
||||
return new TypedCursorWrapper(cursor);
|
||||
}
|
||||
|
||||
private Cursor queryLocalDb(String selection, String[] selectionArgs, String[] columns) {
|
||||
SQLiteDatabase localDb = dbHelper.getReadableDatabase();
|
||||
final Cursor localCursor;
|
||||
if (localDb != null) localCursor = localDb.query(TABLE_NAME, columns, selection, selectionArgs, null, null, CONTACT_LIST_SORT);
|
||||
else localCursor = null;
|
||||
if (localCursor != null && !localCursor.moveToFirst()) {
|
||||
localCursor.close();
|
||||
return null;
|
||||
}
|
||||
return localCursor;
|
||||
}
|
||||
|
||||
private static class DatabaseOpenHelper extends SQLiteOpenHelper {
|
||||
|
||||
private final Context context;
|
||||
private SQLiteDatabase mDatabase;
|
||||
|
||||
private static final String TABLE_CREATE =
|
||||
"CREATE TABLE " + TABLE_NAME + " (" +
|
||||
ID_COLUMN + " INTEGER PRIMARY KEY, " +
|
||||
NAME_COLUMN + " TEXT, " +
|
||||
NUMBER_TYPE_COLUMN + " INTEGER, " +
|
||||
NUMBER_COLUMN + " TEXT, " +
|
||||
TYPE_COLUMN + " INTEGER);";
|
||||
|
||||
DatabaseOpenHelper(Context context) {
|
||||
super(context, null, null, 1);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
Log.d(TAG, "onCreate called for contacts database.");
|
||||
mDatabase = db;
|
||||
mDatabase.execSQL(TABLE_CREATE);
|
||||
if (TextSecurePreferences.isPushRegistered(context)) {
|
||||
try {
|
||||
loadPushUsers();
|
||||
} catch (IOException ioe) {
|
||||
Log.e(TAG, "Issue when trying to load push users into memory db.", ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
|
||||
+ newVersion + ", which will destroy all old data");
|
||||
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
|
||||
onCreate(db);
|
||||
}
|
||||
|
||||
private void loadPushUsers() throws IOException {
|
||||
Log.d(TAG, "populating push users into virtual db.");
|
||||
Collection<ContactAccessor.ContactData> pushUsers = ContactAccessor.getInstance().getContactsWithPush(context);
|
||||
for (ContactAccessor.ContactData user : pushUsers) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(ID_COLUMN, user.id);
|
||||
values.put(NAME_COLUMN, user.name);
|
||||
values.put(NUMBER_TYPE_COLUMN, ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM);
|
||||
values.put(NUMBER_COLUMN, user.numbers.get(0).number);
|
||||
values.put(TYPE_COLUMN, PUSH_TYPE);
|
||||
mDatabase.insert(TABLE_NAME, null, values);
|
||||
}
|
||||
Log.d(TAG, "finished populating push users.");
|
||||
}
|
||||
}
|
||||
|
||||
private static class TypedCursorWrapper extends CursorWrapper {
|
||||
|
||||
private final int pushColumnIndex;
|
||||
|
||||
public TypedCursorWrapper(Cursor cursor) {
|
||||
super(cursor);
|
||||
pushColumnIndex = cursor.getColumnCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return super.getColumnCount() + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnIndex(String columnName) {
|
||||
if (TYPE_COLUMN.equals(columnName)) return super.getColumnCount();
|
||||
else return super.getColumnIndex(columnName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException {
|
||||
if (TYPE_COLUMN.equals(columnName)) return super.getColumnCount();
|
||||
else return super.getColumnIndexOrThrow(columnName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int columnIndex) {
|
||||
if (columnIndex == pushColumnIndex) return TYPE_COLUMN;
|
||||
else return super.getColumnName(columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getColumnNames() {
|
||||
final String[] columns = new String[super.getColumnCount() + 1];
|
||||
System.arraycopy(super.getColumnNames(), 0, columns, 0, super.getColumnCount());
|
||||
columns[pushColumnIndex] = TYPE_COLUMN;
|
||||
return columns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(int columnIndex) {
|
||||
if (columnIndex == pushColumnIndex) return NORMAL_TYPE;
|
||||
else return super.getInt(columnIndex);
|
||||
}
|
||||
}
|
||||
}
|
@ -100,8 +100,8 @@ public class Recipient implements Parcelable, CanonicalRecipient {
|
||||
this.number = in.readString();
|
||||
this.name = in.readString();
|
||||
this.recipientId = in.readLong();
|
||||
this.contactUri = (Uri)in.readParcelable(null);
|
||||
this.contactPhoto = (Bitmap)in.readParcelable(null);
|
||||
this.contactUri = in.readParcelable(null);
|
||||
this.contactPhoto = in.readParcelable(null);
|
||||
}
|
||||
|
||||
public synchronized Uri getContactUri() {
|
||||
@ -138,19 +138,6 @@ public class Recipient implements Parcelable, CanonicalRecipient {
|
||||
return GroupUtil.isEncodedGroup(number);
|
||||
}
|
||||
|
||||
// public void updateAsynchronousContent(RecipientDetails result) {
|
||||
// if (result != null) {
|
||||
// Recipient.this.name.set(result.name);
|
||||
// Recipient.this.contactUri.set(result.contactUri);
|
||||
// Recipient.this.contactPhoto.set(result.avatar);
|
||||
//
|
||||
// synchronized(this) {
|
||||
// if (listener == null) asynchronousUpdateComplete = true;
|
||||
// else listener.onModified(Recipient.this);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
public synchronized void addListener(RecipientModifiedListener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ public class RecipientProvider {
|
||||
try {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
Uri contactUri = Contacts.getLookupUri(cursor.getLong(2), cursor.getString(1));
|
||||
Bitmap contactPhoto = getContactPhoto(context, Uri.withAppendedPath(Contacts.CONTENT_URI,
|
||||
Bitmap contactPhoto = ContactPhotoFactory.getContactPhoto(context, Uri.withAppendedPath(Contacts.CONTENT_URI,
|
||||
cursor.getLong(2)+""));
|
||||
|
||||
return new RecipientDetails(cursor.getString(0), contactUri, contactPhoto);
|
||||
@ -164,15 +164,6 @@ public class RecipientProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap getContactPhoto(Context context, Uri uri) {
|
||||
InputStream inputStream = ContactsContract.Contacts.openContactPhotoInputStream(context.getContentResolver(), uri);
|
||||
|
||||
if (inputStream == null)
|
||||
return ContactPhotoFactory.getDefaultContactPhoto(context);
|
||||
else
|
||||
return BitmapFactory.decodeStream(inputStream);
|
||||
}
|
||||
|
||||
public static class RecipientDetails {
|
||||
public final String name;
|
||||
public final Bitmap avatar;
|
||||
|
116
src/org/thoughtcrime/securesms/util/BitmapWorkerRunnable.java
Normal file
116
src/org/thoughtcrime/securesms/util/BitmapWorkerRunnable.java
Normal file
@ -0,0 +1,116 @@
|
||||
/**
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* Runnable to load contact photos if they have them
|
||||
*
|
||||
* @author Jake McGinty
|
||||
*/
|
||||
public class BitmapWorkerRunnable implements Runnable {
|
||||
private final static String TAG = BitmapWorkerRunnable.class.getSimpleName();
|
||||
|
||||
private final Bitmap defaultPhoto;
|
||||
|
||||
private final WeakReference<ImageView> imageViewReference;
|
||||
private final Context context;
|
||||
private final int size;
|
||||
public final String number;
|
||||
|
||||
public BitmapWorkerRunnable(Context context, ImageView imageView, Bitmap defaultPhoto, String number, int size) {
|
||||
this.imageViewReference = new WeakReference<ImageView>(imageView);
|
||||
this.context = context;
|
||||
this.defaultPhoto = defaultPhoto;
|
||||
this.size = size;
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final Bitmap bitmap;
|
||||
try {
|
||||
final Recipient recipient = RecipientFactory.getRecipientsFromString(context, number, false).getPrimaryRecipient();
|
||||
final Bitmap contactPhoto = recipient.getContactPhoto();
|
||||
if (defaultPhoto == contactPhoto) {
|
||||
return;
|
||||
}
|
||||
|
||||
bitmap = BitmapUtil.getScaledCircleCroppedBitmap(contactPhoto, size);
|
||||
} catch (RecipientFormattingException rfe) {
|
||||
Log.w(TAG, "Couldn't get recipient from string", rfe);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bitmap != null) {
|
||||
final ImageView imageView = imageViewReference.get();
|
||||
final TaggedFutureTask<?> bitmapWorkerTask = AsyncDrawable.getBitmapWorkerTask(imageView);
|
||||
|
||||
if (bitmapWorkerTask.getTag().equals(number) && imageView != null) {
|
||||
final BitmapDrawable drawable = new BitmapDrawable(context.getResources(), bitmap);
|
||||
imageView.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
imageView.setImageDrawable(drawable);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class AsyncDrawable extends BitmapDrawable {
|
||||
private final WeakReference<TaggedFutureTask<?>> bitmapWorkerTaskReference;
|
||||
|
||||
public AsyncDrawable(Resources res, Bitmap bitmap,
|
||||
TaggedFutureTask<?> bitmapWorkerTask) {
|
||||
super(res, bitmap);
|
||||
bitmapWorkerTaskReference =
|
||||
new WeakReference<TaggedFutureTask<?>>(bitmapWorkerTask);
|
||||
}
|
||||
|
||||
public TaggedFutureTask<?> getBitmapWorkerTask() {
|
||||
return bitmapWorkerTaskReference.get();
|
||||
}
|
||||
|
||||
public static TaggedFutureTask<?> getBitmapWorkerTask(ImageView imageView) {
|
||||
if (imageView != null) {
|
||||
final Drawable drawable = imageView.getDrawable();
|
||||
if (drawable instanceof AsyncDrawable) {
|
||||
final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
|
||||
return asyncDrawable.getBitmapWorkerTask();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -2,7 +2,9 @@ package org.thoughtcrime.securesms.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.whispersystems.textsecure.directory.Directory;
|
||||
@ -19,6 +21,37 @@ import java.util.Set;
|
||||
public class DirectoryHelper {
|
||||
private static final String TAG = DirectoryHelper.class.getSimpleName();
|
||||
|
||||
public static void refreshDirectoryWithProgressDialog(final Context context) {
|
||||
refreshDirectoryWithProgressDialog(context, null);
|
||||
}
|
||||
|
||||
public static void refreshDirectoryWithProgressDialog(final Context context, final DirectoryUpdateFinishedListener listener) {
|
||||
if (!TextSecurePreferences.isPushRegistered(context)) {
|
||||
Toast.makeText(context.getApplicationContext(),
|
||||
context.getString(R.string.SingleContactSelectionActivity_you_are_not_registered_with_the_push_service),
|
||||
Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
new ProgressDialogAsyncTask<Void,Void,Void>(context,
|
||||
R.string.SingleContactSelectionActivity_updating_directory,
|
||||
R.string.SingleContactSelectionActivity_updating_push_directory)
|
||||
{
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
DirectoryHelper.refreshDirectory(context.getApplicationContext());
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid) {
|
||||
super.onPostExecute(aVoid);
|
||||
if (listener != null) listener.onUpdateFinished();
|
||||
}
|
||||
}.execute();
|
||||
|
||||
}
|
||||
|
||||
public static void refreshDirectory(final Context context) {
|
||||
refreshDirectory(context, PushServiceSocketFactory.create(context));
|
||||
}
|
||||
@ -63,4 +96,8 @@ public class DirectoryHelper {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static interface DirectoryUpdateFinishedListener {
|
||||
public void onUpdateFinished();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
public abstract class ProgressDialogAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
|
||||
private final Context context;
|
||||
private ProgressDialog progress;
|
||||
private final String title;
|
||||
private final String message;
|
||||
|
||||
public ProgressDialogAsyncTask(Context context, String title, String message) {
|
||||
super();
|
||||
this.context = context;
|
||||
this.title = title;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public ProgressDialogAsyncTask(Context context, int title, int message) {
|
||||
this(context, context.getString(title), context.getString(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
progress = ProgressDialog.show(context, title, message, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Result result) {
|
||||
if (progress != null) progress.dismiss();
|
||||
}
|
||||
}
|
||||
|
43
src/org/thoughtcrime/securesms/util/TaggedFutureTask.java
Normal file
43
src/org/thoughtcrime/securesms/util/TaggedFutureTask.java
Normal file
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (C) 2014 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
/**
|
||||
* FutureTask with a reference identifier tag.
|
||||
*
|
||||
* @author Jake McGinty
|
||||
*/
|
||||
public class TaggedFutureTask<V> extends FutureTask<V> {
|
||||
private final Object tag;
|
||||
public TaggedFutureTask(Runnable runnable, V result, Object tag) {
|
||||
super(runnable, result);
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public TaggedFutureTask(Callable<V> callable, Object tag) {
|
||||
super(callable);
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public Object getTag() {
|
||||
return tag;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user