Suppport receiving 'share' actions from other apps.

This commit is contained in:
Moxie Marlinspike 2013-02-02 20:37:40 -08:00
parent 6bdb0e2d66
commit 288e2b5572
3 changed files with 89 additions and 36 deletions

View File

@ -53,6 +53,14 @@
<data android:scheme="smsto" /> <data android:scheme="smsto" />
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="audio/*" />
<data android:mimeType="image/*" />
<data android:mimeType="text/*" />
</intent-filter>
</activity> </activity>
<activity android:name=".ConversationActivity" <activity android:name=".ConversationActivity"

View File

@ -44,11 +44,6 @@ import android.widget.ImageButton;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import org.thoughtcrime.securesms.components.RecipientsPanel; import org.thoughtcrime.securesms.components.RecipientsPanel;
import org.thoughtcrime.securesms.crypto.AuthenticityCalculator; import org.thoughtcrime.securesms.crypto.AuthenticityCalculator;
import org.thoughtcrime.securesms.crypto.KeyExchangeInitiator; import org.thoughtcrime.securesms.crypto.KeyExchangeInitiator;
@ -74,6 +69,11 @@ import org.thoughtcrime.securesms.util.MemoryCleaner;
import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.MmsException;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import java.io.IOException; import java.io.IOException;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -89,6 +89,13 @@ public class ConversationActivity extends SherlockFragmentActivity
implements ConversationFragment.ConversationFragmentListener implements ConversationFragment.ConversationFragmentListener
{ {
public static final String RECIPIENTS_EXTRA = "recipients";
public static final String THREAD_ID_EXTRA = "thread_id";
public static final String MASTER_SECRET_EXTRA = "master_secret";
public static final String DRAFT_TEXT_EXTRA = "draft_text";
public static final String DRAFT_IMAGE_EXTRA = "draft_image";
public static final String DRAFT_AUDIO_EXTRA = "draft_audio";
private static final int PICK_CONTACT = 1; private static final int PICK_CONTACT = 1;
private static final int PICK_IMAGE = 2; private static final int PICK_IMAGE = 2;
private static final int PICK_VIDEO = 3; private static final int PICK_VIDEO = 3;
@ -121,6 +128,7 @@ public class ConversationActivity extends SherlockFragmentActivity
initializeReceivers(); initializeReceivers();
initializeResources(); initializeResources();
initializeDraft();
initializeTitleBar(); initializeTitleBar();
} }
@ -396,6 +404,16 @@ public class ConversationActivity extends SherlockFragmentActivity
this.invalidateOptionsMenu(); this.invalidateOptionsMenu();
} }
private void initializeDraft() {
String draftText = getIntent().getStringExtra(DRAFT_TEXT_EXTRA);
Uri draftImage = getIntent().getParcelableExtra(DRAFT_IMAGE_EXTRA);
Uri draftAudio = getIntent().getParcelableExtra(DRAFT_AUDIO_EXTRA);
if (draftText != null) composeText.setText(draftText);
if (draftImage != null) addAttachmentImage(draftImage);
if (draftAudio != null) addAttachmentAudio(draftAudio);
}
private void initializeSecurity() { private void initializeSecurity() {
if (isSingleConversation() && if (isSingleConversation() &&
KeyUtil.isSessionFor(this, getRecipients().getPrimaryRecipient())) KeyUtil.isSessionFor(this, getRecipients().getPrimaryRecipient()))
@ -663,6 +681,7 @@ public class ConversationActivity extends SherlockFragmentActivity
// Listeners // Listeners
private class AddRecipientButtonListener implements OnClickListener { private class AddRecipientButtonListener implements OnClickListener {
@Override
public void onClick(View v) { public void onClick(View v) {
Intent intent = new Intent(ConversationActivity.this, ContactSelectionActivity.class); Intent intent = new Intent(ConversationActivity.this, ContactSelectionActivity.class);
startActivityForResult(intent, PICK_CONTACT); startActivityForResult(intent, PICK_CONTACT);
@ -670,12 +689,14 @@ public class ConversationActivity extends SherlockFragmentActivity
}; };
private class AttachmentTypeListener implements DialogInterface.OnClickListener { private class AttachmentTypeListener implements DialogInterface.OnClickListener {
@Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
addAttachment(attachmentAdapter.buttonToCommand(which)); addAttachment(attachmentAdapter.buttonToCommand(which));
} }
} }
private class RecipientsPanelChangeListener implements RecipientsPanel.RecipientsPanelChangedListener { private class RecipientsPanelChangeListener implements RecipientsPanel.RecipientsPanelChangedListener {
@Override
public void onRecipientsPanelUpdate(Recipients recipients) { public void onRecipientsPanelUpdate(Recipients recipients) {
initializeSecurity(); initializeSecurity();
initializeTitleBar(); initializeTitleBar();
@ -684,10 +705,12 @@ public class ConversationActivity extends SherlockFragmentActivity
} }
private class SendButtonListener implements OnClickListener, TextView.OnEditorActionListener { private class SendButtonListener implements OnClickListener, TextView.OnEditorActionListener {
@Override
public void onClick(View v) { public void onClick(View v) {
sendMessage(false); sendMessage(false);
} }
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEND) { if (actionId == EditorInfo.IME_ACTION_SEND) {
sendButton.performClick(); sendButton.performClick();
@ -699,15 +722,19 @@ public class ConversationActivity extends SherlockFragmentActivity
}; };
private class OnTextChangedListener implements TextWatcher { private class OnTextChangedListener implements TextWatcher {
@Override
public void afterTextChanged(Editable s) { public void afterTextChanged(Editable s) {
calculateCharactersRemaining(); calculateCharactersRemaining();
} }
@Override
public void beforeTextChanged(CharSequence s, int start, int count,int after) {} public void beforeTextChanged(CharSequence s, int start, int count,int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before,int count) {} public void onTextChanged(CharSequence s, int start, int before,int count) {}
} }
private class ComposeKeyPressedListener implements OnKeyListener { private class ComposeKeyPressedListener implements OnKeyListener {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) { public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) { if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_ENTER) { if (keyCode == KeyEvent.KEYCODE_ENTER) {

View File

@ -9,6 +9,7 @@ import android.content.IntentFilter;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.database.ContentObserver; import android.database.ContentObserver;
import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
@ -17,11 +18,6 @@ import android.provider.ContactsContract;
import android.util.Log; import android.util.Log;
import android.view.WindowManager; import android.view.WindowManager;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import org.thoughtcrime.securesms.ApplicationExportManager.ApplicationExportListener; import org.thoughtcrime.securesms.ApplicationExportManager.ApplicationExportListener;
import org.thoughtcrime.securesms.crypto.DecryptingQueue; import org.thoughtcrime.securesms.crypto.DecryptingQueue;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
@ -35,6 +31,11 @@ import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.service.SendReceiveService; import org.thoughtcrime.securesms.service.SendReceiveService;
import org.thoughtcrime.securesms.util.MemoryCleaner; import org.thoughtcrime.securesms.util.MemoryCleaner;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
public class ConversationListActivity extends SherlockFragmentActivity public class ConversationListActivity extends SherlockFragmentActivity
implements ConversationListFragment.ConversationSelectedListener implements ConversationListFragment.ConversationSelectedListener
{ {
@ -63,7 +64,6 @@ public class ConversationListActivity extends SherlockFragmentActivity
@Override @Override
protected void onNewIntent(Intent intent) { protected void onNewIntent(Intent intent) {
super.onNewIntent(intent); super.onNewIntent(intent);
Log.w("SecureSMS", "Got onNewIntent...");
createConversationIfNecessary(intent); createConversationIfNecessary(intent);
} }
@ -72,7 +72,6 @@ public class ConversationListActivity extends SherlockFragmentActivity
super.onPause(); super.onPause();
if (newKeyReceiver != null) { if (newKeyReceiver != null) {
Log.w("ConversationListActivity", "Unregistering receiver...");
unregisterReceiver(newKeyReceiver); unregisterReceiver(newKeyReceiver);
newKeyReceiver = null; newKeyReceiver = null;
} }
@ -81,7 +80,6 @@ public class ConversationListActivity extends SherlockFragmentActivity
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
Log.w("ConversationListActivity", "onResume called...");
clearNotifications(); clearNotifications();
initializeKeyCachingServiceRegistration(); initializeKeyCachingServiceRegistration();
@ -126,7 +124,7 @@ public class ConversationListActivity extends SherlockFragmentActivity
super.onOptionsItemSelected(item); super.onOptionsItemSelected(item);
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.menu_new_message: createConversation(-1, null); return true; case R.id.menu_new_message: createConversation(-1, null, null, null, null); return true;
case R.id.menu_unlock: promptForPassphrase(); return true; case R.id.menu_unlock: promptForPassphrase(); return true;
case R.id.menu_settings: handleDisplaySettings(); return true; case R.id.menu_settings: handleDisplaySettings(); return true;
case R.id.menu_export: handleExportDatabase(); return true; case R.id.menu_export: handleExportDatabase(); return true;
@ -139,21 +137,25 @@ public class ConversationListActivity extends SherlockFragmentActivity
@Override @Override
public void onCreateConversation(long threadId, Recipients recipients) { public void onCreateConversation(long threadId, Recipients recipients) {
createConversation(threadId, recipients); createConversation(threadId, recipients, null, null, null);
} }
private void createConversation(long threadId, Recipients recipients) { private void createConversation(long threadId, Recipients recipients,
String text, Uri imageUri, Uri audioUri)
{
if (this.masterSecret == null) { if (this.masterSecret == null) {
promptForPassphrase(); promptForPassphrase();
return; return;
} }
Log.w("ConversationListActivity", "Creating conversation: " + threadId);
Intent intent = new Intent(this, ConversationActivity.class); Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra("recipients", recipients); intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients);
intent.putExtra("thread_id", threadId); intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra("master_secret", masterSecret); intent.putExtra(ConversationActivity.MASTER_SECRET_EXTRA, masterSecret);
intent.putExtra(ConversationActivity.DRAFT_TEXT_EXTRA, text);
intent.putExtra(ConversationActivity.DRAFT_IMAGE_EXTRA, imageUri);
intent.putExtra(ConversationActivity.DRAFT_AUDIO_EXTRA, audioUri);
startActivity(intent); startActivity(intent);
} }
@ -322,27 +324,39 @@ public class ConversationListActivity extends SherlockFragmentActivity
} }
private void createConversationIfNecessary(Intent intent) { private void createConversationIfNecessary(Intent intent) {
Log.w("ConversationListActivity", "createConversationIfNecessary called");
long thread = intent.getLongExtra("thread_id", -1L); long thread = intent.getLongExtra("thread_id", -1L);
String type = intent.getType();
Recipients recipients = null; Recipients recipients = null;
String draftText = null;
Uri draftImage = null;
Uri draftAudio = null;
if (intent.getAction() != null && intent.getAction().equals("android.intent.action.SENDTO")) { if (Intent.ACTION_SENDTO.equals(intent.getAction())) {
Log.w("ConversationListActivity", "Intent has sendto action...");
try { try {
recipients = RecipientFactory.getRecipientsFromString(this, intent.getData().getSchemeSpecificPart(), false); recipients = RecipientFactory.getRecipientsFromString(this, intent.getData().getSchemeSpecificPart(), false);
thread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipients); thread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipients);
} catch (RecipientFormattingException rfe) { } catch (RecipientFormattingException rfe) {
recipients = null; recipients = null;
} }
} else if (Intent.ACTION_SEND.equals(intent.getAction())) {
if ("text/plain".equals(type)) {
draftText = intent.getStringExtra(Intent.EXTRA_TEXT);
} else if (type.startsWith("image/")) {
draftImage = intent.getParcelableExtra(Intent.EXTRA_STREAM);
} else if (type.startsWith("audio/")) {
draftAudio = intent.getParcelableExtra(Intent.EXTRA_STREAM);
}
} else { } else {
recipients = intent.getParcelableExtra("recipients"); recipients = intent.getParcelableExtra("recipients");
} }
if (recipients != null) { if (recipients != null || Intent.ACTION_SEND.equals(intent.getAction())) {
Log.w("ConversationListActivity", "Creating conversation: " + thread + " , " + recipients); createConversation(thread, recipients, draftText, draftImage, draftAudio);
createConversation(thread, recipients);
intent.putExtra("thread_id", -1L); intent.putExtra("thread_id", -1L);
intent.putExtra("recipients", (Parcelable)null); intent.putExtra("recipients", (Parcelable)null);
intent.putExtra(Intent.EXTRA_TEXT, (String)null);
intent.putExtra(Intent.EXTRA_STREAM, (Parcelable)null);
intent.setAction(null); intent.setAction(null);
} }
} }
@ -360,6 +374,7 @@ public class ConversationListActivity extends SherlockFragmentActivity
} }
private ServiceConnection serviceConnection = new ServiceConnection() { private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) { public void onServiceConnected(ComponentName className, IBinder service) {
KeyCachingService keyCachingService = ((KeyCachingService.KeyCachingBinder)service).getService(); KeyCachingService keyCachingService = ((KeyCachingService.KeyCachingBinder)service).getService();
MasterSecret masterSecret = keyCachingService.getMasterSecret(); MasterSecret masterSecret = keyCachingService.getMasterSecret();
@ -379,16 +394,19 @@ public class ConversationListActivity extends SherlockFragmentActivity
} }
} }
@Override
public void onServiceDisconnected(ComponentName name) {} public void onServiceDisconnected(ComponentName name) {}
}; };
private class IdentityKeyInitializer implements Runnable { private class IdentityKeyInitializer implements Runnable {
@Override
public void run() { public void run() {
IdentityKeyUtil.generateIdentityKeys(ConversationListActivity.this, masterSecret); IdentityKeyUtil.generateIdentityKeys(ConversationListActivity.this, masterSecret);
} }
} }
private class AsymmetricMasteSecretInitializer implements Runnable { private class AsymmetricMasteSecretInitializer implements Runnable {
@Override
public void run() { public void run() {
MasterSecretUtil.generateAsymmetricMasterSecret(ConversationListActivity.this, masterSecret); MasterSecretUtil.generateAsymmetricMasterSecret(ConversationListActivity.this, masterSecret);
} }