ShareActivity, destruction of RecipientsPanel

// FREEBIE
master
Santoso Wijaya 2014-02-27 22:44:02 -08:00 committed by Jake McGinty
parent e2f7c1529a
commit 12dac6ccc3
23 changed files with 716 additions and 272 deletions

View File

@ -71,7 +71,7 @@
<data android:mimeType="image/*" />
<data android:mimeType="text/plain" />
<data android:mimeType="video/*" />
</intent-filter>
</intent-filter>
</activity>
@ -93,11 +93,18 @@
<activity android:name=".MmsPreferencesActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".ConversationListActivity"
android:label="@string/app_name"
<activity android:name=".ShareActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:noHistory="true"
android:windowSoftInputMode="stateHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".ConversationListActivity"
android:label="@string/app_name"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".ConversationActivity"
android:windowSoftInputMode="stateUnchanged"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
@ -126,7 +133,7 @@
<activity android:name=".PassphrasePromptActivity"
android:label="@string/AndroidManifest__enter_passphrase"
android:launchMode="singleTop"
android:theme="@style/NoAnimation.Theme.Sherlock.Light.DarkActionBar"
android:theme="@style/NoAnimation.Theme.Sherlock.Light.DarkActionBar"
android:windowSoftInputMode="stateUnchanged"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>

View File

@ -0,0 +1,13 @@
<?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="@color/conversation_list_item_background_read_light" />
</shape>
</item>
<item android:left="73dp" android:right="10dp">
<shape android:shape="rectangle">
<solid android:color="@color/conversation_list_divider_light" />
</shape>
</item>
</layer-list>

View File

@ -0,0 +1,13 @@
<?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="@color/conversation_list_item_background_read_dark" />
</shape>
</item>
<item android:left="73dp" android:right="10dp">
<shape android:shape="rectangle">
<solid android:color="@color/conversation_list_divider_dark" />
</shape>
</item>
</layer-list>

View File

@ -6,12 +6,6 @@
android:background="?conversation_background"
android:orientation="vertical">
<org.thoughtcrime.securesms.components.RecipientsPanel
android:id="@+id/recipients"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
<RelativeLayout
android:id="@+id/layout_container"
android:layout_width="fill_parent"

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:id="@+id/fragment_content"
android:name="org.thoughtcrime.securesms.ShareFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<ListView android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:drawSelectorOnTop="false"
android:scrollbarStyle="insideOverlay"
android:fadingEdgeLength="16dip"
android:divider="?share_list_item_divider"
android:dividerHeight="1px" />
</LinearLayout>

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.ShareListItem
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<org.thoughtcrime.securesms.components.ForegroundImageView
android:id="@+id/contact_photo_image"
android:foreground="@drawable/contact_photo_background"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:layout_marginTop="3dp"
android:layout_marginBottom="3dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:cropToPadding="true"
android:scaleType="centerCrop"
android:contentDescription="@string/SingleContactSelectionActivity_contact_photo" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dip"
android:layout_marginLeft="4dip"
android:layout_marginRight="8dip"
android:layout_marginBottom="4dip"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/contact_photo_image"
android:orientation="vertical">
<TextView android:id="@+id/from"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?attr/conversation_list_item_contact_color"
android:singleLine="true"
android:gravity="bottom"
android:ellipsize="marquee" />
</LinearLayout>
</org.thoughtcrime.securesms.ShareListItem>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="@string/text_secure_normal__menu_new_message"
android:id="@+id/menu_new_message"
android:icon="?attr/menu_new_conversation_icon"
android:showAsAction="always" />
</menu>

View File

@ -9,6 +9,8 @@
<attr name="conversation_list_item_date_color" format="reference|color"/>
<attr name="conversation_list_item_divider" format="reference"/>
<attr name="share_list_item_divider" format="reference"/>
<attr name="conversation_sent_card_background" format="reference|color"/>
<attr name="conversation_group_member_name" format="reference|color"/>
<attr name="conversation_sent_text_primary_color" format="reference|color"/>

View File

@ -84,13 +84,12 @@
<string name="ConversationActivity_sorry_the_selected_audio_exceeds_message_size_restrictions">Sorry, the selected audio exceeds message size restrictions.</string>
<string name="ConversationActivity_recipient_is_not_a_valid_sms_or_email_address_exclamation">Recipient is not a valid SMS or email address!</string>
<string name="ConversationActivity_message_is_empty_exclamation">Message is empty!</string>
<string name="ConversationActivity_forward_message_prefix">FWD</string>
<string name="ConversationActivity_group_conversation_recipients">Group Conversation Recipients</string>
<string name="ConversationActivity_group_conversation">Group Conversation</string>
<string name="ConversationActivity_unnamed_group">Unnamed Group</string>
<string name="ConversationActivity_d_recipients_in_group">%d members</string>
<string name="ConversationActivity_d_recipients_in_group_singular">1 member</string>
<string name="ConversationActivity_saving_draft">Saving draft...</string>
<string name="ConversationActivity_saved_draft">Saved draft</string>
<string name="ConversationActivity_invalid_recipient">Invalid recipient!</string>
<string name="ConversationActivity_calls_not_supported">Calls Not Supported</string>
<string name="ConversationActivity_this_device_does_not_appear_to_support_dial_actions">This device does not appear to support dial actions.</string>
@ -116,6 +115,9 @@
<!-- ConversationListItem -->
<string name="ConversationListItem_key_exchange_message">Key exchange message...</string>
<!-- ShareActivity -->
<string name="ShareActivity_share_with">Share with</string>
<!-- ExportFragment -->
<string name="ExportFragment_export_to_sd_card">Export To SD Card?</string>
<string name="ExportFragment_this_will_export_your_encrypted_keys_settings_and_messages">This

View File

@ -14,6 +14,8 @@
<item name="conversation_list_item_date_color">#ff999999</item>
<item name="conversation_list_item_divider">@drawable/conversation_list_divider_shape</item>
<item name="share_list_item_divider">@drawable/share_list_divider_shape</item>
<item name="actionbar_icon">@drawable/actionbar_icon_holo_light</item>
<item name="lower_right_divet">@drawable/divet_lower_right_dark</item>
@ -87,6 +89,8 @@
<item name="conversation_list_item_date_color">#ffdddddd</item>
<item name="conversation_list_item_divider">@drawable/conversation_list_divider_shape_dark</item>
<item name="share_list_item_divider">@drawable/share_list_divider_shape_dark</item>
<item name="conversation_group_member_name">#99ffffff</item>
<item name="conversation_sent_text_primary_color">#ffeeeeee</item>
<item name="conversation_sent_text_secondary_color">#44eeeeee</item>

View File

@ -58,7 +58,6 @@ import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.components.EmojiDrawer;
import org.thoughtcrime.securesms.components.EmojiToggle;
import org.thoughtcrime.securesms.components.RecipientsPanel;
import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
import org.thoughtcrime.securesms.crypto.KeyExchangeInitiator;
@ -139,21 +138,18 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
public static final String DRAFT_VIDEO_EXTRA = "draft_video";
public static final String DISTRIBUTION_TYPE_EXTRA = "distribution_type";
private static final int PICK_CONTACT = 1;
private static final int PICK_IMAGE = 2;
private static final int PICK_VIDEO = 3;
private static final int PICK_AUDIO = 4;
private static final int PICK_CONTACT_INFO = 5;
private static final int GROUP_EDIT = 6;
private static final int PICK_IMAGE = 1;
private static final int PICK_VIDEO = 2;
private static final int PICK_AUDIO = 3;
private static final int PICK_CONTACT_INFO = 4;
private static final int GROUP_EDIT = 5;
private static final int SEND_ATTRIBUTES[] = new int[]{R.attr.conversation_send_button_push,
R.attr.conversation_send_button_sms_secure,
R.attr.conversation_send_button_sms_insecure};
private MasterSecret masterSecret;
private RecipientsPanel recipientsPanel;
private EditText composeText;
private ImageButton addContactButton;
private ImageButton sendButton;
private TextView charactersLeft;
@ -200,7 +196,6 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
@Override
protected void onResume() {
initializeRecipientsInput();
super.onResume();
dynamicTheme.onResume(this);
@ -241,11 +236,6 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
if (data == null || resultCode != RESULT_OK) return;
switch (reqCode) {
case PICK_CONTACT:
Recipients recipients = data.getParcelableExtra("recipients");
if (recipients != null)
recipientsPanel.addRecipients(recipients);
break;
case PICK_IMAGE:
addAttachmentImage(data.getData());
break;
@ -723,12 +713,10 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
}
private void initializeResources() {
recipientsPanel = (RecipientsPanel)findViewById(R.id.recipients);
recipients = RecipientFactory.getRecipientsForIds(this, getIntent().getStringExtra(RECIPIENTS_EXTRA), true);
threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1);
distributionType = getIntent().getIntExtra(DISTRIBUTION_TYPE_EXTRA,
ThreadDatabase.DistributionTypes.DEFAULT);
addContactButton = (ImageButton)findViewById(R.id.contacts_button);
sendButton = (ImageButton)findViewById(R.id.send_button);
composeText = (EditText)findViewById(R.id.embedded_text_editor);
masterSecret = getIntent().getParcelableExtra(MASTER_SECRET_EXTRA);
@ -746,10 +734,8 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
SendButtonListener sendButtonListener = new SendButtonListener();
ComposeKeyPressedListener composeKeyPressedListener = new ComposeKeyPressedListener();
recipientsPanel.setPanelChangeListener(new RecipientsPanelChangeListener());
sendButton.setOnClickListener(sendButtonListener);
sendButton.setEnabled(true);
addContactButton.setOnClickListener(new AddRecipientButtonListener());
composeText.setOnKeyListener(composeKeyPressedListener);
composeText.addTextChangedListener(composeKeyPressedListener);
composeText.setOnEditorActionListener(sendButtonListener);
@ -769,23 +755,6 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && TextSecurePreferences.isScreenSecurityEnabled(this)) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
}
if (getIntent().getStringExtra("forwarded_message") != null) {
composeText.setText(getString(R.string.ConversationActivity_forward_message_prefix) + ": " +
getIntent().getStringExtra("forwarded_message"));
}
}
private void initializeRecipientsInput() {
if (recipients == null || recipients.isEmpty()) {
recipientsPanel.setVisibility(View.VISIBLE);
} else if (recipients != null) {
recipientsPanel.addRecipients(this.recipients);
} else {
InputMethodManager input = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
input.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, 0);
}
}
private void initializeReceivers() {
@ -945,9 +914,10 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
new AsyncTask<Void, Void, Void>() {
@Override
protected void onPreExecute() {
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
Toast.makeText(ConversationActivity.this,
R.string.ConversationActivity_saving_draft,
R.string.ConversationActivity_saved_draft,
Toast.LENGTH_SHORT).show();
}
@ -1004,13 +974,7 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
}
private Recipients getRecipients() {
try {
if (isExistingConversation()) return this.recipients;
else return recipientsPanel.getRecipients();
} catch (RecipientFormattingException rfe) {
Log.d(TAG, "Empty list of recipients retrieved from RecipientsPanel.");
return null;
}
return this.recipients;
}
private String getMessage() throws InvalidMessageException {
@ -1038,7 +1002,6 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
private void sendComplete(Recipients recipients, long threadId, boolean refreshFragment) {
attachmentManager.clear();
recipientsPanel.disable();
composeText.setText("");
this.recipients = recipients;
@ -1049,7 +1012,6 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
if (refreshFragment) {
fragment.reload(recipients, threadId);
this.recipientsPanel.setVisibility(View.GONE);
initializeTitleBar();
initializeSecurity();
}
@ -1114,14 +1076,6 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
// Listeners
private class AddRecipientButtonListener implements OnClickListener {
@Override
public void onClick(View v) {
Intent intent = new Intent(ConversationActivity.this, ContactSelectionActivity.class);
startActivityForResult(intent, PICK_CONTACT);
}
}
private class AttachmentTypeListener implements DialogInterface.OnClickListener {
@Override
public void onClick(DialogInterface dialog, int which) {
@ -1129,15 +1083,6 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
}
}
private class RecipientsPanelChangeListener implements RecipientsPanel.RecipientsPanelChangedListener {
@Override
public void onRecipientsPanelUpdate(Recipients recipients) {
initializeSecurity();
initializeTitleBar();
calculateCharactersRemaining();
}
}
private class EmojiToggleListener implements OnClickListener {
@Override
public void onClick(View v) {

View File

@ -184,9 +184,9 @@ public class ConversationFragment extends SherlockListFragment
}
private void handleForwardMessage(MessageRecord message) {
Intent composeIntent = new Intent(getActivity(), ConversationActivity.class);
composeIntent.putExtra("forwarded_message", message.getDisplayBody().toString());
composeIntent.putExtra("master_secret", masterSecret);
Intent composeIntent = new Intent(getActivity(), ShareActivity.class);
composeIntent.putExtra(ConversationActivity.DRAFT_TEXT_EXTRA, message.getDisplayBody().toString());
composeIntent.putExtra(ShareActivity.MASTER_SECRET_EXTRA, masterSecret);
startActivity(composeIntent);
}

View File

@ -64,7 +64,7 @@ public class ConversationListActivity extends PassphraseRequiredSherlockFragment
setContentView(R.layout.conversation_list_activity);
ActionBarUtil.initializeDefaultActionBar(this, getSupportActionBar(), "TextSecure");
ActionBarUtil.initializeDefaultActionBar(this, getSupportActionBar(), R.string.app_name);
initializeNavigationDrawer();
initializeSenderReceiverService();
@ -287,7 +287,7 @@ public class ConversationListActivity extends PassphraseRequiredSherlockFragment
this.drawerLayout = (DrawerLayout)findViewById(R.id.drawer_layout);
this.drawerList = (ListView)findViewById(R.id.left_drawer);
this.masterSecret = (MasterSecret)getIntent().getParcelableExtra("master_secret");
this.masterSecret = getIntent().getParcelableExtra("master_secret");
this.fragment = (ConversationListFragment)this.getSupportFragmentManager()
.findFragmentById(R.id.fragment_content);

View File

@ -1,3 +1,20 @@
/**
* 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;
import android.app.Activity;
@ -67,7 +84,11 @@ import ws.com.google.android.mms.MmsException;
import static org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
/**
* Activity to create and update groups
*
* @author Jake McGinty
*/
public class GroupCreateActivity extends PassphraseRequiredSherlockFragmentActivity {
private final static String TAG = GroupCreateActivity.class.getSimpleName();

View File

@ -19,16 +19,12 @@ package org.thoughtcrime.securesms;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import org.thoughtcrime.securesms.components.SingleRecipientPanel;
import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.ThreadDatabase;
@ -40,7 +36,6 @@ import org.thoughtcrime.securesms.util.ActionBarUtil;
import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.NumberUtil;
import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.crypto.MasterSecret;
@ -159,6 +154,10 @@ public class NewConversationActivity extends PassphraseRequiredSherlockFragmentA
Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.toIdString());
intent.putExtra(ConversationActivity.MASTER_SECRET_EXTRA, masterSecret);
intent.putExtra(ConversationActivity.DRAFT_TEXT_EXTRA, getIntent().getStringExtra(ConversationActivity.DRAFT_TEXT_EXTRA));
intent.putExtra(ConversationActivity.DRAFT_AUDIO_EXTRA, getIntent().getParcelableExtra(ConversationActivity.DRAFT_AUDIO_EXTRA));
intent.putExtra(ConversationActivity.DRAFT_VIDEO_EXTRA, getIntent().getParcelableExtra(ConversationActivity.DRAFT_VIDEO_EXTRA));
intent.putExtra(ConversationActivity.DRAFT_IMAGE_EXTRA, getIntent().getParcelableExtra(ConversationActivity.DRAFT_IMAGE_EXTRA));
long existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipients);
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, existingThread);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);

View File

@ -2,6 +2,7 @@ package org.thoughtcrime.securesms;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
@ -126,16 +127,25 @@ public class RoutingActivity extends PassphraseRequiredSherlockActivity {
}
private void handleDisplayConversationOrList() {
ConversationParameters parameters = getConversationParameters();
final ConversationParameters parameters = getConversationParameters();
Intent intent;
if (isShareAction() || parameters.recipients != null) {
final Intent intent;
if (isShareAction()) {
intent = getShareIntent(parameters);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
}
} else if (parameters.recipients != null) {
intent = getConversationIntent(parameters);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
} else {
intent = getConversationListIntent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
}
}
startActivity(intent);
finish();
}
@ -153,6 +163,20 @@ public class RoutingActivity extends PassphraseRequiredSherlockActivity {
return intent;
}
private Intent getShareIntent(ConversationParameters parameters) {
Intent intent = new Intent(this, ShareActivity.class);
intent.putExtra("master_secret", masterSecret);
if (parameters != null) {
intent.putExtra(ConversationActivity.DRAFT_TEXT_EXTRA, parameters.draftText);
intent.putExtra(ConversationActivity.DRAFT_IMAGE_EXTRA, parameters.draftImage);
intent.putExtra(ConversationActivity.DRAFT_AUDIO_EXTRA, parameters.draftAudio);
intent.putExtra(ConversationActivity.DRAFT_VIDEO_EXTRA, parameters.draftVideo);
}
return intent;
}
private Intent getConversationListIntent() {
Intent intent = new Intent(this, ConversationListActivity.class);
intent.putExtra("master_secret", masterSecret);
@ -197,8 +221,8 @@ public class RoutingActivity extends PassphraseRequiredSherlockActivity {
}
private ConversationParameters getConversationParametersForSendAction() {
Recipients recipients = null;
long threadId = getIntent().getLongExtra("thread_id", -1);
Recipients recipients;
long threadId = getIntent().getLongExtra("thread_id", -1);
try {
String data = getIntent().getData().getSchemeSpecificPart();
@ -220,11 +244,11 @@ public class RoutingActivity extends PassphraseRequiredSherlockActivity {
if ("text/plain".equals(type)) {
draftText = getIntent().getStringExtra(Intent.EXTRA_TEXT);
} else if (type.startsWith("image/")) {
} else if (type != null && type.startsWith("image/")) {
draftImage = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
} else if (type.startsWith("audio/")) {
} else if (type != null && type.startsWith("audio/")) {
draftAudio = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
} else if (type.startsWith("video/")) {
} else if (type != null && type.startsWith("video/")) {
draftVideo = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
}

View File

@ -0,0 +1,173 @@
/**
* 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;
import android.content.Intent;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ListView;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.DirectoryRefreshListener;
import org.thoughtcrime.securesms.util.ActionBarUtil;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.MemoryCleaner;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.crypto.MasterSecret;
/**
* An activity to quickly share content with contacts
*
* @author Jake McGinty
*/
public class ShareActivity extends PassphraseRequiredSherlockFragmentActivity
implements ShareFragment.ConversationSelectedListener
{
public final static String MASTER_SECRET_EXTRA = "master_secret";
private final DynamicTheme dynamicTheme = new DynamicTheme ();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private ShareFragment fragment;
private MasterSecret masterSecret;
@Override
public void onCreate(Bundle icicle) {
dynamicTheme.onCreate(this);
dynamicLanguage.onCreate(this);
super.onCreate(icicle);
setContentView(R.layout.share_activity);
ActionBarUtil.initializeDefaultActionBar(this, getSupportActionBar(), R.string.ShareActivity_share_with);
initializeResources();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
@Override
public void onResume() {
super.onResume();
dynamicTheme.onResume(this);
dynamicLanguage.onResume(this);
}
@Override
public void onPause() {
super.onPause();
if (!isFinishing()) finish();
}
@Override
public void onDestroy() {
MemoryCleaner.clean(masterSecret);
super.onDestroy();
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuInflater inflater = this.getSupportMenuInflater();
menu.clear();
inflater.inflate(R.menu.share, menu);
super.onPrepareOptionsMenu(menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case R.id.menu_new_message: handleNewConversation(); return true;
case android.R.id.home: finish(); return true;
}
return false;
}
@Override
public void onMasterSecretCleared() {
startActivity(new Intent(this, RoutingActivity.class));
super.onMasterSecretCleared();
}
private void handleNewConversation() {
Intent intent = getBaseShareIntent(NewConversationActivity.class);
startActivity(intent);
}
@Override
public void onCreateConversation(long threadId, Recipients recipients, int distributionType) {
createConversation(threadId, recipients, distributionType);
}
private void createConversation(long threadId, Recipients recipients, int distributionType) {
final Intent intent = getBaseShareIntent(ConversationActivity.class);
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.toIdString());
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);
startActivity(intent);
}
private void initializeResources() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && TextSecurePreferences.isScreenSecurityEnabled(this)) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE);
}
this.masterSecret = getIntent().getParcelableExtra(MASTER_SECRET_EXTRA);
this.fragment = (ShareFragment)this.getSupportFragmentManager()
.findFragmentById(R.id.fragment_content);
this.fragment.setMasterSecret(masterSecret);
}
private Intent getBaseShareIntent(final Class<?> target) {
final Intent intent = new Intent(this, target);
final Intent originalIntent = getIntent();
final String draftText = originalIntent.getStringExtra(ConversationActivity.DRAFT_TEXT_EXTRA);
final Uri draftImage = originalIntent.getParcelableExtra(ConversationActivity.DRAFT_IMAGE_EXTRA);
final Uri draftAudio = originalIntent.getParcelableExtra(ConversationActivity.DRAFT_AUDIO_EXTRA);
final Uri draftVideo = originalIntent.getParcelableExtra(ConversationActivity.DRAFT_VIDEO_EXTRA);
intent.putExtra(ConversationActivity.DRAFT_TEXT_EXTRA, draftText);
intent.putExtra(ConversationActivity.DRAFT_IMAGE_EXTRA, draftImage);
intent.putExtra(ConversationActivity.DRAFT_AUDIO_EXTRA, draftAudio);
intent.putExtra(ConversationActivity.DRAFT_VIDEO_EXTRA, draftVideo);
intent.putExtra(NewConversationActivity.MASTER_SECRET_EXTRA, masterSecret);
return intent;
}
}

View File

@ -0,0 +1,110 @@
/**
* 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;
import android.app.Activity;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import com.actionbarsherlock.app.SherlockListFragment;
import org.thoughtcrime.securesms.database.loaders.ConversationListLoader;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.whispersystems.textsecure.crypto.MasterSecret;
/**
* A fragment to select and share to open conversations
*
* @author Jake McGinty
*/
public class ShareFragment extends SherlockListFragment implements LoaderManager.LoaderCallbacks<Cursor> {
private ConversationSelectedListener listener;
private MasterSecret masterSecret;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
return inflater.inflate(R.layout.share_fragment, container, false);
}
@Override
public void onActivityCreated(Bundle bundle) {
super.onActivityCreated(bundle);
initializeListAdapter();
getLoaderManager().initLoader(0, null, this);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.listener = (ConversationSelectedListener) activity;
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
if (v instanceof ShareListItem) {
ShareListItem headerView = (ShareListItem) v;
handleCreateConversation(headerView.getThreadId(), headerView.getRecipients(),
headerView.getDistributionType());
}
}
public void setMasterSecret(MasterSecret masterSecret) {
if (this.masterSecret != masterSecret) {
this.masterSecret = masterSecret;
initializeListAdapter();
}
}
private void initializeListAdapter() {
this.setListAdapter(new ShareListAdapter(getActivity(), null, masterSecret));
getListView().setRecyclerListener((ShareListAdapter) getListAdapter());
getLoaderManager().restartLoader(0, null, this);
}
private void handleCreateConversation(long threadId, Recipients recipients, int distributionType) {
listener.onCreateConversation(threadId, recipients, distributionType);
}
@Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
return new ConversationListLoader(getActivity(), null);
}
@Override
public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor) {
((CursorAdapter)getListAdapter()).changeCursor(cursor);
}
@Override
public void onLoaderReset(Loader<Cursor> arg0) {
((CursorAdapter)getListAdapter()).changeCursor(null);
}
public interface ConversationSelectedListener {
public void onCreateConversation(long threadId, Recipients recipients, int distributionType);
}
}

View File

@ -0,0 +1,75 @@
/**
* 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;
import android.content.Context;
import android.database.Cursor;
import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.whispersystems.textsecure.crypto.MasterCipher;
import org.whispersystems.textsecure.crypto.MasterSecret;
/**
* A CursorAdapter for building a list of open conversations
*
* @author Jake McGinty
*/
public class ShareListAdapter extends CursorAdapter implements AbsListView.RecyclerListener {
private final ThreadDatabase threadDatabase;
private final MasterCipher masterCipher;
private final Context context;
private final LayoutInflater inflater;
public ShareListAdapter(Context context, Cursor cursor, MasterSecret masterSecret) {
super(context, cursor, 0);
if (masterSecret != null) this.masterCipher = new MasterCipher(masterSecret);
else this.masterCipher = null;
this.context = context;
this.threadDatabase = DatabaseFactory.getThreadDatabase(context);
this.inflater = LayoutInflater.from(context);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return inflater.inflate(R.layout.share_list_item_view, parent, false);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
if (masterCipher != null) {
ThreadDatabase.Reader reader = threadDatabase.readerFor(cursor, masterCipher);
ThreadRecord record = reader.getCurrent();
((ShareListItem)view).set(record);
}
}
@Override
public void onMovedToScrapHeap(View view) {
((ShareListItem)view).unbind();
}
}

View File

@ -0,0 +1,155 @@
/**
* 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;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.provider.Contacts.Intents;
import android.provider.ContactsContract.QuickContact;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.StyleSpan;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.Emoji;
import java.util.Set;
/**
* A simple view to show the recipients of an open conversation
*
* @author Jake McGinty
*/
public class ShareListItem extends RelativeLayout
implements Recipient.RecipientModifiedListener
{
private final static String TAG = ShareListItem.class.getSimpleName();
private Context context;
private Recipients recipients;
private long threadId;
private TextView fromView;
private ImageView contactPhotoImage;
private final Handler handler = new Handler();
private int distributionType;
public ShareListItem(Context context) {
super(context);
this.context = context;
}
public ShareListItem(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
@Override
protected void onFinishInflate() {
this.fromView = (TextView) findViewById(R.id.from);
this.contactPhotoImage = (ImageView) findViewById(R.id.contact_photo_image);
}
public void set(ThreadRecord thread) {
this.recipients = thread.getRecipients();
this.threadId = thread.getThreadId();
this.distributionType = thread.getDistributionType();
this.recipients.addListener(this);
this.fromView.setText(formatFrom(recipients));
setBackground();
setContactPhoto(this.recipients.getPrimaryRecipient());
}
public void unbind() {
if (this.recipients != null) this.recipients.removeListener(this);
}
private void setContactPhoto(final Recipient recipient) {
if (recipient == null) return;
contactPhotoImage.setImageBitmap(BitmapUtil.getCircleCroppedBitmap(recipient.getContactPhoto()));
}
private void setBackground() {
int[] attributes = new int[]{R.attr.conversation_list_item_background_read};
TypedArray drawables = context.obtainStyledAttributes(attributes);
setBackgroundDrawable(drawables.getDrawable(0));
drawables.recycle();
}
private CharSequence formatFrom(Recipients from) {
final String fromString;
final boolean isUnnamedGroup = from.isGroupRecipient() && TextUtils.isEmpty(from.getPrimaryRecipient().getName());
if (isUnnamedGroup) {
fromString = context.getString(R.string.ConversationActivity_unnamed_group);
} else {
fromString = from.toShortString();
}
SpannableStringBuilder builder = new SpannableStringBuilder(fromString);
final int typeface;
if (isUnnamedGroup) typeface = Typeface.ITALIC;
else typeface = Typeface.NORMAL;
builder.setSpan(new StyleSpan(typeface), 0, builder.length(),
Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
return builder;
}
public Recipients getRecipients() {
return recipients;
}
public long getThreadId() {
return threadId;
}
public int getDistributionType() {
return distributionType;
}
@Override
public void onModified(Recipient recipient) {
handler.post(new Runnable() {
@Override
public void run() {
fromView.setText(formatFrom(recipients));
setContactPhoto(recipients.getPrimaryRecipient());
}
});
}
}

View File

@ -1,172 +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.components;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.RelativeLayout;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.contacts.RecipientsAdapter;
import org.thoughtcrime.securesms.contacts.RecipientsEditor;
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 java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* Panel component combining both an editable field with a button for
* a list-based contact selector.
*
* @author Moxie Marlinspike
*/
public class RecipientsPanel extends RelativeLayout {
private RecipientsPanelChangedListener panelChangeListener;
private RecipientsEditor recipientsText;
private View panel;
private static final int RECIPIENTS_MAX_LENGTH = 312;
public RecipientsPanel(Context context) {
super(context);
initialize();
}
public RecipientsPanel(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public RecipientsPanel(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize();
}
public void addRecipient(String name, String number) {
if (name != null) recipientsText.append(sanitizeRecipientName(name) + "< " + number + ">, ");
else recipientsText.append(number + ", ");
}
public void addContacts(List<ContactAccessor.ContactData> contacts) {
for (ContactAccessor.ContactData contact : contacts) {
for (ContactAccessor.NumberData number : contact.numbers) {
addRecipient(contact.name, number.number);
}
}
}
public void addRecipients(Recipients recipients) {
Set<Recipient> panelRecipients;
try {
panelRecipients = new HashSet<Recipient>(getRecipients().getRecipientsList());
} catch (RecipientFormattingException e) {
Log.w("RecipientsPanel", e);
panelRecipients = new HashSet<Recipient>();
}
List<Recipient> recipientList = recipients.getRecipientsList();
Iterator<Recipient> iterator = recipientList.iterator();
while (iterator.hasNext()) {
Recipient recipient = iterator.next();
if (!panelRecipients.contains(recipient)) {
addRecipient(recipient.getName(), recipient.getNumber());
}
}
}
public Recipients getRecipients() throws RecipientFormattingException {
String rawText = recipientsText.getText().toString();
Recipients recipients = RecipientFactory.getRecipientsFromString(getContext(), rawText, false);
if (recipients.isEmpty())
throw new RecipientFormattingException("Recipient List Is Empty!");
return recipients;
}
public void disable() {
recipientsText.setText("");
panel.setVisibility(View.GONE);
}
public void setPanelChangeListener(RecipientsPanelChangedListener panelChangeListener) {
this.panelChangeListener = panelChangeListener;
}
private void initialize() {
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.recipients_panel, this, true);
View imageButton = findViewById(R.id.contacts_button);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
((MarginLayoutParams) imageButton.getLayoutParams()).topMargin = 0;
panel = findViewById(R.id.recipients_panel);
initRecipientsEditor();
}
private void initRecipientsEditor() {
Recipients recipients = null;
recipientsText = (RecipientsEditor)findViewById(R.id.recipients_text);
try {
recipients = getRecipients();
} catch (RecipientFormattingException e) {
recipients = new Recipients( new LinkedList<Recipient>() );
}
recipientsText.setAdapter(new RecipientsAdapter(this.getContext()));
recipientsText.populate(recipients);
recipientsText.setOnFocusChangeListener(new FocusChangedListener());
}
private static String sanitizeRecipientName(String name) {
return name.replaceAll("[,<>]", "");
}
private class FocusChangedListener implements View.OnFocusChangeListener {
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus && (panelChangeListener != null)) {
try {
panelChangeListener.onRecipientsPanelUpdate(getRecipients());
} catch (RecipientFormattingException rfe) {
panelChangeListener.onRecipientsPanelUpdate(null);
}
}
}
}
public interface RecipientsPanelChangedListener {
public void onRecipientsPanelUpdate(Recipients recipients);
}
}

View File

@ -16,8 +16,6 @@
*/
package org.thoughtcrime.securesms.util;
import android.util.Log;
import org.thoughtcrime.securesms.sms.SmsTransportDetails;
public class EncryptedCharacterCalculator extends CharacterCalculator {
@ -37,7 +35,6 @@ public class EncryptedCharacterCalculator extends CharacterCalculator {
spilloverMessagesSpent++;
int charactersRemaining = (SmsTransportDetails.MULTI_MESSAGE_MAX_BYTES * spilloverMessagesSpent) - spillover;
Log.w("EncryptedCharacterCalculator", "charactersRemaining: " + charactersRemaining);
return new CharacterState(spilloverMessagesSpent+1, charactersRemaining, SmsTransportDetails.MULTI_MESSAGE_MAX_BYTES);
}