batch invitation
// FREEBIE
@ -112,6 +112,16 @@
|
||||
<activity android:name=".ImportExportActivity"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".InviteActivity"
|
||||
android:theme="@style/TextSecure.HighlightTheme"
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:parentActivityName=".ConversationListActivity"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="org.thoughtcrime.securesms.ConversationListActivity" />
|
||||
</activity>
|
||||
|
||||
<activity android:name=".PromptMmsActivity"
|
||||
android:label="Configure MMS Settings"
|
||||
android:windowSoftInputMode="stateUnchanged"
|
||||
|
7
res/anim/slide_from_bottom.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<translate
|
||||
android:duration="350"
|
||||
android:fromYDelta="100%"
|
||||
android:toYDelta="0%" />
|
||||
</set>
|
7
res/anim/slide_to_bottom.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<translate
|
||||
android:duration="350"
|
||||
android:fromYDelta="0%"
|
||||
android:toYDelta="100%" />
|
||||
</set>
|
BIN
res/drawable-hdpi/ic_message_black_18dp.png
Normal file
After Width: | Height: | Size: 354 B |
BIN
res/drawable-hdpi/ic_share_black_18dp.png
Normal file
After Width: | Height: | Size: 398 B |
BIN
res/drawable-hdpi/love_heart.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
res/drawable-mdpi/ic_message_black_18dp.png
Normal file
After Width: | Height: | Size: 217 B |
BIN
res/drawable-mdpi/ic_share_black_18dp.png
Normal file
After Width: | Height: | Size: 267 B |
BIN
res/drawable-mdpi/love_heart.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
res/drawable-xhdpi/ic_message_black_18dp.png
Normal file
After Width: | Height: | Size: 320 B |
BIN
res/drawable-xhdpi/ic_share_black_18dp.png
Normal file
After Width: | Height: | Size: 531 B |
BIN
res/drawable-xhdpi/love_heart.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
res/drawable-xxhdpi/ic_message_black_18dp.png
Normal file
After Width: | Height: | Size: 506 B |
BIN
res/drawable-xxhdpi/ic_share_black_18dp.png
Normal file
After Width: | Height: | Size: 833 B |
BIN
res/drawable-xxhdpi/love_heart.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
res/drawable-xxxhdpi/ic_message_black_18dp.png
Normal file
After Width: | Height: | Size: 249 B |
BIN
res/drawable-xxxhdpi/ic_share_black_18dp.png
Normal file
After Width: | Height: | Size: 670 B |
BIN
res/drawable-xxxhdpi/love_heart.png
Normal file
After Width: | Height: | Size: 9.6 KiB |
76
res/layout/contact_filter_toolbar.xml
Normal file
@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<LinearLayout android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView android:id="@+id/action_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="center_vertical"
|
||||
android:clickable="true"
|
||||
android:background="@drawable/circle_touch_highlight_background"
|
||||
android:src="@drawable/ic_search_white_24dp" />
|
||||
|
||||
<LinearLayout android:id="@+id/toggle_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<EditText android:id="@+id/search_view"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0px"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:hint="@string/contact_selection_activity__enter_name_or_number"
|
||||
android:inputType="textPersonName"
|
||||
style="@style/TextSecure.TitleTextStyle"
|
||||
android:background="@android:color/transparent"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="center_vertical"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.AnimatingToggle
|
||||
android:id="@+id/button_toggle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView android:id="@+id/search_dialpad"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="center_vertical"
|
||||
android:clickable="true"
|
||||
android:background="@drawable/circle_touch_highlight_background"
|
||||
android:src="@drawable/ic_dialpad_white_24dp" />
|
||||
|
||||
<ImageView android:id="@+id/search_keyboard"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="center_vertical"
|
||||
android:clickable="true"
|
||||
android:visibility="gone"
|
||||
android:background="@drawable/circle_touch_highlight_background"
|
||||
android:src="@drawable/ic_keyboard_white_24dp" />
|
||||
|
||||
<ImageView android:id="@+id/search_clear"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="center_vertical"
|
||||
android:clickable="true"
|
||||
android:visibility="gone"
|
||||
android:background="@drawable/circle_touch_highlight_background"
|
||||
android:src="@drawable/ic_clear_white_24dp" />
|
||||
|
||||
</org.thoughtcrime.securesms.components.AnimatingToggle>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</merge>
|
@ -5,94 +5,17 @@
|
||||
android:orientation="vertical"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
<org.thoughtcrime.securesms.components.ContactFilterToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:layout_width="match_parent"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:theme="@style/TextSecure.LightActionBar">
|
||||
android:theme="@style/TextSecure.LightActionBar" />
|
||||
|
||||
<LinearLayout android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView android:id="@+id/action_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="center_vertical"
|
||||
android:clickable="true"
|
||||
android:background="@drawable/circle_touch_highlight_background"
|
||||
android:src="@drawable/ic_search_white_24dp" />
|
||||
|
||||
<LinearLayout android:id="@+id/toggle_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<EditText android:id="@+id/search_view"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0px"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:hint="@string/contact_selection_activity__enter_name_or_number"
|
||||
android:inputType="textPersonName"
|
||||
style="@style/TextSecure.TitleTextStyle"
|
||||
android:background="@android:color/transparent"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="center_vertical"/>
|
||||
|
||||
<org.thoughtcrime.securesms.components.AnimatingToggle
|
||||
android:id="@+id/button_toggle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView android:id="@+id/search_dialpad"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="center_vertical"
|
||||
android:clickable="true"
|
||||
android:background="@drawable/circle_touch_highlight_background"
|
||||
android:src="@drawable/ic_dialpad_white_24dp" />
|
||||
|
||||
<ImageView android:id="@+id/search_keyboard"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="center_vertical"
|
||||
android:clickable="true"
|
||||
android:visibility="gone"
|
||||
android:background="@drawable/circle_touch_highlight_background"
|
||||
android:src="@drawable/ic_keyboard_white_24dp" />
|
||||
|
||||
<ImageView android:id="@+id/search_clear"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="center_vertical"
|
||||
android:clickable="true"
|
||||
android:visibility="gone"
|
||||
android:background="@drawable/circle_touch_highlight_background"
|
||||
android:src="@drawable/ic_clear_white_24dp" />
|
||||
|
||||
</org.thoughtcrime.securesms.components.AnimatingToggle>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</android.support.v7.widget.Toolbar>
|
||||
|
||||
<fragment
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/contact_selection_list_fragment"
|
||||
android:name="org.thoughtcrime.securesms.ContactSelectionListFragment">
|
||||
</fragment>
|
||||
<fragment android:id="@+id/contact_selection_list_fragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:name="org.thoughtcrime.securesms.ContactSelectionListFragment" />
|
||||
|
||||
</LinearLayout>
|
@ -1,20 +1,13 @@
|
||||
<?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="?contact_selection_header_text"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</RelativeLayout>
|
||||
<TextView android:layout_width="match_parent"
|
||||
android:layout_height="32sp"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:textSize="15sp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="25dp"
|
||||
android:textStyle="bold"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android" />
|
||||
|
@ -35,16 +35,14 @@
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:fontFamily="sans-serif-light" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="10dip"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?contact_selection_label_text"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
<TextView android:id="@+id/label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="10dip"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:fontFamily="sans-serif-light"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
118
res/layout/invite_activity.xml
Normal file
@ -0,0 +1,118 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ScrollView android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true"
|
||||
android:clickable="false">
|
||||
|
||||
<LinearLayout android:layout_gravity="center"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp"
|
||||
android:background="@color/signal_primary">
|
||||
|
||||
<ImageView android:id="@+id/heart"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:contentDescription="@string/InviteActivity_heart_content_description"
|
||||
android:src="@drawable/love_heart" />
|
||||
|
||||
<TextView android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="20sp"
|
||||
android:text="@string/InviteActivity_friends_dont_let_friends_text_unencrypted"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_marginBottom="30dp"
|
||||
android:layout_marginTop="18dp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:paddingLeft="10dp"/>
|
||||
|
||||
<EditText android:id="@+id/invite_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minLines="2"
|
||||
tools:text="Let's switch to Signal: http://sgnl.link/asdfdfsa"
|
||||
android:background="@drawable/sent_bubble"
|
||||
android:padding="10dp"
|
||||
android:gravity="top"
|
||||
android:inputType="textShortMessage|textMultiLine"
|
||||
android:textColor="#ff333333"/>
|
||||
|
||||
<LinearLayout android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button android:id="@+id/share_button"
|
||||
android:drawablePadding="10dp"
|
||||
android:drawableLeft="@drawable/ic_share_black_18dp"
|
||||
android:text="@string/InviteActivity_share"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:ignore="ButtonStyle" />
|
||||
|
||||
<Button android:id="@+id/sms_button"
|
||||
android:drawableLeft="@drawable/ic_message_black_18dp"
|
||||
android:drawablePadding="10dp"
|
||||
android:text="@string/InviteActivity_send_sms"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:ignore="ButtonStyle" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
<LinearLayout android:id="@+id/sms_send_frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/white"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<org.thoughtcrime.securesms.components.ContactFilterToolbar
|
||||
android:id="@+id/contact_filter"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:layout_width="match_parent"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:theme="@style/TextSecure.LightActionBar" />
|
||||
|
||||
<fragment android:id="@+id/contact_selection_list_fragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:name="org.thoughtcrime.securesms.ContactSelectionListFragment"
|
||||
tools:layout="@layout/contact_selection_list_fragment"/>
|
||||
|
||||
<LinearLayout android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="right"
|
||||
android:padding="10dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button android:id="@+id/cancel_sms_button"
|
||||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/InviteActivity_cancel" />
|
||||
|
||||
<Button android:id="@+id/send_sms_button"
|
||||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
android:textColor="@color/signal_primary"
|
||||
android:enabled="false"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="Send to 0 friends"/>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
@ -1,29 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:title="@string/text_secure_normal__menu_new_group"
|
||||
android:id="@+id/menu_new_group"
|
||||
android:icon="@android:drawable/ic_menu_add" />
|
||||
android:id="@+id/menu_new_group" />
|
||||
|
||||
<item android:title="@string/text_secure_normal__menu_clear_passphrase"
|
||||
android:id="@+id/menu_clear_passphrase"
|
||||
android:icon="@android:drawable/ic_menu_close_clear_cancel" />
|
||||
android:id="@+id/menu_clear_passphrase" />
|
||||
|
||||
<item android:title="@string/text_secure_normal__mark_all_as_read"
|
||||
android:id="@+id/menu_mark_all_read"
|
||||
android:icon="@android:drawable/ic_menu_set_as" />
|
||||
android:id="@+id/menu_mark_all_read" />
|
||||
|
||||
<item android:title="@string/text_secure_normal__invite_friends"
|
||||
android:id="@+id/menu_invite" />
|
||||
|
||||
<item android:title="@string/arrays__import_export"
|
||||
android:id="@+id/menu_import_export"
|
||||
android:icon="@android:drawable/ic_menu_upload" />
|
||||
android:id="@+id/menu_import_export" />
|
||||
|
||||
<item android:title="@string/arrays__my_identity_key"
|
||||
android:id="@+id/menu_my_identity"
|
||||
android:icon="@android:drawable/ic_menu_view" />
|
||||
android:id="@+id/menu_my_identity" />
|
||||
|
||||
<item android:title="@string/text_secure_normal__menu_settings"
|
||||
android:id="@+id/menu_settings"
|
||||
android:icon="@android:drawable/ic_menu_preferences" />
|
||||
android:id="@+id/menu_settings" />
|
||||
|
||||
</menu>
|
||||
|
@ -115,8 +115,8 @@
|
||||
<string name="ConversationActivity_transport_insecure_sms">Insecure SMS</string>
|
||||
<string name="ConversationActivity_transport_insecure_mms">Insecure MMS</string>
|
||||
<string name="ConversationActivity_transport_signal">Signal</string>
|
||||
<string name="ConversationActivity_get_with_it">Get with it: %s</string>
|
||||
<string name="ConversationActivity_lets_use_this_to_chat">Let\'s use this to chat: %s</string>
|
||||
<string name="ConversationActivity_lets_switch_to_signal">Let\'s switch to Signal %1$s</string>
|
||||
<string name="ConversationActivity_lets_use_this_to_chat">Let\'s use this to chat: %1$s</string>
|
||||
<string name="ConversationActivity_error_leaving_group">Error leaving group...</string>
|
||||
<string name="ConversationActivity_mms_not_supported_title">MMS not supported</string>
|
||||
<string name="ConversationActivity_mms_not_supported_message">This message cannot be sent since your carrier doesn\'t support MMS.</string>
|
||||
@ -262,6 +262,20 @@
|
||||
<string name="ImportFragment_no_encrypted_backup_found">No encrypted backup found!</string>
|
||||
<string name="ImportFragment_restore_complete">Restore complete!</string>
|
||||
|
||||
<!-- InviteActivity -->
|
||||
<string name="InviteActivity_share">Share</string>
|
||||
<string name="InviteActivity_send_sms">Send SMS</string>
|
||||
<string name="InviteActivity_cancel">Cancel</string>
|
||||
<string name="InviteActivity_sending">Sending...</string>
|
||||
<string name="InviteActivity_heart_content_description">Heart</string>
|
||||
<string name="InviteActivity_invitations_sent">Invitations sent!</string>
|
||||
<string name="InviteActivity_invite_to_signal">Invite to Signal</string>
|
||||
<string name="InviteActivity_send_to_friends">SEND TO %1$s FRIEND(S)</string>
|
||||
<string name="InviteActivity_send_sms_invites">Send %1$s SMS invite(s)?</string>
|
||||
<string name="InviteActivity_lets_switch_to_signal">Let\'s switch to Signal: %1$s</string>
|
||||
<string name="InviteActivity_no_app_to_share_to">It looks like you don\'t have any apps to share to.</string>
|
||||
<string name="InviteActivity_friends_dont_let_friends_text_unencrypted">Friends don\'t let friends chat unencrypted.</string>
|
||||
|
||||
<!-- KeyScanningActivity -->
|
||||
<string name="KeyScanningActivity_no_scanned_key_found_exclamation">No scanned key found!</string>
|
||||
<string name="KeyScanningActivity_install_barcode_Scanner">Install Barcode Scanner?</string>
|
||||
@ -803,6 +817,7 @@
|
||||
<string name="AndroidManifest__media_overview_named">All images with %1$s</string>
|
||||
<string name="AndroidManifest__message_details">Message Details</string>
|
||||
<string name="AndroidManifest_manage_linked_devices">Manage linked devices</string>
|
||||
<string name="AndroidManifest__invite_friends">Invite friends</string>
|
||||
|
||||
<!-- arrays.xml -->
|
||||
<string name="arrays__import_export">Import / export</string>
|
||||
@ -1031,6 +1046,7 @@
|
||||
<string name="text_secure_normal__menu_settings">Settings</string>
|
||||
<string name="text_secure_normal__menu_clear_passphrase">Lock</string>
|
||||
<string name="text_secure_normal__mark_all_as_read">Mark all read</string>
|
||||
<string name="text_secure_normal__invite_friends">Invite friends</string>
|
||||
|
||||
<!-- reminder_header -->
|
||||
<string name="reminder_header_expired_build">Your build of Signal has expired!</string>
|
||||
@ -1048,6 +1064,9 @@
|
||||
<string name="reminder_header_invite_title">Invite to Signal</string>
|
||||
<string name="reminder_header_invite_text">Take your conversation with %1$s to the next level.</string>
|
||||
<string name="reminder_header_invite_button">INVITE</string>
|
||||
<string name="reminder_header_share_title">Invite your friends!</string>
|
||||
<string name="reminder_header_share_text">The more friends use Signal, the better it gets.</string>
|
||||
<string name="reminder_header_share_button">SHARE</string>
|
||||
<string name="reminder_header_close_button">CLOSE</string>
|
||||
|
||||
<!-- MediaPreviewActivity -->
|
||||
|
@ -44,6 +44,11 @@
|
||||
<item name="android:textColorSecondary">#BFffffff</item>
|
||||
</style>
|
||||
|
||||
<style name="TextSecure.FlatLightActionBar"
|
||||
parent="@style/TextSecure.LightActionBar">
|
||||
<item name="elevation">0dp</item>
|
||||
</style>
|
||||
|
||||
<style name="TextSecure.DarkActionBar.TabBar"
|
||||
parent="@style/Widget.AppCompat.ActionBar.TabBar">
|
||||
<item name="background">@color/gray95</item>
|
||||
|
@ -30,6 +30,13 @@
|
||||
<item name="contact_selection_header_text">#66eeeeee</item>
|
||||
</style>
|
||||
|
||||
<style name="TextSecure.HighlightTheme" parent="@style/TextSecure.LightTheme">
|
||||
<item name="actionBarStyle">@style/TextSecure.FlatLightActionBar</item>
|
||||
<item name="actionBarPopupTheme">@style/ThemeOverlay.AppCompat.Dark</item>
|
||||
<item name="android:windowBackground">@color/signal_primary</item>
|
||||
<item name="colorButtonNormal">@color/white</item>
|
||||
</style>
|
||||
|
||||
<style name="TextSecure.LightIntroTheme" parent="@style/Theme.AppCompat.Light">
|
||||
<!--<item name="colorPrimary">@android:color/transparent</item>-->
|
||||
<item name="actionBarStyle">@style/TextSecure.IntroActionBar</item>
|
||||
|
@ -1,8 +1,10 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
@ -65,4 +67,12 @@ public abstract class BaseActionBarActivity extends AppCompatActivity {
|
||||
.toBundle();
|
||||
ActivityCompat.startActivity(this, intent, bundle);
|
||||
}
|
||||
|
||||
@TargetApi(VERSION_CODES.LOLLIPOP)
|
||||
protected void setStatusBarColor(int color) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
getWindow().setStatusBarColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,28 +17,21 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.TouchDelegate;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import org.thoughtcrime.securesms.components.AnimatingToggle;
|
||||
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
|
||||
import org.thoughtcrime.securesms.components.ContactFilterToolbar.OnFilterChangedListener;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
@ -53,23 +46,15 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
|
||||
implements SwipeRefreshLayout.OnRefreshListener,
|
||||
ContactSelectionListFragment.OnContactSelectedListener
|
||||
{
|
||||
private static final String TAG = ContactSelectionActivity.class.getSimpleName();
|
||||
public final static String PUSH_ONLY_EXTRA = "push_only";
|
||||
private static final String TAG = ContactSelectionActivity.class.getSimpleName();
|
||||
|
||||
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
|
||||
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
||||
|
||||
protected ContactSelectionListFragment contactsFragment;
|
||||
|
||||
private MasterSecret masterSecret;
|
||||
private Toolbar toolbar;
|
||||
private EditText searchText;
|
||||
private AnimatingToggle toggle;
|
||||
protected ImageView action;
|
||||
private ImageView keyboardToggle;
|
||||
private ImageView dialpadToggle;
|
||||
private ImageView clearToggle;
|
||||
private LinearLayout toggleContainer;
|
||||
private MasterSecret masterSecret;
|
||||
private ContactFilterToolbar toolbar;
|
||||
|
||||
@Override
|
||||
protected void onPreCreate() {
|
||||
@ -78,9 +63,16 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle icicle, MasterSecret masterSecret) {
|
||||
setContentView(R.layout.contact_selection_activity);
|
||||
protected void onCreate(Bundle icicle, @NonNull MasterSecret masterSecret) {
|
||||
this.masterSecret = masterSecret;
|
||||
if (!getIntent().hasExtra(ContactSelectionListFragment.DISPLAY_MODE)) {
|
||||
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE,
|
||||
TextSecurePreferences.isSmsEnabled(this)
|
||||
? ContactSelectionListFragment.DISPLAY_MODE_ALL
|
||||
: ContactSelectionListFragment.DISPLAY_MODE_PUSH_ONLY);
|
||||
}
|
||||
|
||||
setContentView(R.layout.contact_selection_activity);
|
||||
|
||||
initializeToolbar();
|
||||
initializeResources();
|
||||
@ -94,8 +86,12 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
|
||||
dynamicLanguage.onResume(this);
|
||||
}
|
||||
|
||||
protected ContactFilterToolbar getToolbar() {
|
||||
return toolbar;
|
||||
}
|
||||
|
||||
private void initializeToolbar() {
|
||||
this.toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
this.toolbar = ViewUtil.findById(this, R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
|
||||
@ -103,69 +99,15 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
this.action = (ImageView) findViewById(R.id.action_icon);
|
||||
this.searchText = (EditText) findViewById(R.id.search_view);
|
||||
this.toggle = (AnimatingToggle) findViewById(R.id.button_toggle);
|
||||
this.keyboardToggle = (ImageView) findViewById(R.id.search_keyboard);
|
||||
this.dialpadToggle = (ImageView) findViewById(R.id.search_dialpad);
|
||||
this.clearToggle = (ImageView) findViewById(R.id.search_clear);
|
||||
this.toggleContainer = (LinearLayout) findViewById(R.id.toggle_container);
|
||||
|
||||
contactsFragment = (ContactSelectionListFragment) getSupportFragmentManager().findFragmentById(R.id.contact_selection_list_fragment);
|
||||
contactsFragment.setOnContactSelectedListener(this);
|
||||
contactsFragment.setOnRefreshListener(this);
|
||||
|
||||
this.keyboardToggle.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
searchText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PERSON_NAME);
|
||||
ServiceUtil.getInputMethodManager(ContactSelectionActivity.this).showSoftInput(searchText, 0);
|
||||
displayTogglingView(dialpadToggle);
|
||||
}
|
||||
});
|
||||
|
||||
this.dialpadToggle.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
searchText.setInputType(InputType.TYPE_CLASS_PHONE);
|
||||
ServiceUtil.getInputMethodManager(ContactSelectionActivity.this).showSoftInput(searchText, 0);
|
||||
displayTogglingView(keyboardToggle);
|
||||
}
|
||||
});
|
||||
|
||||
this.clearToggle.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
searchText.setText("");
|
||||
|
||||
if (SearchUtil.isTextInput(searchText)) displayTogglingView(dialpadToggle);
|
||||
else displayTogglingView(keyboardToggle);
|
||||
}
|
||||
});
|
||||
|
||||
expandTapArea(toolbar, action, 500);
|
||||
expandTapArea(toggleContainer, dialpadToggle, 500);
|
||||
}
|
||||
|
||||
private void initializeSearch() {
|
||||
this.searchText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (!SearchUtil.isEmpty(searchText)) displayTogglingView(clearToggle);
|
||||
else if (SearchUtil.isTextInput(searchText)) displayTogglingView(dialpadToggle);
|
||||
else if (SearchUtil.isPhoneInput(searchText)) displayTogglingView(keyboardToggle);
|
||||
|
||||
contactsFragment.setQueryFilter(searchText.getText().toString());
|
||||
toolbar.setOnFilterChangedListener(new OnFilterChangedListener() {
|
||||
@Override public void onFilterChanged(String filter) {
|
||||
contactsFragment.setQueryFilter(filter);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -178,42 +120,8 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
|
||||
@Override
|
||||
public void onContactSelected(String number) {}
|
||||
|
||||
private void displayTogglingView(View view) {
|
||||
toggle.display(view);
|
||||
expandTapArea(toggleContainer, view, 500);
|
||||
}
|
||||
|
||||
private void expandTapArea(final View container, final View child, final int padding) {
|
||||
container.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Rect rect = new Rect();
|
||||
child.getHitRect(rect);
|
||||
|
||||
rect.top -= padding;
|
||||
rect.left -= padding;
|
||||
rect.right += padding;
|
||||
rect.bottom += padding;
|
||||
|
||||
container.setTouchDelegate(new TouchDelegate(rect, child));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static class SearchUtil {
|
||||
|
||||
public static boolean isTextInput(EditText editText) {
|
||||
return (editText.getInputType() & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_TEXT;
|
||||
}
|
||||
|
||||
public static boolean isPhoneInput(EditText editText) {
|
||||
return (editText.getInputType() & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_PHONE;
|
||||
}
|
||||
|
||||
public static boolean isEmpty(EditText editText) {
|
||||
return editText.getText().length() <= 0;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onContactDeselected(String number) {}
|
||||
|
||||
private static class RefreshDirectoryTask extends AsyncTask<Context, Void, Void> {
|
||||
|
||||
@ -243,7 +151,7 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
|
||||
ContactSelectionActivity activity = this.activity.get();
|
||||
|
||||
if (activity != null && !activity.isFinishing()) {
|
||||
activity.searchText.setText("");
|
||||
activity.toolbar.clear();
|
||||
activity.contactsFragment.resetQueryFilter();
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ package org.thoughtcrime.securesms;
|
||||
import android.database.Cursor;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.Loader;
|
||||
@ -34,7 +35,6 @@ import android.widget.TextView;
|
||||
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter;
|
||||
import org.thoughtcrime.securesms.contacts.ContactSelectionListItem;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@ -53,6 +53,14 @@ public class ContactSelectionListFragment extends Fragment
|
||||
{
|
||||
private static final String TAG = ContactSelectionListFragment.class.getSimpleName();
|
||||
|
||||
public final static String DISPLAY_MODE = "display_mode";
|
||||
public final static String MULTI_SELECT = "multi_select";
|
||||
public final static String REFRESHABLE = "refreshable";
|
||||
|
||||
public final static int DISPLAY_MODE_ALL = ContactsCursorLoader.MODE_ALL;
|
||||
public final static int DISPLAY_MODE_PUSH_ONLY = ContactsCursorLoader.MODE_PUSH_ONLY;
|
||||
public final static int DISPLAY_MODE_OTHER_ONLY = ContactsCursorLoader.MODE_OTHER_ONLY;
|
||||
|
||||
private TextView emptyText;
|
||||
|
||||
private Map<Long, String> selectedContacts;
|
||||
@ -61,8 +69,6 @@ public class ContactSelectionListFragment extends Fragment
|
||||
private SwipeRefreshLayout swipeRefresh;
|
||||
private String cursorFilter;
|
||||
|
||||
private boolean multi = false;
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
@ -91,26 +97,27 @@ public class ContactSelectionListFragment extends Fragment
|
||||
listView.setDrawingListUnderStickyHeader(false);
|
||||
listView.setOnItemClickListener(new ListClickListener());
|
||||
|
||||
swipeRefresh.setEnabled(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN);
|
||||
swipeRefresh.setEnabled(getActivity().getIntent().getBooleanExtra(REFRESHABLE, true) &&
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
public List<String> getSelectedContacts() {
|
||||
if (selectedContacts == null) return null;
|
||||
|
||||
public @NonNull List<String> getSelectedContacts() {
|
||||
List<String> selected = new LinkedList<>();
|
||||
selected.addAll(selectedContacts.values());
|
||||
if (selectedContacts != null) {
|
||||
selected.addAll(selectedContacts.values());
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
public void setMultiSelect(boolean multi) {
|
||||
this.multi = multi;
|
||||
private boolean isMulti() {
|
||||
return getActivity().getIntent().getBooleanExtra(MULTI_SELECT, false);
|
||||
}
|
||||
|
||||
private void initializeCursor() {
|
||||
ContactSelectionListAdapter adapter = new ContactSelectionListAdapter(getActivity(), null, multi);
|
||||
ContactSelectionListAdapter adapter = new ContactSelectionListAdapter(getActivity(), null, isMulti());
|
||||
selectedContacts = adapter.getSelectedContacts();
|
||||
listView.setAdapter(adapter);
|
||||
this.getLoaderManager().initLoader(0, null, this);
|
||||
@ -126,12 +133,16 @@ public class ContactSelectionListFragment extends Fragment
|
||||
swipeRefresh.setRefreshing(false);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
selectedContacts.clear();
|
||||
getLoaderManager().restartLoader(0, null, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||
boolean pushOnly = getActivity().getIntent().getBooleanExtra(ContactSelectionActivity.PUSH_ONLY_EXTRA, false);
|
||||
boolean supportsSms = TextSecurePreferences.isSmsEnabled(getActivity());
|
||||
|
||||
return new ContactsCursorLoader(getActivity(), !pushOnly && supportsSms, cursorFilter);
|
||||
return new ContactsCursorLoader(getActivity(),
|
||||
getActivity().getIntent().getIntExtra(DISPLAY_MODE, DISPLAY_MODE_ALL),
|
||||
cursorFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -150,13 +161,14 @@ public class ContactSelectionListFragment extends Fragment
|
||||
public void onItemClick(AdapterView<?> l, View v, int position, long id) {
|
||||
ContactSelectionListItem contact = (ContactSelectionListItem)v;
|
||||
|
||||
if (!multi || !selectedContacts.containsKey(contact.getContactId())) {
|
||||
if (!isMulti() || !selectedContacts.containsKey(contact.getContactId())) {
|
||||
selectedContacts.put(contact.getContactId(), contact.getNumber());
|
||||
contact.setChecked(true);
|
||||
if (onContactSelectedListener != null) onContactSelectedListener.onContactSelected(contact.getNumber());
|
||||
} else {
|
||||
selectedContacts.remove(contact.getContactId());
|
||||
contact.setChecked(false);
|
||||
if (onContactSelectedListener != null) onContactSelectedListener.onContactDeselected(contact.getNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -170,6 +182,7 @@ public class ContactSelectionListFragment extends Fragment
|
||||
}
|
||||
|
||||
public interface OnContactSelectedListener {
|
||||
public void onContactSelected(String number);
|
||||
void onContactSelected(String number);
|
||||
void onContactDeselected(String number);
|
||||
}
|
||||
}
|
||||
|
@ -488,8 +488,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
private void handleInviteLink() {
|
||||
try {
|
||||
boolean a = SecureRandom.getInstance("SHA1PRNG").nextBoolean();
|
||||
if (a) composeText.appendInvite(getString(R.string.ConversationActivity_get_with_it, "http://sgnl.link/1IvurmD"));
|
||||
else composeText.appendInvite(getString(R.string.ConversationActivity_lets_use_this_to_chat, "http://sgnl.link/1CYCQQN"));
|
||||
if (a) composeText.appendInvite(getString(R.string.ConversationActivity_lets_switch_to_signal, "http://sgnl.link/1LoIMUl"));
|
||||
else composeText.appendInvite(getString(R.string.ConversationActivity_lets_use_this_to_chat, "http://sgnl.link/1MF56H1"));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
@ -1108,10 +1108,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
private void setActionBarColor(MaterialColor color) {
|
||||
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(color.toActionBarColor(this)));
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
getWindow().setStatusBarColor(color.toStatusBarColor(this));
|
||||
}
|
||||
setStatusBarColor(color.toStatusBarColor(this));
|
||||
}
|
||||
|
||||
private void setBlockedUserState(Recipients recipients) {
|
||||
|
@ -146,12 +146,13 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
|
||||
super.onOptionsItemSelected(item);
|
||||
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_new_group: createGroup(); return true;
|
||||
case R.id.menu_settings: handleDisplaySettings(); return true;
|
||||
case R.id.menu_clear_passphrase: handleClearPassphrase(); return true;
|
||||
case R.id.menu_mark_all_read: handleMarkAllRead(); return true;
|
||||
case R.id.menu_import_export: handleImportExport(); return true;
|
||||
case R.id.menu_my_identity: handleMyIdentity(); return true;
|
||||
case R.id.menu_new_group: createGroup(); return true;
|
||||
case R.id.menu_settings: handleDisplaySettings(); return true;
|
||||
case R.id.menu_clear_passphrase: handleClearPassphrase(); return true;
|
||||
case R.id.menu_mark_all_read: handleMarkAllRead(); return true;
|
||||
case R.id.menu_import_export: handleImportExport(); return true;
|
||||
case R.id.menu_my_identity: handleMyIdentity(); return true;
|
||||
case R.id.menu_invite: handleInvite(); return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -207,6 +208,10 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void handleInvite() {
|
||||
startActivity(new Intent(this, InviteActivity.class));
|
||||
}
|
||||
|
||||
private void initializeContactUpdatesReceiver() {
|
||||
observer = new ContentObserver(null) {
|
||||
@Override
|
||||
|
@ -52,6 +52,7 @@ import org.thoughtcrime.securesms.components.reminder.PushRegistrationReminder;
|
||||
import org.thoughtcrime.securesms.components.reminder.Reminder;
|
||||
import org.thoughtcrime.securesms.components.reminder.ReminderView;
|
||||
import org.thoughtcrime.securesms.components.reminder.ReminderView.OnDismissListener;
|
||||
import org.thoughtcrime.securesms.components.reminder.ShareReminder;
|
||||
import org.thoughtcrime.securesms.components.reminder.SystemSmsImportReminder;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
@ -149,6 +150,8 @@ public class ConversationListFragment extends Fragment
|
||||
return Optional.of((new SystemSmsImportReminder(context, masterSecret)));
|
||||
} else if (PushRegistrationReminder.isEligible(context)) {
|
||||
return Optional.of((new PushRegistrationReminder(context, masterSecret)));
|
||||
} else if (ShareReminder.isEligible(context)) {
|
||||
return Optional.of(new ShareReminder(context));
|
||||
} else {
|
||||
return Optional.absent();
|
||||
}
|
||||
|
@ -136,13 +136,6 @@ public class ExperienceUpgradeActivity extends BaseActionBarActivity {
|
||||
ServiceUtil.getNotificationManager(this).cancel(NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
@TargetApi(VERSION_CODES.LOLLIPOP)
|
||||
private void setStatusBarColor(int color) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
getWindow().setStatusBarColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
private void onContinue(Optional<ExperienceUpgrade> seenUpgrade) {
|
||||
ServiceUtil.getNotificationManager(this).cancel(NOTIFICATION_ID);
|
||||
int latestVersion = seenUpgrade.isPresent() ? seenUpgrade.get().getVersion()
|
||||
|
@ -50,6 +50,7 @@ import com.soundcloud.android.crop.Crop;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.attachments.UriAttachment;
|
||||
import org.thoughtcrime.securesms.components.PushRecipientsPanel;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
|
||||
import org.thoughtcrime.securesms.contacts.RecipientsEditor;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
@ -422,7 +423,8 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(GroupCreateActivity.this, PushContactSelectionActivity.class);
|
||||
if (existingContacts != null) intent.putExtra(PushContactSelectionActivity.PUSH_ONLY_EXTRA, true);
|
||||
if (existingContacts != null) intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE,
|
||||
ContactSelectionListFragment.DISPLAY_MODE_PUSH_ONLY);
|
||||
startActivityForResult(intent, PICK_CONTACT);
|
||||
}
|
||||
}
|
||||
|
244
src/org/thoughtcrime/securesms/InviteActivity.java
Normal file
@ -0,0 +1,244 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.AnimRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.view.animation.FastOutSlowInInterpolator;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewAnimationUtils;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver.OnPreDrawListener;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
|
||||
import org.thoughtcrime.securesms.components.ContactFilterToolbar.OnFilterChangedListener;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
|
||||
import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
public class InviteActivity extends PassphraseRequiredActionBarActivity implements ContactSelectionListFragment.OnContactSelectedListener {
|
||||
|
||||
private MasterSecret masterSecret;
|
||||
private ContactSelectionListFragment contactsFragment;
|
||||
private EditText inviteText;
|
||||
private View shareButton;
|
||||
private View smsButton;
|
||||
private ViewGroup smsSendFrame;
|
||||
private Button smsSendButton;
|
||||
private Button smsCancelButton;
|
||||
private Animation slideInAnimation;
|
||||
private Animation slideOutAnimation;
|
||||
private ContactFilterToolbar contactFilter;
|
||||
private ImageView heart;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState, @NonNull MasterSecret masterSecret) {
|
||||
this.masterSecret = masterSecret;
|
||||
|
||||
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, ContactSelectionListFragment.DISPLAY_MODE_OTHER_ONLY);
|
||||
getIntent().putExtra(ContactSelectionListFragment.MULTI_SELECT, true);
|
||||
getIntent().putExtra(ContactSelectionListFragment.REFRESHABLE, false);
|
||||
|
||||
super.onCreate(savedInstanceState, masterSecret);
|
||||
setContentView(R.layout.invite_activity);
|
||||
getSupportActionBar().setTitle(R.string.AndroidManifest__invite_friends);
|
||||
|
||||
initializeResources();
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
slideInAnimation = loadAnimation(R.anim.slide_from_bottom);
|
||||
slideOutAnimation = loadAnimation(R.anim.slide_to_bottom);
|
||||
shareButton = ViewUtil.findById(this, R.id.share_button);
|
||||
smsButton = ViewUtil.findById(this, R.id.sms_button);
|
||||
inviteText = ViewUtil.findById(this, R.id.invite_text);
|
||||
smsSendFrame = ViewUtil.findById(this, R.id.sms_send_frame);
|
||||
smsSendButton = ViewUtil.findById(this, R.id.send_sms_button);
|
||||
smsCancelButton = ViewUtil.findById(this, R.id.cancel_sms_button);
|
||||
contactFilter = ViewUtil.findById(this, R.id.contact_filter);
|
||||
heart = ViewUtil.findById(this, R.id.heart);
|
||||
contactsFragment = (ContactSelectionListFragment)getSupportFragmentManager().findFragmentById(R.id.contact_selection_list_fragment);
|
||||
|
||||
inviteText.setText(getString(R.string.InviteActivity_lets_switch_to_signal, "http://sgnl.link/1KpeYmF"));
|
||||
updateSmsButtonText();
|
||||
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
|
||||
heart.getViewTreeObserver().addOnPreDrawListener(new HeartPreDrawListener());
|
||||
}
|
||||
contactsFragment.setOnContactSelectedListener(this);
|
||||
shareButton.setOnClickListener(new ShareClickListener());
|
||||
smsButton.setOnClickListener(new SmsClickListener());
|
||||
smsCancelButton.setOnClickListener(new SmsCancelClickListener());
|
||||
smsSendButton.setOnClickListener(new SmsSendClickListener());
|
||||
contactFilter.setOnFilterChangedListener(new ContactFilterChangedListener());
|
||||
}
|
||||
|
||||
private Animation loadAnimation(@AnimRes int animResId) {
|
||||
final Animation animation = AnimationUtils.loadAnimation(this, animResId);
|
||||
animation.setInterpolator(new FastOutSlowInInterpolator());
|
||||
return animation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContactSelected(String number) {
|
||||
updateSmsButtonText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContactDeselected(String number) {
|
||||
updateSmsButtonText();
|
||||
}
|
||||
|
||||
private void sendSmsInvites() {
|
||||
new SendSmsInvitesAsyncTask(this, inviteText.getText().toString())
|
||||
.execute(contactsFragment.getSelectedContacts()
|
||||
.toArray(new String[contactsFragment.getSelectedContacts().size()]));
|
||||
}
|
||||
|
||||
private void updateSmsButtonText() {
|
||||
smsSendButton.setText(getString(R.string.InviteActivity_send_to_friends, contactsFragment.getSelectedContacts().size()));
|
||||
smsSendButton.setEnabled(!contactsFragment.getSelectedContacts().isEmpty());
|
||||
}
|
||||
|
||||
@Override public void onBackPressed() {
|
||||
if (smsSendFrame.getVisibility() == View.VISIBLE) {
|
||||
cancelSmsSelection();
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelSmsSelection() {
|
||||
contactsFragment.reset();
|
||||
updateSmsButtonText();
|
||||
ViewUtil.animateOut(smsSendFrame, slideOutAnimation);
|
||||
}
|
||||
|
||||
private class ShareClickListener implements OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent sendIntent = new Intent();
|
||||
sendIntent.setAction(Intent.ACTION_SEND);
|
||||
sendIntent.putExtra(Intent.EXTRA_TEXT, inviteText.getText().toString());
|
||||
sendIntent.setType("text/plain");
|
||||
if (sendIntent.resolveActivity(getPackageManager()) != null) {
|
||||
startActivity(Intent.createChooser(sendIntent, getString(R.string.InviteActivity_invite_to_signal)));
|
||||
} else {
|
||||
Toast.makeText(InviteActivity.this, R.string.InviteActivity_no_app_to_share_to, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SmsClickListener implements OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
ViewUtil.animateIn(smsSendFrame, slideInAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
private class SmsCancelClickListener implements OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
cancelSmsSelection();
|
||||
}
|
||||
}
|
||||
|
||||
private class SmsSendClickListener implements OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
new AlertDialog.Builder(InviteActivity.this)
|
||||
.setTitle(getString(R.string.InviteActivity_send_sms_invites, contactsFragment.getSelectedContacts().size()))
|
||||
.setMessage(inviteText.getText().toString())
|
||||
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
|
||||
@Override public void onClick(DialogInterface dialog, int which) {
|
||||
sendSmsInvites();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
|
||||
@Override public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
private class ContactFilterChangedListener implements OnFilterChangedListener {
|
||||
@Override
|
||||
public void onFilterChanged(String filter) {
|
||||
contactsFragment.setQueryFilter(filter);
|
||||
}
|
||||
}
|
||||
|
||||
private class HeartPreDrawListener implements OnPreDrawListener {
|
||||
@Override
|
||||
@TargetApi(VERSION_CODES.LOLLIPOP)
|
||||
public boolean onPreDraw() {
|
||||
heart.getViewTreeObserver().removeOnPreDrawListener(this);
|
||||
final int w = heart.getWidth();
|
||||
final int h = heart.getHeight();
|
||||
Animator reveal = ViewAnimationUtils.createCircularReveal(heart,
|
||||
w / 2, h,
|
||||
0, (float)Math.sqrt(h*h + (w*w/4)));
|
||||
reveal.setInterpolator(new FastOutSlowInInterpolator());
|
||||
reveal.setDuration(800);
|
||||
reveal.start();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private class SendSmsInvitesAsyncTask extends ProgressDialogAsyncTask<String,Void,Void> {
|
||||
private final String message;
|
||||
|
||||
public SendSmsInvitesAsyncTask(Context context, String message) {
|
||||
super(context, R.string.InviteActivity_sending, R.string.InviteActivity_sending);
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(String... numbers) {
|
||||
final Context context = getContext();
|
||||
if (context == null) return null;
|
||||
|
||||
for (String number : numbers) {
|
||||
final Recipients recipients = RecipientFactory.getRecipientsFromString(context, number, false);
|
||||
if (recipients != null && recipients.getPrimaryRecipient() != null) {
|
||||
MessageSender.send(context, masterSecret, new OutgoingTextMessage(recipients, message), -1L, true);
|
||||
if (recipients.getPrimaryRecipient().getContactUri() != null) {
|
||||
DatabaseFactory.getRecipientPreferenceDatabase(context).setSeenInviteReminder(recipients, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid) {
|
||||
super.onPostExecute(aVoid);
|
||||
final Context context = getContext();
|
||||
if (context == null) return;
|
||||
|
||||
ViewUtil.animateOut(smsSendFrame, slideOutAnimation);
|
||||
Toast.makeText(context, R.string.InviteActivity_invitations_sent, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}
|
@ -18,8 +18,8 @@ package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
@ -38,10 +38,10 @@ public class NewConversationActivity extends ContactSelectionActivity {
|
||||
private static final String TAG = NewConversationActivity.class.getSimpleName();
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle bundle, MasterSecret masterSecret) {
|
||||
public void onCreate(Bundle bundle, @NonNull MasterSecret masterSecret) {
|
||||
super.onCreate(bundle, masterSecret);
|
||||
|
||||
action.setVisibility(View.GONE);
|
||||
getToolbar().setShowCustomNavigationButton(false);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
|
@ -19,17 +19,9 @@ package org.thoughtcrime.securesms;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -46,14 +38,14 @@ public class PushContactSelectionActivity extends ContactSelectionActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle icicle, @NonNull MasterSecret masterSecret) {
|
||||
getIntent().putExtra(ContactSelectionListFragment.MULTI_SELECT, true);
|
||||
super.onCreate(icicle, masterSecret);
|
||||
contactsFragment.setMultiSelect(true);
|
||||
|
||||
action.setImageDrawable(getResources().getDrawable(R.drawable.ic_check_white_24dp));
|
||||
action.setOnClickListener(new View.OnClickListener() {
|
||||
getToolbar().setNavigationIcon(R.drawable.ic_check_white_24dp);
|
||||
getToolbar().setNavigationOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent resultIntent = getIntent();
|
||||
Intent resultIntent = getIntent();
|
||||
List<String> selectedContacts = contactsFragment.getSelectedContacts();
|
||||
|
||||
if (selectedContacts != null) {
|
||||
|
@ -0,0 +1,171 @@
|
||||
package org.thoughtcrime.securesms.components;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.TouchDelegate;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
public class ContactFilterToolbar extends Toolbar {
|
||||
private OnFilterChangedListener listener;
|
||||
|
||||
private EditText searchText;
|
||||
private AnimatingToggle toggle;
|
||||
private ImageView action;
|
||||
private ImageView keyboardToggle;
|
||||
private ImageView dialpadToggle;
|
||||
private ImageView clearToggle;
|
||||
private LinearLayout toggleContainer;
|
||||
|
||||
public ContactFilterToolbar(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ContactFilterToolbar(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, R.attr.toolbarStyle);
|
||||
}
|
||||
|
||||
public ContactFilterToolbar(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
inflate(context, R.layout.contact_filter_toolbar, this);
|
||||
|
||||
|
||||
this.action = ViewUtil.findById(this, R.id.action_icon);
|
||||
this.searchText = ViewUtil.findById(this, R.id.search_view);
|
||||
this.toggle = ViewUtil.findById(this, R.id.button_toggle);
|
||||
this.keyboardToggle = ViewUtil.findById(this, R.id.search_keyboard);
|
||||
this.dialpadToggle = ViewUtil.findById(this, R.id.search_dialpad);
|
||||
this.clearToggle = ViewUtil.findById(this, R.id.search_clear);
|
||||
this.toggleContainer = ViewUtil.findById(this, R.id.toggle_container);
|
||||
|
||||
this.keyboardToggle.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
searchText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PERSON_NAME);
|
||||
ServiceUtil.getInputMethodManager(getContext()).showSoftInput(searchText, 0);
|
||||
displayTogglingView(dialpadToggle);
|
||||
}
|
||||
});
|
||||
|
||||
this.dialpadToggle.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
searchText.setInputType(InputType.TYPE_CLASS_PHONE);
|
||||
ServiceUtil.getInputMethodManager(getContext()).showSoftInput(searchText, 0);
|
||||
displayTogglingView(keyboardToggle);
|
||||
}
|
||||
});
|
||||
|
||||
this.clearToggle.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
searchText.setText("");
|
||||
|
||||
if (SearchUtil.isTextInput(searchText)) displayTogglingView(dialpadToggle);
|
||||
else displayTogglingView(keyboardToggle);
|
||||
}
|
||||
});
|
||||
|
||||
this.searchText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (!SearchUtil.isEmpty(searchText)) displayTogglingView(clearToggle);
|
||||
else if (SearchUtil.isTextInput(searchText)) displayTogglingView(dialpadToggle);
|
||||
else if (SearchUtil.isPhoneInput(searchText)) displayTogglingView(keyboardToggle);
|
||||
notifyListener();
|
||||
}
|
||||
});
|
||||
expandTapArea(this, action, 500);
|
||||
expandTapArea(toggleContainer, dialpadToggle, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNavigationIcon(int resId) {
|
||||
action.setImageResource(resId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNavigationOnClickListener(OnClickListener listener) {
|
||||
super.setNavigationOnClickListener(listener);
|
||||
action.setOnClickListener(listener);
|
||||
}
|
||||
|
||||
public void setShowCustomNavigationButton(boolean show) {
|
||||
action.setVisibility(show ? VISIBLE : GONE);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
searchText.setText("");
|
||||
notifyListener();
|
||||
}
|
||||
|
||||
public void setOnFilterChangedListener(OnFilterChangedListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
private void notifyListener() {
|
||||
if (listener != null) listener.onFilterChanged(searchText.getText().toString());
|
||||
}
|
||||
|
||||
private void displayTogglingView(View view) {
|
||||
toggle.display(view);
|
||||
expandTapArea(toggleContainer, view, 500);
|
||||
}
|
||||
|
||||
private void expandTapArea(final View container, final View child, final int padding) {
|
||||
container.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Rect rect = new Rect();
|
||||
child.getHitRect(rect);
|
||||
|
||||
rect.top -= padding;
|
||||
rect.left -= padding;
|
||||
rect.right += padding;
|
||||
rect.bottom += padding;
|
||||
|
||||
container.setTouchDelegate(new TouchDelegate(rect, child));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static class SearchUtil {
|
||||
public static boolean isTextInput(EditText editText) {
|
||||
return (editText.getInputType() & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_TEXT;
|
||||
}
|
||||
|
||||
public static boolean isPhoneInput(EditText editText) {
|
||||
return (editText.getInputType() & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_PHONE;
|
||||
}
|
||||
|
||||
public static boolean isEmpty(EditText editText) {
|
||||
return editText.getText().length() <= 0;
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnFilterChangedListener {
|
||||
void onFilterChanged(String filter);
|
||||
}
|
||||
}
|
@ -54,18 +54,15 @@ public class ReminderView extends LinearLayout {
|
||||
|
||||
setOnClickListener(reminder.getOkListener());
|
||||
|
||||
if (reminder.isDismissable()) {
|
||||
closeButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
hide();
|
||||
if (reminder.getDismissListener() != null) reminder.getDismissListener().onClick(v);
|
||||
if (dismissListener != null) dismissListener.onDismiss();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
closeButton.setVisibility(View.GONE);
|
||||
}
|
||||
closeButton.setVisibility(reminder.isDismissable() ? View.VISIBLE : View.GONE);
|
||||
closeButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
hide();
|
||||
if (reminder.getDismissListener() != null) reminder.getDismissListener().onClick(v);
|
||||
if (dismissListener != null) dismissListener.onDismiss();
|
||||
}
|
||||
});
|
||||
|
||||
container.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
@ -0,0 +1,50 @@
|
||||
package org.thoughtcrime.securesms.components.reminder;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
|
||||
import org.thoughtcrime.securesms.InviteActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
public class ShareReminder extends Reminder {
|
||||
|
||||
public ShareReminder(final @NonNull Context context) {
|
||||
super(context.getString(R.string.reminder_header_share_title),
|
||||
context.getString(R.string.reminder_header_share_text));
|
||||
|
||||
setDismissListener(new OnClickListener() {
|
||||
@Override public void onClick(View v) {
|
||||
TextSecurePreferences.setPromptedShare(context, true);
|
||||
}
|
||||
});
|
||||
|
||||
setOkListener(new OnClickListener() {
|
||||
@Override public void onClick(View v) {
|
||||
TextSecurePreferences.setPromptedShare(context, true);
|
||||
context.startActivity(new Intent(context, InviteActivity.class));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean isEligible(final @NonNull Context context) {
|
||||
if (!TextSecurePreferences.isPushRegistered(context) ||
|
||||
TextSecurePreferences.hasPromptedShare(context))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = DatabaseFactory.getThreadDatabase(context).getConversationList();
|
||||
return cursor.getCount() >= 1;
|
||||
} finally {
|
||||
if (cursor != null) cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ import android.content.res.TypedArray;
|
||||
import android.database.Cursor;
|
||||
import android.provider.ContactsContract;
|
||||
import android.support.v4.widget.CursorAdapter;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@ -59,10 +60,6 @@ public class ContactSelectionListAdapter extends CursorAdapter
|
||||
this.multiSelect = multiSelect;
|
||||
}
|
||||
|
||||
public static class HeaderViewHolder {
|
||||
TextView text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||
return li.inflate(R.layout.contact_selection_list_item, parent, false);
|
||||
@ -90,27 +87,23 @@ public class ContactSelectionListAdapter extends CursorAdapter
|
||||
|
||||
@Override
|
||||
public View getHeaderView(int i, View convertView, ViewGroup viewGroup) {
|
||||
Cursor cursor = getCursor();
|
||||
|
||||
HeaderViewHolder holder;
|
||||
Cursor cursor = getCursor();
|
||||
|
||||
final TextView text;
|
||||
if (convertView == null) {
|
||||
holder = new HeaderViewHolder();
|
||||
convertView = li.inflate(R.layout.contact_selection_list_header, viewGroup, false);
|
||||
holder.text = (TextView) convertView.findViewById(R.id.text);
|
||||
convertView.setTag(holder);
|
||||
text = (TextView)li.inflate(R.layout.contact_selection_list_header, viewGroup, false);
|
||||
} else {
|
||||
holder = (HeaderViewHolder) convertView.getTag();
|
||||
text = (TextView)convertView;
|
||||
}
|
||||
|
||||
cursor.moveToPosition(i);
|
||||
|
||||
int contactType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.CONTACT_TYPE_COLUMN));
|
||||
|
||||
if (contactType == ContactsDatabase.PUSH_TYPE) holder.text.setText(R.string.contact_selection_list__header_signal_users);
|
||||
else holder.text.setText(R.string.contact_selection_list__header_other);
|
||||
if (contactType == ContactsDatabase.PUSH_TYPE) text.setText(R.string.contact_selection_list__header_signal_users);
|
||||
else text.setText(R.string.contact_selection_list__header_other);
|
||||
|
||||
return convertView;
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -18,12 +18,18 @@ package org.thoughtcrime.securesms.contacts;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.database.MergeCursor;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
||||
import org.thoughtcrime.securesms.util.DirectoryHelper.UserCapabilities.Capability;
|
||||
import org.thoughtcrime.securesms.util.NumberUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -37,14 +43,18 @@ public class ContactsCursorLoader extends CursorLoader {
|
||||
|
||||
private static final String TAG = ContactsCursorLoader.class.getSimpleName();
|
||||
|
||||
private final String filter;
|
||||
private boolean includeSmsContacts;
|
||||
public final static int MODE_ALL = 0;
|
||||
public final static int MODE_PUSH_ONLY = 1;
|
||||
public final static int MODE_OTHER_ONLY = 2;
|
||||
|
||||
public ContactsCursorLoader(Context context, boolean includeSmsContacts, String filter) {
|
||||
private final String filter;
|
||||
private final int mode;
|
||||
|
||||
public ContactsCursorLoader(Context context, int mode, String filter) {
|
||||
super(context);
|
||||
|
||||
this.filter = filter;
|
||||
this.includeSmsContacts = includeSmsContacts;
|
||||
this.filter = filter;
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -52,10 +62,14 @@ public class ContactsCursorLoader extends CursorLoader {
|
||||
ContactsDatabase contactsDatabase = DatabaseFactory.getContactsDatabase(getContext());
|
||||
ArrayList<Cursor> cursorList = new ArrayList<>(3);
|
||||
|
||||
cursorList.add(contactsDatabase.queryTextSecureContacts(filter));
|
||||
if (mode != MODE_OTHER_ONLY) {
|
||||
cursorList.add(contactsDatabase.queryTextSecureContacts(filter));
|
||||
}
|
||||
|
||||
if (includeSmsContacts) {
|
||||
if (mode == MODE_ALL) {
|
||||
cursorList.add(contactsDatabase.querySystemContacts(filter));
|
||||
} else if (mode == MODE_OTHER_ONLY) {
|
||||
cursorList.add(filterNonPushContacts(contactsDatabase.querySystemContacts(filter)));
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(filter) && NumberUtil.isValidSmsOrEmail(filter)) {
|
||||
@ -64,4 +78,35 @@ public class ContactsCursorLoader extends CursorLoader {
|
||||
|
||||
return new MergeCursor(cursorList.toArray(new Cursor[0]));
|
||||
}
|
||||
|
||||
private @NonNull Cursor filterNonPushContacts(@NonNull Cursor cursor) {
|
||||
try {
|
||||
final long startMillis = System.currentTimeMillis();
|
||||
final MatrixCursor matrix = new MatrixCursor(new String[]{ContactsDatabase.ID_COLUMN,
|
||||
ContactsDatabase.NAME_COLUMN,
|
||||
ContactsDatabase.NUMBER_COLUMN,
|
||||
ContactsDatabase.NUMBER_TYPE_COLUMN,
|
||||
ContactsDatabase.LABEL_COLUMN,
|
||||
ContactsDatabase.CONTACT_TYPE_COLUMN});
|
||||
while (cursor.moveToNext()) {
|
||||
final String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_COLUMN));
|
||||
final Recipients recipients = RecipientFactory.getRecipientsFromString(getContext(), number, true);
|
||||
|
||||
if (DirectoryHelper.getUserCapabilities(getContext(), recipients)
|
||||
.getTextCapability() != Capability.SUPPORTED)
|
||||
{
|
||||
matrix.addRow(new Object[]{cursor.getLong(cursor.getColumnIndexOrThrow(ContactsDatabase.ID_COLUMN)),
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NAME_COLUMN)),
|
||||
number,
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_TYPE_COLUMN)),
|
||||
cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.LABEL_COLUMN)),
|
||||
ContactsDatabase.NORMAL_TYPE});
|
||||
}
|
||||
}
|
||||
Log.w(TAG, "filterNonPushContacts() -> " + (System.currentTimeMillis() - startMillis) + "ms");
|
||||
return matrix;
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,11 +23,13 @@ import android.content.OperationApplicationException;
|
||||
import android.database.Cursor;
|
||||
import android.database.CursorWrapper;
|
||||
import android.database.MatrixCursor;
|
||||
import android.database.MergeCursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.BaseColumns;
|
||||
import android.provider.ContactsContract;
|
||||
import android.provider.ContactsContract.CommonDataKinds.Phone;
|
||||
import android.provider.ContactsContract.RawContacts;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
@ -36,6 +38,10 @@ import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.database.TextSecureDirectory;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
||||
import org.thoughtcrime.securesms.util.DirectoryHelper.UserCapabilities.Capability;
|
||||
import org.whispersystems.libaxolotl.util.guava.Optional;
|
||||
import org.whispersystems.textsecure.api.util.InvalidNumberException;
|
||||
import org.whispersystems.textsecure.api.util.PhoneNumberFormatter;
|
||||
@ -55,7 +61,9 @@ import java.util.Set;
|
||||
*/
|
||||
public class ContactsDatabase {
|
||||
|
||||
private static final String TAG = ContactsDatabase.class.getSimpleName();
|
||||
private static final String TAG = ContactsDatabase.class.getSimpleName();
|
||||
private static final String MIME = "vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.contact";
|
||||
private static final String SYNC = "__TS";
|
||||
|
||||
public static final String ID_COLUMN = "_id";
|
||||
public static final String NAME_COLUMN = "name";
|
||||
@ -154,12 +162,12 @@ public class ContactsDatabase {
|
||||
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
|
||||
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, e164number)
|
||||
.withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_OTHER)
|
||||
.withValue(ContactsContract.Data.SYNC2, "__TS")
|
||||
.withValue(ContactsContract.Data.SYNC2, SYNC)
|
||||
.build());
|
||||
|
||||
operations.add(ContentProviderOperation.newInsert(dataUri)
|
||||
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, index)
|
||||
.withValue(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.contact")
|
||||
.withValue(ContactsContract.Data.MIMETYPE, MIME)
|
||||
.withValue(ContactsContract.Data.DATA1, e164number)
|
||||
.withValue(ContactsContract.Data.DATA2, context.getString(R.string.app_name))
|
||||
.withValue(ContactsContract.Data.DATA3, context.getString(R.string.ContactsDatabase_message_s, e164number))
|
||||
@ -219,7 +227,7 @@ public class ContactsDatabase {
|
||||
Cursor cursor = context.getContentResolver().query(uri, projection,
|
||||
ContactsContract.Data.SYNC2 + " IS NULL OR " +
|
||||
ContactsContract.Data.SYNC2 + " != ?",
|
||||
new String[] {"__TS"},
|
||||
new String[] {SYNC},
|
||||
sort);
|
||||
|
||||
return new ProjectionMappingCursor(cursor, projectionMap,
|
||||
@ -245,13 +253,13 @@ public class ContactsDatabase {
|
||||
cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI,
|
||||
projection,
|
||||
ContactsContract.Data.MIMETYPE + " = ?",
|
||||
new String[] {"vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.contact"},
|
||||
new String[] {MIME},
|
||||
sort);
|
||||
} else {
|
||||
cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI,
|
||||
projection,
|
||||
ContactsContract.Data.MIMETYPE + " = ? AND (" + ContactsContract.Contacts.DISPLAY_NAME + " LIKE ? OR " + ContactsContract.Data.DATA1 + " LIKE ?)",
|
||||
new String[] {"vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.contact",
|
||||
new String[] {MIME,
|
||||
"%" + filter + "%", "%" + filter + "%"},
|
||||
sort);
|
||||
}
|
||||
|
@ -150,7 +150,9 @@ public class DirectoryHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public static UserCapabilities getUserCapabilities(Context context, Recipients recipients) {
|
||||
public static @NonNull UserCapabilities getUserCapabilities(@NonNull Context context,
|
||||
@Nullable Recipients recipients)
|
||||
{
|
||||
try {
|
||||
if (recipients == null) {
|
||||
return UserCapabilities.UNSUPPORTED;
|
||||
|
@ -14,7 +14,7 @@ public abstract class ProgressDialogAsyncTask<Params, Progress, Result> extends
|
||||
|
||||
public ProgressDialogAsyncTask(Context context, String title, String message) {
|
||||
super();
|
||||
this.contextReference = new WeakReference<Context>(context);
|
||||
this.contextReference = new WeakReference<>(context);
|
||||
this.title = title;
|
||||
this.message = message;
|
||||
}
|
||||
|
@ -68,6 +68,7 @@ public class TextSecurePreferences {
|
||||
private static final String GCM_PASSWORD_PREF = "pref_gcm_password";
|
||||
private static final String PROMPTED_PUSH_REGISTRATION_PREF = "pref_prompted_push_registration";
|
||||
private static final String PROMPTED_DEFAULT_SMS_PREF = "pref_prompted_default_sms";
|
||||
private static final String PROMPTED_SHARE_PREF = "pref_prompted_share";
|
||||
private static final String SIGNALING_KEY_PREF = "pref_signaling_key";
|
||||
private static final String DIRECTORY_FRESH_TIME_PREF = "pref_directory_refresh_time";
|
||||
private static final String IN_THREAD_NOTIFICATION_PREF = "pref_key_inthread_notifications";
|
||||
@ -423,6 +424,14 @@ public class TextSecurePreferences {
|
||||
setBooleanPreference(context, PROMPTED_DEFAULT_SMS_PREF, value);
|
||||
}
|
||||
|
||||
public static boolean hasPromptedShare(Context context) {
|
||||
return getBooleanPreference(context, PROMPTED_SHARE_PREF, false);
|
||||
}
|
||||
|
||||
public static void setPromptedShare(Context context, boolean value) {
|
||||
setBooleanPreference(context, PROMPTED_SHARE_PREF, value);
|
||||
}
|
||||
|
||||
public static boolean isInterceptAllMmsEnabled(Context context) {
|
||||
return getBooleanPreference(context, ALL_MMS_PREF, true);
|
||||
}
|
||||
|