mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-11 18:54:21 +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:windowSoftInputMode="stateHidden"
|
||||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||||
|
|
||||||
<activity android:name=".SingleContactSelectionActivity"
|
<activity android:name=".NewConversationActivity"
|
||||||
android:label="@string/AndroidManifest__select_contact"
|
android:label="@string/AndroidManifest__select_contacts"
|
||||||
android:windowSoftInputMode="stateHidden"
|
android:windowSoftInputMode="stateHidden"
|
||||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ dependencies {
|
|||||||
compile 'com.actionbarsherlock:actionbarsherlock:4.4.0@aar'
|
compile 'com.actionbarsherlock:actionbarsherlock:4.4.0@aar'
|
||||||
compile 'com.android.support:support-v4:19.0.1'
|
compile 'com.android.support:support-v4:19.0.1'
|
||||||
compile 'com.google.android.gcm:gcm-client:1.0.2'
|
compile 'com.google.android.gcm:gcm-client:1.0.2'
|
||||||
|
compile 'se.emilsjolander:stickylistheaders:2.2.0'
|
||||||
|
|
||||||
compile project(':library')
|
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"
|
<ListView android:id="@android:id/list"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:drawSelectorOnTop="false"
|
android:drawSelectorOnTop="false"
|
||||||
android:scrollbarStyle="insideOverlay"
|
android:scrollbarStyle="insideOverlay"
|
||||||
android:fadingEdgeLength="16dip"
|
android:fadingEdgeLength="16dip"
|
||||||
|
@ -5,12 +5,16 @@
|
|||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
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
|
<fragment
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:id="@+id/contact_selection_list_fragment"
|
android:id="@+id/contact_selection_list_fragment"
|
||||||
android:name="org.thoughtcrime.securesms.SingleContactSelectionListFragment">
|
android:name="org.thoughtcrime.securesms.PushContactSelectionListFragment">
|
||||||
</fragment>
|
</fragment>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
@ -8,7 +8,7 @@
|
|||||||
<fragment
|
<fragment
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:id="@+id/contact_selection_list_fragment"
|
android:id="@+id/contact_selection_list_fragment"
|
||||||
android:name="org.thoughtcrime.securesms.PushContactSelectionListFragment">
|
android:name="org.thoughtcrime.securesms.PushContactSelectionListFragment">
|
||||||
</fragment>
|
</fragment>
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<ListView android:id="@android:id/list"
|
<se.emilsjolander.stickylistheaders.StickyListHeadersListView android:id="@android:id/list"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="match_parent" />
|
||||||
android:fastScrollEnabled="true" />
|
|
||||||
|
|
||||||
<TextView android:id="@android:id/empty"
|
<TextView android:id="@android:id/empty"
|
||||||
android:layout_width="match_parent"
|
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"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<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:layout_height="?android:attr/listPreferredItemHeight"
|
||||||
android:paddingRight="25dip">
|
android:paddingRight="25dip">
|
||||||
|
|
||||||
<View android:id="@+id/push_support_label"
|
<ImageView android:id="@+id/contact_photo_image"
|
||||||
android:layout_height="fill_parent"
|
android:layout_width="@dimen/contact_selection_photo_size"
|
||||||
android:layout_width="3dip"
|
android:layout_height="@dimen/contact_selection_photo_size"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:background="#ff64a926"
|
android:layout_centerVertical="true"
|
||||||
android:visibility="visible"
|
android:layout_marginLeft="10dp"
|
||||||
/>
|
android:cropToPadding="true"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
<TextView android:id="@+id/label"
|
android:contentDescription="@string/SingleContactSelectionActivity_contact_photo" />
|
||||||
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"
|
<TextView android:id="@+id/number"
|
||||||
android:visibility = "visible"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="8dip"
|
android:layout_marginBottom="8dip"
|
||||||
android:layout_marginTop="-8dip"
|
android:layout_marginTop="-8dip"
|
||||||
android:layout_marginLeft="14dip"
|
android:layout_marginLeft="14dip"
|
||||||
android:layout_alignParentLeft="true"
|
|
||||||
android:layout_alignParentBottom="true"
|
android:layout_alignParentBottom="true"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:ellipsize="marquee"
|
android:ellipsize="marquee"
|
||||||
|
android:layout_toRightOf="@id/contact_photo_image"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:fontFamily="sans-serif-light"
|
android:fontFamily="sans-serif-light" />
|
||||||
/>
|
|
||||||
|
|
||||||
<TextView android:id="@+id/name"
|
<TextView android:id="@+id/name"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_above="@id/number"
|
android:layout_above="@id/number"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
android:layout_alignParentLeft="true"
|
|
||||||
android:layout_marginBottom="1dip"
|
android:layout_marginBottom="1dip"
|
||||||
android:layout_marginLeft="14dip"
|
android:layout_marginLeft="14dip"
|
||||||
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
|
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:ellipsize="marquee"
|
android:ellipsize="marquee"
|
||||||
|
android:layout_toRightOf="@id/contact_photo_image"
|
||||||
android:gravity="center_vertical|left"
|
android:gravity="center_vertical|left"
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||||
/>
|
|
||||||
|
|
||||||
<CheckBox
|
<CheckBox android:id="@+id/check_box"
|
||||||
android:id="@+id/check_box"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentRight="true"
|
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:singleLine="true"
|
||||||
android:hint="@string/recipients_panel__to"
|
android:hint="@string/recipients_panel__to"
|
||||||
android:paddingRight="45dip"
|
android:paddingRight="45dip"
|
||||||
|
android:paddingLeft="15dp"
|
||||||
|
android:paddingTop="12dp"
|
||||||
|
android:paddingBottom="12dp"
|
||||||
android:textColor="?conversation_editor_text_color"
|
android:textColor="?conversation_editor_text_color"
|
||||||
|
android:background="?conversation_editor_background"
|
||||||
android:layout_width="fill_parent"/>
|
android:layout_width="fill_parent"/>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
@ -21,7 +21,7 @@
|
|||||||
<attr name="lower_right_divet" format="reference" />
|
<attr name="lower_right_divet" format="reference" />
|
||||||
|
|
||||||
<attr name="conversation_background" format="reference|color"/>
|
<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_editor_text_color" format="reference|color"/>
|
||||||
<attr name="conversation_send_button" format="reference"/>
|
<attr name="conversation_send_button" format="reference"/>
|
||||||
<attr name="conversation_send_secure_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_push_user" format="reference|color" />
|
||||||
<attr name="contact_selection_lay_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_label_text" format="reference|color" />
|
||||||
<attr name="contact_selection_lay_label" format="reference|color" />
|
|
||||||
|
|
||||||
<attr name="navigation_drawer_background" format="reference|color"/>
|
<attr name="navigation_drawer_background" format="reference|color"/>
|
||||||
<attr name="navigation_drawer_text_color" format="color"/>
|
<attr name="navigation_drawer_text_color" format="color"/>
|
||||||
|
@ -3,4 +3,5 @@
|
|||||||
<dimen name="emoji_drawer_size">40dip</dimen>
|
<dimen name="emoji_drawer_size">40dip</dimen>
|
||||||
<dimen name="conversation_item_corner_radius">3dp</dimen>
|
<dimen name="conversation_item_corner_radius">3dp</dimen>
|
||||||
<dimen name="conversation_item_drop_shadow_dist">2dp</dimen>
|
<dimen name="conversation_item_drop_shadow_dist">2dp</dimen>
|
||||||
|
<dimen name="contact_selection_photo_size">50dp</dimen>
|
||||||
</resources>
|
</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_group_activity -->
|
||||||
<!-- contact_selection_list_activity -->
|
<!-- contact_selection_list_activity -->
|
||||||
<string name="contact_selection_group_activity__no_contacts">No contacts.</string>
|
<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 -->
|
<!-- single_contact_selection_activity -->
|
||||||
<string name="single_contact_selection_group_activity__filter">Type a name to filter…</string>
|
<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_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-->
|
<!-- ContactSelectionListFragment-->
|
||||||
<string name="ContactSelectionlistFragment_select_for">Select for</string>
|
<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__copied_to_clipboard">Copied to clipboard</string>
|
||||||
<string name="log_submit_activity__loading_logcat">Loading logcat…</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__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 -->
|
<!-- database_migration_activity -->
|
||||||
<string name="database_migration_activity__would_you_like_to_import_your_existing_text_messages">Would you like to import your existing text messages into TextSecure\'s encrypted database?</string>
|
<string name="database_migration_activity__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>
|
<string name="registration_progress_activity__generating_keys">Generating keys...</string>
|
||||||
|
|
||||||
<!-- recipients_panel -->
|
<!-- 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>
|
<string name="recipients_panel__add_member">Add member</string>
|
||||||
|
|
||||||
<!-- review_identities -->
|
<!-- review_identities -->
|
||||||
@ -752,6 +755,8 @@
|
|||||||
<!-- contact_selection_list -->
|
<!-- contact_selection_list -->
|
||||||
<string name="contact_selection_list__menu_select_all">Select All</string>
|
<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__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 -->
|
<!-- contact_selection -->
|
||||||
<string name="contact_selection__menu_finished">Finished</string>
|
<string name="contact_selection__menu_finished">Finished</string>
|
||||||
|
@ -23,6 +23,22 @@
|
|||||||
<item name="background">#ff111111</item>
|
<item name="background">#ff111111</item>
|
||||||
</style>
|
</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">
|
<style name="transparent_progress">
|
||||||
<item name="android:windowFrame">@null</item>
|
<item name="android:windowFrame">@null</item>
|
||||||
<item name="android:windowBackground">@android:color/transparent</item>
|
<item name="android:windowBackground">@android:color/transparent</item>
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<style name="TextSecure.LightTheme" parent="@style/Theme.Sherlock.Light">
|
<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_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_unread">@drawable/conversation_list_item_background_unread_light</item>
|
||||||
<item name="conversation_list_item_background_selected">@drawable/list_selected_holo_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_push_user">#ff000000</item>
|
||||||
<item name="contact_selection_lay_user">#a0000000</item>
|
<item name="contact_selection_lay_user">#a0000000</item>
|
||||||
<item name="contact_selection_push_label">#ff64a926</item>
|
<item name="contact_selection_label_text">#66000000</item>
|
||||||
<item name="contact_selection_lay_label">#99000000</item>
|
|
||||||
|
|
||||||
<item name="conversation_background">#ffdddddd</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_editor_text_color">#ff111111</item>
|
||||||
<item name="conversation_send_button">@drawable/ic_send_holo_light</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>
|
<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_primary_color">#ffeeeeee</item>
|
||||||
<item name="conversation_received_text_secondary_color">#44eeeeee</item>
|
<item name="conversation_received_text_secondary_color">#44eeeeee</item>
|
||||||
|
|
||||||
<item name="contact_selection_push_user">#ffdddddd</item>
|
<item name="contact_selection_push_user">#ffeeeeee</item>
|
||||||
<item name="contact_selection_lay_user">#ffcccccc</item>
|
<item name="contact_selection_lay_user">#afeeeeee</item>
|
||||||
<item name="contact_selection_push_label">#ff64a926</item>
|
<item name="contact_selection_label_text">#66eeeeee</item>
|
||||||
<item name="contact_selection_lay_label">#11ffffff</item>
|
|
||||||
|
|
||||||
<item name="conversation_item_received_background">@drawable/conversation_item_received_shape_dark</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>
|
<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="lower_right_divet">@drawable/divet_lower_right_light</item>
|
||||||
|
|
||||||
<item name="conversation_background">@color/black</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_editor_text_color">#ffeeeeee</item>
|
||||||
<item name="conversation_send_button">@drawable/ic_send_holo_dark</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>
|
<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 {
|
private class DirectoryUpdateListener implements Preference.OnPreferenceClickListener {
|
||||||
@Override
|
@Override
|
||||||
public boolean onPreferenceClick(Preference preference) {
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
final Context context = ApplicationPreferencesActivity.this;
|
DirectoryHelper.refreshDirectoryWithProgressDialog(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();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,6 +176,7 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle state) {
|
protected void onCreate(Bundle state) {
|
||||||
|
overridePendingTransition(R.anim.slide_from_right, R.anim.fade_scale_out);
|
||||||
dynamicTheme.onCreate(this);
|
dynamicTheme.onCreate(this);
|
||||||
dynamicLanguage.onCreate(this);
|
dynamicLanguage.onCreate(this);
|
||||||
super.onCreate(state);
|
super.onCreate(state);
|
||||||
@ -219,6 +220,7 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
|
|||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
MessageNotifier.setVisibleThread(-1L);
|
MessageNotifier.setVisibleThread(-1L);
|
||||||
|
overridePendingTransition(R.anim.fade_scale_in, R.anim.slide_to_right);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -170,8 +170,8 @@ public class ConversationListActivity extends PassphraseRequiredSherlockFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void openSingleContactSelection() {
|
private void openSingleContactSelection() {
|
||||||
Intent intent = new Intent(this, SingleContactSelectionActivity.class);
|
Intent intent = new Intent(this, NewConversationActivity.class);
|
||||||
intent.putExtra(SingleContactSelectionActivity.MASTER_SECRET_EXTRA, masterSecret);
|
intent.putExtra(NewConversationActivity.MASTER_SECRET_EXTRA, masterSecret);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ import org.apache.http.impl.client.DefaultHttpClient;
|
|||||||
import org.apache.http.message.BasicNameValuePair;
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
import org.apache.http.util.EntityUtils;
|
import org.apache.http.util.EntityUtils;
|
||||||
import org.thoughtcrime.securesms.util.ActionBarUtil;
|
import org.thoughtcrime.securesms.util.ActionBarUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
@ -159,11 +160,11 @@ public class LogSubmitActivity extends SherlockActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SubmitToPastebinAsyncTask extends AsyncTask<Void,Void,String> {
|
private class SubmitToPastebinAsyncTask extends ProgressDialogAsyncTask<Void,Void,String> {
|
||||||
private ProgressDialog progressDialog;
|
|
||||||
private final String paste;
|
private final String paste;
|
||||||
|
|
||||||
public SubmitToPastebinAsyncTask(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;
|
this.paste = paste;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,7 +175,6 @@ public class LogSubmitActivity extends SherlockActivity {
|
|||||||
URL url = new URL(HASTEBIN_ENDPOINT);
|
URL url = new URL(HASTEBIN_ENDPOINT);
|
||||||
urlConnection = (HttpURLConnection) url.openConnection();
|
urlConnection = (HttpURLConnection) url.openConnection();
|
||||||
urlConnection.setDoOutput(true);
|
urlConnection.setDoOutput(true);
|
||||||
urlConnection.setFixedLengthStreamingMode(paste.length());
|
|
||||||
urlConnection.setReadTimeout(10000);
|
urlConnection.setReadTimeout(10000);
|
||||||
urlConnection.connect();
|
urlConnection.connect();
|
||||||
|
|
||||||
@ -201,22 +201,9 @@ public class LogSubmitActivity extends SherlockActivity {
|
|||||||
return null;
|
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
|
@Override
|
||||||
protected void onPostExecute(final String response) {
|
protected void onPostExecute(final String response) {
|
||||||
super.onPostExecute(response);
|
super.onPostExecute(response);
|
||||||
progressDialog.dismiss();
|
|
||||||
|
|
||||||
if (response != null && !response.startsWith("Bad API request")) {
|
if (response != null && !response.startsWith("Bad API request")) {
|
||||||
TextView showText = new TextView(LogSubmitActivity.this);
|
TextView showText = new TextView(LogSubmitActivity.this);
|
||||||
|
@ -16,12 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms;
|
package org.thoughtcrime.securesms;
|
||||||
|
|
||||||
import android.app.ProgressDialog;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.Preference;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Toast;
|
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.DirectoryHelper;
|
||||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||||
import org.thoughtcrime.securesms.util.NumberUtil;
|
import org.thoughtcrime.securesms.util.NumberUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.whispersystems.textsecure.crypto.MasterSecret;
|
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
import static org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activity container for selecting a list of contacts. Provides a tab frame for
|
* Activity container for selecting a list of contacts.
|
||||||
* contact, group, and "recent contact" activity tabs. Used by ComposeMessageActivity
|
|
||||||
* when selecting a list of contacts to address a message to.
|
|
||||||
*
|
*
|
||||||
* @author Moxie Marlinspike
|
* @author Moxie Marlinspike
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class SingleContactSelectionActivity extends PassphraseRequiredSherlockFragmentActivity {
|
public class NewConversationActivity extends PassphraseRequiredSherlockFragmentActivity {
|
||||||
private final static String TAG = "SingleContactSelectionActivity";
|
private final static String TAG = "ContactSelectActivity";
|
||||||
public final static String MASTER_SECRET_EXTRA = "master_secret";
|
public final static String MASTER_SECRET_EXTRA = "master_secret";
|
||||||
|
|
||||||
private final DynamicTheme dynamicTheme = new DynamicTheme();
|
private final DynamicTheme dynamicTheme = new DynamicTheme();
|
||||||
private MasterSecret masterSecret;
|
private MasterSecret masterSecret;
|
||||||
|
|
||||||
|
private SingleRecipientPanel recipientsPanel;
|
||||||
|
private PushContactSelectionListFragment contactsFragment;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle icicle) {
|
protected void onCreate(Bundle icicle) {
|
||||||
dynamicTheme.onCreate(this);
|
dynamicTheme.onCreate(this);
|
||||||
@ -74,16 +74,41 @@ public class SingleContactSelectionActivity extends PassphraseRequiredSherlockFr
|
|||||||
ActionBarUtil.initializeDefaultActionBar(this, actionBar);
|
ActionBarUtil.initializeDefaultActionBar(this, actionBar);
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
setContentView(R.layout.single_contact_selection_activity);
|
setContentView(R.layout.new_conversation_activity);
|
||||||
initializeResources();
|
initializeResources();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeResources() {
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
dynamicTheme.onResume(this);
|
||||||
masterSecret = getIntent().getParcelableExtra(MASTER_SECRET_EXTRA);
|
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);
|
@Override
|
||||||
listFragment.setOnContactSelectedListener(new SingleContactSelectionListFragment.OnContactSelectedListener() {
|
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
|
@Override
|
||||||
public void onContactSelected(ContactData contactData) {
|
public void onContactSelected(ContactData contactData) {
|
||||||
Log.i(TAG, "Choosing contact from list.");
|
Log.i(TAG, "Choosing contact from list.");
|
||||||
@ -99,15 +124,35 @@ public class SingleContactSelectionActivity extends PassphraseRequiredSherlockFr
|
|||||||
openNewConversation(recipients);
|
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) {
|
private Recipients contactDataToRecipients(ContactData contactData) {
|
||||||
if (contactData == null || contactData.numbers == null) return null;
|
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) {
|
for (ContactAccessor.NumberData numberData : contactData.numbers) {
|
||||||
if (NumberUtil.isValidSmsOrEmailOrGroup(numberData.number)) {
|
if (NumberUtil.isValidSmsOrEmailOrGroup(numberData.number)) {
|
||||||
try {
|
try {
|
||||||
Recipients recipientsForNumber = RecipientFactory.getRecipientsFromString(SingleContactSelectionActivity.this,
|
Recipients recipientsForNumber = RecipientFactory.getRecipientsFromString(NewConversationActivity.this,
|
||||||
numberData.number,
|
numberData.number,
|
||||||
false);
|
false);
|
||||||
recipients.getRecipientsList().addAll(recipientsForNumber.getRecipientsList());
|
recipients.getRecipientsList().addAll(recipientsForNumber.getRecipientsList());
|
||||||
@ -121,83 +166,14 @@ public class SingleContactSelectionActivity extends PassphraseRequiredSherlockFr
|
|||||||
|
|
||||||
private void openNewConversation(Recipients recipients) {
|
private void openNewConversation(Recipients recipients) {
|
||||||
if (recipients != null) {
|
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.RECIPIENTS_EXTRA, recipients.toIdString());
|
||||||
intent.putExtra(ConversationActivity.MASTER_SECRET_EXTRA, masterSecret);
|
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.THREAD_ID_EXTRA, existingThread);
|
||||||
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
|
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
finish();
|
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.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
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.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.recipients.Recipients;
|
||||||
import org.thoughtcrime.securesms.util.ActionBarUtil;
|
import org.thoughtcrime.securesms.util.ActionBarUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
||||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
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.app.ActionBar;
|
||||||
import com.actionbarsherlock.view.Menu;
|
import com.actionbarsherlock.view.Menu;
|
||||||
@ -31,22 +44,24 @@ import com.actionbarsherlock.view.MenuInflater;
|
|||||||
import com.actionbarsherlock.view.MenuItem;
|
import com.actionbarsherlock.view.MenuItem;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
import static org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activity container for selecting a list of contacts. Provides a tab frame for
|
* Activity container for selecting a list of contacts.
|
||||||
* contact, group, and "recent contact" activity tabs. Used by ComposeMessageActivity
|
|
||||||
* when selecting a list of contacts to address a message to.
|
|
||||||
*
|
*
|
||||||
* @author Moxie Marlinspike
|
* @author Moxie Marlinspike
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class PushContactSelectionActivity extends PassphraseRequiredSherlockFragmentActivity {
|
public class PushContactSelectionActivity extends PassphraseRequiredSherlockFragmentActivity {
|
||||||
|
private final static String TAG = "ContactSelectActivity";
|
||||||
|
|
||||||
private final DynamicTheme dynamicTheme = new DynamicTheme();
|
private final DynamicTheme dynamicTheme = new DynamicTheme();
|
||||||
|
|
||||||
|
private PushContactSelectionListFragment contactsFragment;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle icicle) {
|
protected void onCreate(Bundle icicle) {
|
||||||
dynamicTheme.onCreate(this);
|
dynamicTheme.onCreate(this);
|
||||||
@ -57,6 +72,7 @@ public class PushContactSelectionActivity extends PassphraseRequiredSherlockFrag
|
|||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
setContentView(R.layout.push_contact_selection_activity);
|
setContentView(R.layout.push_contact_selection_activity);
|
||||||
|
initializeResources();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -66,34 +82,55 @@ public class PushContactSelectionActivity extends PassphraseRequiredSherlockFrag
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||||
MenuInflater inflater = this.getSupportMenuInflater();
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
super.onOptionsItemSelected(item);
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.menu_selection_finished:
|
case R.id.menu_refresh_directory: handleDirectoryRefresh(); return true;
|
||||||
case android.R.id.home:
|
case R.id.menu_selection_finished: handleSelectionFinished(); return true;
|
||||||
handleSelectionFinished(); return true;
|
case android.R.id.home: finish(); return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
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() {
|
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);
|
setResult(RESULT_OK, resultIntent);
|
||||||
|
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleDirectoryRefresh() {
|
||||||
|
DirectoryHelper.refreshDirectoryWithProgressDialog(this, new DirectoryHelper.DirectoryUpdateFinishedListener() {
|
||||||
|
@Override
|
||||||
|
public void onUpdateFinished() {
|
||||||
|
contactsFragment.update();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,345 +17,167 @@
|
|||||||
package org.thoughtcrime.securesms;
|
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.Cursor;
|
||||||
import android.database.MergeCursor;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.provider.ContactsContract;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.LoaderManager;
|
import android.support.v4.app.LoaderManager;
|
||||||
import android.support.v4.content.Loader;
|
import android.support.v4.content.Loader;
|
||||||
|
import android.support.v4.widget.CursorAdapter;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.CheckBox;
|
import android.widget.AdapterView;
|
||||||
import android.widget.CursorAdapter;
|
|
||||||
import android.widget.ListView;
|
|
||||||
import android.widget.RelativeLayout;
|
|
||||||
import android.widget.TextView;
|
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;
|
||||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
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.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activity for selecting a list of contacts. Displayed inside
|
* Fragment for selecting a one or more contacts from a list.
|
||||||
* a PushContactSelectionActivity tab frame, and ultimately called by
|
|
||||||
* ComposeMessageActivity for selecting a list of destination contacts.
|
|
||||||
*
|
*
|
||||||
* @author Moxie Marlinspike
|
* @author Moxie Marlinspike
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class PushContactSelectionListFragment extends SherlockListFragment
|
public class PushContactSelectionListFragment extends Fragment
|
||||||
implements LoaderManager.LoaderCallbacks<Cursor>
|
implements LoaderManager.LoaderCallbacks<Cursor>
|
||||||
{
|
{
|
||||||
private final int STYLE_ATTRIBUTES[] = new int[]{R.attr.contact_selection_push_user,
|
private static final String TAG = "ContactSelectFragment";
|
||||||
R.attr.contact_selection_lay_user,
|
|
||||||
R.attr.contact_selection_push_label,
|
|
||||||
R.attr.contact_selection_lay_label};
|
|
||||||
|
|
||||||
private final HashMap<Long, ContactData> selectedContacts = new HashMap<Long, ContactData>();
|
private TextView emptyText;
|
||||||
private static LayoutInflater li;
|
|
||||||
private TypedArray drawables;
|
private Map<Long, ContactData> selectedContacts;
|
||||||
|
private OnContactSelectedListener onContactSelectedListener;
|
||||||
|
private boolean multi = false;
|
||||||
|
private StickyListHeadersListView listView;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityCreated(Bundle icicle) {
|
public void onActivityCreated(Bundle icicle) {
|
||||||
super.onCreate(icicle);
|
super.onCreate(icicle);
|
||||||
li = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
|
||||||
initializeResources();
|
initializeResources();
|
||||||
initializeCursor();
|
initializeCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
return inflater.inflate(R.layout.push_contact_selection_list_activity, container, false);
|
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() {
|
public List<ContactData> getSelectedContacts() {
|
||||||
|
if (selectedContacts == null) return null;
|
||||||
|
|
||||||
List<ContactData> selected = new LinkedList<ContactData>();
|
List<ContactData> selected = new LinkedList<ContactData>();
|
||||||
selected.addAll(selectedContacts.values());
|
selected.addAll(selectedContacts.values());
|
||||||
|
|
||||||
return selected;
|
return selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleUnselectAll() {
|
public void setMultiSelect(boolean multi) {
|
||||||
selectedContacts.clear();
|
this.multi = multi;
|
||||||
((CursorAdapter) getListView().getAdapter()).notifyDataSetChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSelectAll() {
|
private void addContact(DataHolder data) {
|
||||||
selectedContacts.clear();
|
final ContactData contactData = new ContactData(data.id, data.name);
|
||||||
|
final CharSequence label = ContactsContract.CommonDataKinds.Phone.getTypeLabel(getResources(),
|
||||||
Cursor cursor = null;
|
data.numberType, "");
|
||||||
|
contactData.numbers.add(new ContactAccessor.NumberData(label.toString(), data.number));
|
||||||
try {
|
if (multi) {
|
||||||
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) {
|
|
||||||
selectedContacts.put(contactData.id, contactData);
|
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);
|
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() {
|
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);
|
this.getLoaderManager().initLoader(0, null, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeResources() {
|
private void initializeResources() {
|
||||||
this.getListView().setFocusable(true);
|
emptyText = (TextView) getView().findViewById(android.R.id.empty);
|
||||||
this.drawables = getActivity().obtainStyledAttributes(STYLE_ATTRIBUTES);
|
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
|
@Override
|
||||||
public void onListItemClick(ListView l, View v, int position, long id) {
|
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||||
((ContactItemView)v).selected();
|
return ContactAccessor.getInstance().getCursorLoaderForContacts(getActivity());
|
||||||
}
|
|
||||||
|
|
||||||
private class ContactSelectionListAdapter extends CursorAdapter {
|
|
||||||
|
|
||||||
public ContactSelectionListAdapter(Context context, Cursor c) {
|
|
||||||
super(context, c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
||||||
ContactItemView view = new ContactItemView(context);
|
((CursorAdapter) listView.getAdapter()).changeCursor(data);
|
||||||
bindView(view, context, cursor);
|
emptyText.setText(R.string.contact_selection_group_activity__no_contacts);
|
||||||
|
|
||||||
return view;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bindView(View view, Context context, Cursor cursor) {
|
public void onLoaderReset(Loader<Cursor> loader) {
|
||||||
boolean isPushUser;
|
((CursorAdapter) listView.getAdapter()).changeCursor(null);
|
||||||
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 class ListClickListener implements AdapterView.OnItemClickListener {
|
||||||
private final ContactData contactData;
|
@Override
|
||||||
private final boolean pushSupport;
|
public void onItemClick(AdapterView<?> l, View v, int position, long id) {
|
||||||
public PushContactData(ContactData contactData, boolean pushSupport) {
|
final DataHolder contactData = (DataHolder) v.getTag(R.id.contact_info_tag);
|
||||||
this.contactData = contactData;
|
final ViewHolder holder = (ViewHolder) v.getTag(R.id.holder_tag);
|
||||||
this.pushSupport = pushSupport;
|
|
||||||
}
|
if (holder == null) {
|
||||||
|
Log.w(TAG, "ViewHolder was null, can't proceed with click logic.");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ContactItemView extends RelativeLayout {
|
if (multi) holder.checkBox.toggle();
|
||||||
private ContactData contactData;
|
|
||||||
private boolean pushSupport;
|
|
||||||
private CheckBox checkBox;
|
|
||||||
private TextView name;
|
|
||||||
private TextView number;
|
|
||||||
private TextView label;
|
|
||||||
private View pushLabel;
|
|
||||||
|
|
||||||
public ContactItemView(Context context) {
|
if (!multi || holder.checkBox.isChecked()) {
|
||||||
super(context);
|
addContact(contactData);
|
||||||
|
} else if (multi) {
|
||||||
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 {
|
|
||||||
removeContact(contactData);
|
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))
|
public void setOnContactSelectedListener(OnContactSelectedListener onContactSelectedListener) {
|
||||||
this.checkBox.setChecked(true);
|
this.onContactSelectedListener = onContactSelectedListener;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DiscriminatorFinishedListener implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
|
public interface OnContactSelectedListener {
|
||||||
private final ContactData contactData;
|
public void onContactSelected(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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.MatrixCursor;
|
|
||||||
import android.database.MergeCursor;
|
import android.database.MergeCursor;
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
@ -33,6 +31,7 @@ import android.provider.ContactsContract.Contacts;
|
|||||||
import android.provider.ContactsContract.Data;
|
import android.provider.ContactsContract.Data;
|
||||||
import android.provider.ContactsContract.PhoneLookup;
|
import android.provider.ContactsContract.PhoneLookup;
|
||||||
import android.support.v4.content.CursorLoader;
|
import android.support.v4.content.CursorLoader;
|
||||||
|
import android.support.v4.content.Loader;
|
||||||
import android.telephony.PhoneNumberUtils;
|
import android.telephony.PhoneNumberUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@ -44,6 +43,7 @@ import org.whispersystems.textsecure.util.Base64;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.Long;
|
import java.lang.Long;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -82,6 +82,10 @@ public class ContactAccessor {
|
|||||||
null, null, null, ContactsContract.Groups.TITLE + " ASC");
|
null, null, null, ContactsContract.Groups.TITLE + " ASC");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Loader<Cursor> getCursorLoaderForContacts(Context context) {
|
||||||
|
return new ContactsCursorLoader(context);
|
||||||
|
}
|
||||||
|
|
||||||
public Cursor getCursorForContactsWithNumbers(Context context) {
|
public Cursor getCursorForContactsWithNumbers(Context context) {
|
||||||
Uri uri = ContactsContract.Contacts.CONTENT_URI;
|
Uri uri = ContactsContract.Contacts.CONTENT_URI;
|
||||||
String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER + " = 1";
|
String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER + " = 1";
|
||||||
@ -90,25 +94,28 @@ public class ContactAccessor {
|
|||||||
ContactsContract.Contacts.DISPLAY_NAME + " ASC");
|
ContactsContract.Contacts.DISPLAY_NAME + " ASC");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cursor getCursorForContactsWithPush(Context context) {
|
public Collection<ContactData> getContactsWithPush(Context context) {
|
||||||
final ContentResolver resolver = context.getContentResolver();
|
final ContentResolver resolver = context.getContentResolver();
|
||||||
final String[] inProjection = new String[]{PhoneLookup._ID, PhoneLookup.DISPLAY_NAME};
|
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();
|
List<String> pushNumbers = Directory.getInstance(context).getActiveNumbers();
|
||||||
|
final Collection<ContactData> lookupData = new ArrayList<ContactData>(pushNumbers.size());
|
||||||
|
|
||||||
for (String pushNumber : pushNumbers) {
|
for (String pushNumber : pushNumbers) {
|
||||||
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(pushNumber));
|
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(pushNumber));
|
||||||
Cursor lookupCursor = resolver.query(uri, inProjection, null, null, null);
|
Cursor lookupCursor = resolver.query(uri, inProjection, null, null, null);
|
||||||
try {
|
try {
|
||||||
if (lookupCursor != null && lookupCursor.moveToFirst()) {
|
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 {
|
} finally {
|
||||||
if (lookupCursor != null)
|
if (lookupCursor != null)
|
||||||
lookupCursor.close();
|
lookupCursor.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cursor;
|
return lookupData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getNameFromContact(Context context, Uri uri) {
|
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.R;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||||
import org.thoughtcrime.securesms.util.LRUCache;
|
import org.thoughtcrime.securesms.util.LRUCache;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@ -82,10 +83,13 @@ public class ContactPhotoFactory {
|
|||||||
localUserContactPhotoCache.remove(recipient.getContactUri());
|
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);
|
InputStream inputStream = ContactsContract.Contacts.openContactPhotoInputStream(context.getContentResolver(), uri);
|
||||||
|
|
||||||
if (inputStream == null) return ContactPhotoFactory.getDefaultContactPhoto(context);
|
final Bitmap contactPhoto;
|
||||||
else return BitmapFactory.decodeStream(inputStream);
|
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.number = in.readString();
|
||||||
this.name = in.readString();
|
this.name = in.readString();
|
||||||
this.recipientId = in.readLong();
|
this.recipientId = in.readLong();
|
||||||
this.contactUri = (Uri)in.readParcelable(null);
|
this.contactUri = in.readParcelable(null);
|
||||||
this.contactPhoto = (Bitmap)in.readParcelable(null);
|
this.contactPhoto = in.readParcelable(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized Uri getContactUri() {
|
public synchronized Uri getContactUri() {
|
||||||
@ -138,19 +138,6 @@ public class Recipient implements Parcelable, CanonicalRecipient {
|
|||||||
return GroupUtil.isEncodedGroup(number);
|
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) {
|
public synchronized void addListener(RecipientModifiedListener listener) {
|
||||||
listeners.add(listener);
|
listeners.add(listener);
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ public class RecipientProvider {
|
|||||||
try {
|
try {
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
Uri contactUri = Contacts.getLookupUri(cursor.getLong(2), cursor.getString(1));
|
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)+""));
|
cursor.getLong(2)+""));
|
||||||
|
|
||||||
return new RecipientDetails(cursor.getString(0), contactUri, contactPhoto);
|
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 static class RecipientDetails {
|
||||||
public final String name;
|
public final String name;
|
||||||
public final Bitmap avatar;
|
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.content.Context;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
|
import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.whispersystems.textsecure.directory.Directory;
|
import org.whispersystems.textsecure.directory.Directory;
|
||||||
@ -19,6 +21,37 @@ import java.util.Set;
|
|||||||
public class DirectoryHelper {
|
public class DirectoryHelper {
|
||||||
private static final String TAG = DirectoryHelper.class.getSimpleName();
|
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) {
|
public static void refreshDirectory(final Context context) {
|
||||||
refreshDirectory(context, PushServiceSocketFactory.create(context));
|
refreshDirectory(context, PushServiceSocketFactory.create(context));
|
||||||
}
|
}
|
||||||
@ -63,4 +96,8 @@ public class DirectoryHelper {
|
|||||||
return false;
|
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