mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-30 13:35:18 +00:00
Move media attachment long-click event to context menu.
Long-click on a media attachment will now bring up the normal context menu for a ConversationItem long-click, but with the addition of a "save attachment" option. This allows users to long-click on messages with media in them and still see the other contextual menu options. // FREEBIE
This commit is contained in:
parent
68747142d6
commit
c719a48a2c
6
res/menu/conversation_context_image.xml
Normal file
6
res/menu/conversation_context_image.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:title="@string/conversation_context_image__save_attachment"
|
||||||
|
android:id="@+id/menu_context_save_attachment" />
|
||||||
|
</menu>
|
@ -47,13 +47,6 @@
|
|||||||
<string name="ConversationItem_message_size_d_kb">Message size: %d KB</string>
|
<string name="ConversationItem_message_size_d_kb">Message size: %d KB</string>
|
||||||
<string name="ConversationItem_expires_s">Expires: %s</string>
|
<string name="ConversationItem_expires_s">Expires: %s</string>
|
||||||
<string name="ConversationItem_error_sending_message">Error sending message</string>
|
<string name="ConversationItem_error_sending_message">Error sending message</string>
|
||||||
<string name="ConversationItem_saving_attachment">Saving attachment</string>
|
|
||||||
<string name="ConversationItem_saving_attachment_to_sd_card">Saving attachment to SD card...</string>
|
|
||||||
<string name="ConversationItem_save_to_sd_card">Save to SD card?</string>
|
|
||||||
<string name="ConversationItem_this_media_has_been_stored_in_an_encrypted_database_warning">This media has been stored in an encrypted database. The version you save to the SD card will no longer be encrypted. Would you like to continue?</string>
|
|
||||||
<string name="ConversationItem_error_while_saving_attachment_to_sd_card">Error while saving attachment to SD card!</string>
|
|
||||||
<string name="ConversationItem_success_exclamation">Success!</string>
|
|
||||||
<string name="ConversationItem_unable_to_write_to_sd_card_exclamation">Unable to write to SD card!</string>
|
|
||||||
<string name="ConversationItem_view_secure_media_question">View secure media?</string>
|
<string name="ConversationItem_view_secure_media_question">View secure media?</string>
|
||||||
<string name="ConversationItem_this_media_has_been_stored_in_an_encrypted_database_external_viewer_warning">This media has been stored in an encrypted database. Unfortunately, to view it with an external content viewer currently requires the data to be temporarily decrypted and written to disk. Are you sure that you would like to do this?</string>
|
<string name="ConversationItem_this_media_has_been_stored_in_an_encrypted_database_external_viewer_warning">This media has been stored in an encrypted database. Unfortunately, to view it with an external content viewer currently requires the data to be temporarily decrypted and written to disk. Are you sure that you would like to do this?</string>
|
||||||
<string name="ConversationItem_received_and_processed_key_exchange_message">Received and processed key exchange message.</string>
|
<string name="ConversationItem_received_and_processed_key_exchange_message">Received and processed key exchange message.</string>
|
||||||
@ -102,6 +95,13 @@
|
|||||||
<string name="ConversationFragment_sender_s_transport_s_sent_s_received_s">Sender: %1$s\nTransport: %2$s\nSent: %3$s\nReceived: %4$s</string>
|
<string name="ConversationFragment_sender_s_transport_s_sent_s_received_s">Sender: %1$s\nTransport: %2$s\nSent: %3$s\nReceived: %4$s</string>
|
||||||
<string name="ConversationFragment_confirm_message_delete">Confirm message delete</string>
|
<string name="ConversationFragment_confirm_message_delete">Confirm message delete</string>
|
||||||
<string name="ConversationFragment_are_you_sure_you_want_to_permanently_delete_this_message">Are you sure that you want to permanently delete this message?</string>
|
<string name="ConversationFragment_are_you_sure_you_want_to_permanently_delete_this_message">Are you sure that you want to permanently delete this message?</string>
|
||||||
|
<string name="ConversationFragment_save_to_sd_card">Save to SD card?</string>
|
||||||
|
<string name="ConversationFragment_this_media_has_been_stored_in_an_encrypted_database_warning">This media has been stored in an encrypted database. The version you save to the SD card will no longer be encrypted. Would you like to continue?</string>
|
||||||
|
<string name="ConversationFragment_error_while_saving_attachment_to_sd_card">Error while saving attachment to SD card!</string>
|
||||||
|
<string name="ConversationFragment_success_exclamation">Success!</string>
|
||||||
|
<string name="ConversationFragment_unable_to_write_to_sd_card_exclamation">Unable to write to SD card!</string>
|
||||||
|
<string name="ConversationFragment_saving_attachment">Saving attachment</string>
|
||||||
|
<string name="ConversationFragment_saving_attachment_to_sd_card">Saving attachment to SD card...</string>
|
||||||
|
|
||||||
<!-- ConversationListAdapter -->
|
<!-- ConversationListAdapter -->
|
||||||
<string name="ConversationListAdapter_key_exchange_message">Key exchange message...</string>
|
<string name="ConversationListAdapter_key_exchange_message">Key exchange message...</string>
|
||||||
@ -795,6 +795,9 @@
|
|||||||
<string name="conversation_context__menu_forward_message">Forward message</string>
|
<string name="conversation_context__menu_forward_message">Forward message</string>
|
||||||
<string name="conversation_context__menu_resend_message">Resend message</string>
|
<string name="conversation_context__menu_resend_message">Resend message</string>
|
||||||
|
|
||||||
|
<!-- conversation_context_image -->
|
||||||
|
<string name="conversation_context_image__save_attachment">Save attachment</string>
|
||||||
|
|
||||||
<!-- conversation_insecure -->
|
<!-- conversation_insecure -->
|
||||||
<string name="conversation_insecure__menu_start_secure_session">Start secure session</string>
|
<string name="conversation_insecure__menu_start_secure_session">Start secure session</string>
|
||||||
|
|
||||||
|
@ -2,41 +2,61 @@ package org.thoughtcrime.securesms;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
|
import android.app.ProgressDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
import android.media.MediaScannerConnection;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Environment;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.support.v4.app.LoaderManager;
|
import android.support.v4.app.LoaderManager;
|
||||||
import android.support.v4.content.Loader;
|
import android.support.v4.content.Loader;
|
||||||
import android.text.ClipboardManager;
|
import android.text.ClipboardManager;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.ContextMenu;
|
import android.view.ContextMenu;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.support.v4.widget.CursorAdapter;
|
import android.support.v4.widget.CursorAdapter;
|
||||||
|
import android.webkit.MimeTypeMap;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.actionbarsherlock.app.SherlockListFragment;
|
import com.actionbarsherlock.app.SherlockListFragment;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.loaders.ConversationLoader;
|
import org.thoughtcrime.securesms.database.loaders.ConversationLoader;
|
||||||
|
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||||
|
import org.thoughtcrime.securesms.mms.Slide;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||||
import org.thoughtcrime.securesms.util.Dialogs;
|
import org.thoughtcrime.securesms.util.Dialogs;
|
||||||
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
||||||
import org.whispersystems.textsecure.crypto.MasterSecret;
|
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||||
|
import org.whispersystems.textsecure.util.Util;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
import java.sql.Date;
|
import java.sql.Date;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
public class ConversationFragment extends SherlockListFragment
|
public class ConversationFragment extends SherlockListFragment
|
||||||
implements LoaderManager.LoaderCallbacks<Cursor>
|
implements LoaderManager.LoaderCallbacks<Cursor>
|
||||||
{
|
{
|
||||||
|
private static final String TAG = ConversationFragment.class.getSimpleName();
|
||||||
|
|
||||||
private ConversationFragmentListener listener;
|
private ConversationFragmentListener listener;
|
||||||
|
|
||||||
@ -66,10 +86,23 @@ public class ConversationFragment extends SherlockListFragment
|
|||||||
inflater.inflate(R.menu.conversation_context, menu);
|
inflater.inflate(R.menu.conversation_context, menu);
|
||||||
|
|
||||||
MessageRecord messageRecord = getMessageRecord();
|
MessageRecord messageRecord = getMessageRecord();
|
||||||
|
|
||||||
if (messageRecord.isFailed()) {
|
if (messageRecord.isFailed()) {
|
||||||
MenuItem resend = menu.findItem(R.id.menu_context_resend);
|
MenuItem resend = menu.findItem(R.id.menu_context_resend);
|
||||||
resend.setVisible(true);
|
resend.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (messageRecord.isMms() && !messageRecord.isMmsNotification()) {
|
||||||
|
try {
|
||||||
|
if (((MediaMmsMessageRecord)messageRecord).getSlideDeck().get().containsMediaSlide()) {
|
||||||
|
inflater.inflate(R.menu.conversation_context_image, menu);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
Log.w(TAG, ie);
|
||||||
|
} catch (ExecutionException ee) {
|
||||||
|
Log.w(TAG, ee);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -81,6 +114,7 @@ public class ConversationFragment extends SherlockListFragment
|
|||||||
case R.id.menu_context_details: handleDisplayDetails(messageRecord); return true;
|
case R.id.menu_context_details: handleDisplayDetails(messageRecord); return true;
|
||||||
case R.id.menu_context_forward: handleForwardMessage(messageRecord); return true;
|
case R.id.menu_context_forward: handleForwardMessage(messageRecord); return true;
|
||||||
case R.id.menu_context_resend: handleResendMessage(messageRecord); return true;
|
case R.id.menu_context_resend: handleResendMessage(messageRecord); return true;
|
||||||
|
case R.id.menu_context_save_attachment:handleSaveAttachment(messageRecord); return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -196,11 +230,26 @@ public class ConversationFragment extends SherlockListFragment
|
|||||||
MessageSender.resend(activity, messageId, message.isMms());
|
MessageSender.resend(activity, messageId, message.isMms());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleSaveAttachment(final MessageRecord message) {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||||
|
builder.setTitle(R.string.ConversationFragment_save_to_sd_card);
|
||||||
|
builder.setIcon(Dialogs.resolveIcon(getActivity(), R.attr.dialog_alert_icon));
|
||||||
|
builder.setCancelable(true);
|
||||||
|
builder.setMessage(R.string.ConversationFragment_this_media_has_been_stored_in_an_encrypted_database_warning);
|
||||||
|
builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
SaveAttachmentTask saveTask = new SaveAttachmentTask(getActivity());
|
||||||
|
saveTask.execute((MediaMmsMessageRecord) message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.setNegativeButton(R.string.no, null);
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
|
|
||||||
private void initializeResources() {
|
private void initializeResources() {
|
||||||
String recipientIds = this.getActivity().getIntent().getStringExtra("recipients");
|
String recipientIds = this.getActivity().getIntent().getStringExtra("recipients");
|
||||||
|
|
||||||
this.masterSecret = (MasterSecret)this.getActivity().getIntent()
|
this.masterSecret = this.getActivity().getIntent().getParcelableExtra("master_secret");
|
||||||
.getParcelableExtra("master_secret");
|
|
||||||
this.recipients = RecipientFactory.getRecipientsForIds(getActivity(), recipientIds, true);
|
this.recipients = RecipientFactory.getRecipientsForIds(getActivity(), recipientIds, true);
|
||||||
this.threadId = this.getActivity().getIntent().getLongExtra("thread_id", -1);
|
this.threadId = this.getActivity().getIntent().getLongExtra("thread_id", -1);
|
||||||
}
|
}
|
||||||
@ -244,4 +293,130 @@ public class ConversationFragment extends SherlockListFragment
|
|||||||
public void setComposeText(String text);
|
public void setComposeText(String text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class SaveAttachmentTask extends AsyncTask<MediaMmsMessageRecord, Void, Integer> {
|
||||||
|
|
||||||
|
private static final int SUCCESS = 0;
|
||||||
|
private static final int FAILURE = 1;
|
||||||
|
private static final int WRITE_ACCESS_FAILURE = 2;
|
||||||
|
|
||||||
|
private final WeakReference<Context> contextReference;
|
||||||
|
private ProgressDialog progressDialog;
|
||||||
|
|
||||||
|
public SaveAttachmentTask(Context context) {
|
||||||
|
this.contextReference = new WeakReference<Context>(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
Context context = contextReference.get();
|
||||||
|
|
||||||
|
if (context != null) {
|
||||||
|
progressDialog = ProgressDialog.show(context,
|
||||||
|
context.getString(R.string.ConversationFragment_saving_attachment),
|
||||||
|
context.getString(R.string.ConversationFragment_saving_attachment_to_sd_card),
|
||||||
|
true, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Integer doInBackground(MediaMmsMessageRecord... messageRecord) {
|
||||||
|
try {
|
||||||
|
Context context = contextReference.get();
|
||||||
|
|
||||||
|
if (!Environment.getExternalStorageDirectory().canWrite()) {
|
||||||
|
return WRITE_ACCESS_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context == null) {
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Slide slide = getAttachment(messageRecord[0]);
|
||||||
|
|
||||||
|
if (slide == null) {
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
File mediaFile = constructOutputFile(slide);
|
||||||
|
InputStream inputStream = slide.getPartDataInputStream();
|
||||||
|
OutputStream outputStream = new FileOutputStream(mediaFile);
|
||||||
|
|
||||||
|
Util.copy(inputStream, outputStream);
|
||||||
|
|
||||||
|
MediaScannerConnection.scanFile(context, new String[] {mediaFile.getAbsolutePath()},
|
||||||
|
new String[] {slide.getContentType()}, null);
|
||||||
|
|
||||||
|
return SUCCESS;
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
Log.w(TAG, ioe);
|
||||||
|
return FAILURE;
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Integer result) {
|
||||||
|
Context context = contextReference.get();
|
||||||
|
if (context == null) return;
|
||||||
|
|
||||||
|
switch (result) {
|
||||||
|
case FAILURE:
|
||||||
|
Toast.makeText(context, R.string.ConversationFragment_error_while_saving_attachment_to_sd_card,
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
break;
|
||||||
|
case SUCCESS:
|
||||||
|
Toast.makeText(context, R.string.ConversationFragment_success_exclamation,
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
break;
|
||||||
|
case WRITE_ACCESS_FAILURE:
|
||||||
|
Toast.makeText(context, R.string.ConversationFragment_unable_to_write_to_sd_card_exclamation,
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progressDialog != null)
|
||||||
|
progressDialog.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Slide getAttachment(MediaMmsMessageRecord record)
|
||||||
|
throws ExecutionException, InterruptedException
|
||||||
|
{
|
||||||
|
List<Slide> slides = record.getSlideDeck().get().getSlides();
|
||||||
|
|
||||||
|
for (Slide slide : slides) {
|
||||||
|
if (slide.hasImage() || slide.hasVideo() || slide.hasAudio()) {
|
||||||
|
return slide;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private File constructOutputFile(Slide slide) throws IOException {
|
||||||
|
File sdCard = Environment.getExternalStorageDirectory();
|
||||||
|
File outputDirectory;
|
||||||
|
|
||||||
|
if (slide.hasVideo()) {
|
||||||
|
outputDirectory = new File(sdCard.getAbsoluteFile() + File.separator + "Movies");
|
||||||
|
} else if (slide.hasAudio()) {
|
||||||
|
outputDirectory = new File(sdCard.getAbsolutePath() + File.separator + "Music");
|
||||||
|
} else {
|
||||||
|
outputDirectory = new File(sdCard.getAbsolutePath() + File.separator + "Pictures");
|
||||||
|
}
|
||||||
|
|
||||||
|
outputDirectory.mkdirs();
|
||||||
|
|
||||||
|
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
|
||||||
|
String extension = mimeTypeMap.getExtensionFromMimeType(slide.getContentType());
|
||||||
|
|
||||||
|
if (extension == null)
|
||||||
|
extension = "attach";
|
||||||
|
|
||||||
|
return File.createTempFile("textsecure", "." + extension, outputDirectory);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,17 +17,14 @@
|
|||||||
package org.thoughtcrime.securesms;
|
package org.thoughtcrime.securesms;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.ProgressDialog;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.media.MediaScannerConnection;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Environment;
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.provider.Contacts.Intents;
|
import android.provider.Contacts.Intents;
|
||||||
@ -35,12 +32,10 @@ import android.provider.ContactsContract.QuickContact;
|
|||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.webkit.MimeTypeMap;
|
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||||
@ -53,18 +48,12 @@ import org.thoughtcrime.securesms.mms.SlideDeck;
|
|||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.service.SendReceiveService;
|
import org.thoughtcrime.securesms.service.SendReceiveService;
|
||||||
import org.thoughtcrime.securesms.util.DateUtils;
|
import org.thoughtcrime.securesms.util.DateUtils;
|
||||||
import org.thoughtcrime.securesms.util.Emoji;
|
|
||||||
import org.thoughtcrime.securesms.util.Dialogs;
|
import org.thoughtcrime.securesms.util.Dialogs;
|
||||||
|
import org.thoughtcrime.securesms.util.Emoji;
|
||||||
import org.whispersystems.textsecure.crypto.MasterSecret;
|
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||||
import org.whispersystems.textsecure.util.FutureTaskListener;
|
import org.whispersystems.textsecure.util.FutureTaskListener;
|
||||||
import org.whispersystems.textsecure.util.ListenableFutureTask;
|
import org.whispersystems.textsecure.util.ListenableFutureTask;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A view that displays an individual conversation item within a conversation
|
* A view that displays an individual conversation item within a conversation
|
||||||
* thread. Used by ComposeMessageActivity's ListActivity via a ConversationAdapter.
|
* thread. Used by ComposeMessageActivity's ListActivity via a ConversationAdapter.
|
||||||
@ -182,9 +171,9 @@ public class ConversationItem extends LinearLayout {
|
|||||||
setEvents(messageRecord);
|
setEvents(messageRecord);
|
||||||
setMinimumWidth();
|
setMinimumWidth();
|
||||||
|
|
||||||
if (messageRecord instanceof NotificationMmsMessageRecord) {
|
if (messageRecord.isMmsNotification()) {
|
||||||
setNotificationMmsAttributes((NotificationMmsMessageRecord)messageRecord);
|
setNotificationMmsAttributes((NotificationMmsMessageRecord)messageRecord);
|
||||||
} else if (messageRecord instanceof MediaMmsMessageRecord) {
|
} else if (messageRecord.isMms()) {
|
||||||
setMediaMmsAttributes((MediaMmsMessageRecord)messageRecord);
|
setMediaMmsAttributes((MediaMmsMessageRecord)messageRecord);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -365,9 +354,13 @@ public class ConversationItem extends LinearLayout {
|
|||||||
for (Slide slide : result.getSlides()) {
|
for (Slide slide : result.getSlides()) {
|
||||||
if (slide.hasImage()) {
|
if (slide.hasImage()) {
|
||||||
slide.setThumbnailOn(mmsThumbnail);
|
slide.setThumbnailOn(mmsThumbnail);
|
||||||
// mmsThumbnail.setImageBitmap(slide.getThumbnail());
|
|
||||||
mmsThumbnail.setOnClickListener(new ThumbnailClickListener(slide));
|
mmsThumbnail.setOnClickListener(new ThumbnailClickListener(slide));
|
||||||
mmsThumbnail.setOnLongClickListener(new ThumbnailSaveListener(slide));
|
mmsThumbnail.setOnLongClickListener(new OnLongClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onLongClick(View v) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
mmsThumbnail.setVisibility(View.VISIBLE);
|
mmsThumbnail.setVisibility(View.VISIBLE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -439,127 +432,6 @@ public class ConversationItem extends LinearLayout {
|
|||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ThumbnailSaveListener extends Handler implements View.OnLongClickListener, Runnable, MediaScannerConnection.MediaScannerConnectionClient {
|
|
||||||
private static final int SUCCESS = 0;
|
|
||||||
private static final int FAILURE = 1;
|
|
||||||
private static final int WRITE_ACCESS_FAILURE = 2;
|
|
||||||
|
|
||||||
private final Slide slide;
|
|
||||||
private ProgressDialog progressDialog;
|
|
||||||
private MediaScannerConnection mediaScannerConnection;
|
|
||||||
private File mediaFile;
|
|
||||||
|
|
||||||
public ThumbnailSaveListener(Slide slide) {
|
|
||||||
this.slide = slide;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run() {
|
|
||||||
if (!Environment.getExternalStorageDirectory().canWrite()) {
|
|
||||||
this.obtainMessage(WRITE_ACCESS_FAILURE).sendToTarget();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
mediaFile = constructOutputFile();
|
|
||||||
InputStream inputStream = slide.getPartDataInputStream();
|
|
||||||
OutputStream outputStream = new FileOutputStream(mediaFile);
|
|
||||||
|
|
||||||
byte[] buffer = new byte[4096];
|
|
||||||
int read;
|
|
||||||
|
|
||||||
while ((read = inputStream.read(buffer)) != -1) {
|
|
||||||
outputStream.write(buffer, 0, read);
|
|
||||||
}
|
|
||||||
|
|
||||||
outputStream.close();
|
|
||||||
inputStream.close();
|
|
||||||
|
|
||||||
mediaScannerConnection = new MediaScannerConnection(context, this);
|
|
||||||
mediaScannerConnection.connect();
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
Log.w(TAG, ioe);
|
|
||||||
this.obtainMessage(FAILURE).sendToTarget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private File constructOutputFile() throws IOException {
|
|
||||||
File sdCard = Environment.getExternalStorageDirectory();
|
|
||||||
File outputDirectory;
|
|
||||||
|
|
||||||
if (slide.hasVideo())
|
|
||||||
outputDirectory = new File(sdCard.getAbsoluteFile() + File.separator + "Movies");
|
|
||||||
else if (slide.hasAudio())
|
|
||||||
outputDirectory = new File(sdCard.getAbsolutePath() + File.separator + "Music");
|
|
||||||
else
|
|
||||||
outputDirectory = new File(sdCard.getAbsolutePath() + File.separator + "Pictures");
|
|
||||||
outputDirectory.mkdirs();
|
|
||||||
|
|
||||||
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
|
|
||||||
String extension = mimeTypeMap.getExtensionFromMimeType(slide.getContentType());
|
|
||||||
if (extension == null)
|
|
||||||
extension = "attach";
|
|
||||||
|
|
||||||
return File.createTempFile("textsecure", "." + extension, outputDirectory);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveToSdCard() {
|
|
||||||
progressDialog = new ProgressDialog(context);
|
|
||||||
progressDialog.setTitle(context.getString(R.string.ConversationItem_saving_attachment));
|
|
||||||
progressDialog.setMessage(context.getString(R.string.ConversationItem_saving_attachment_to_sd_card));
|
|
||||||
progressDialog.setCancelable(false);
|
|
||||||
progressDialog.setIndeterminate(true);
|
|
||||||
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
|
|
||||||
progressDialog.show();
|
|
||||||
new Thread(this).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onLongClick(View v) {
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
|
||||||
builder.setTitle(R.string.ConversationItem_save_to_sd_card);
|
|
||||||
builder.setIcon(Dialogs.resolveIcon(context, R.attr.dialog_alert_icon));
|
|
||||||
builder.setCancelable(true);
|
|
||||||
builder.setMessage(R.string.ConversationItem_this_media_has_been_stored_in_an_encrypted_database_warning);
|
|
||||||
builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
saveToSdCard();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.setNegativeButton(R.string.no, null);
|
|
||||||
builder.show();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleMessage(Message message) {
|
|
||||||
switch (message.what) {
|
|
||||||
case FAILURE:
|
|
||||||
Toast.makeText(context, R.string.ConversationItem_error_while_saving_attachment_to_sd_card,
|
|
||||||
Toast.LENGTH_LONG).show();
|
|
||||||
break;
|
|
||||||
case SUCCESS:
|
|
||||||
Toast.makeText(context, R.string.ConversationItem_success_exclamation,
|
|
||||||
Toast.LENGTH_LONG).show();
|
|
||||||
break;
|
|
||||||
case WRITE_ACCESS_FAILURE:
|
|
||||||
Toast.makeText(context, R.string.ConversationItem_unable_to_write_to_sd_card_exclamation,
|
|
||||||
Toast.LENGTH_LONG).show();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
progressDialog.dismiss();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onMediaScannerConnected() {
|
|
||||||
mediaScannerConnection.scanFile(mediaFile.getAbsolutePath(), slide.getContentType());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onScanCompleted(String path, Uri uri) {
|
|
||||||
mediaScannerConnection.disconnect();
|
|
||||||
this.obtainMessage(SUCCESS).sendToTarget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ThumbnailClickListener implements View.OnClickListener {
|
private class ThumbnailClickListener implements View.OnClickListener {
|
||||||
private final Slide slide;
|
private final Slide slide;
|
||||||
|
|
||||||
|
@ -68,6 +68,11 @@ public class MediaMmsMessageRecord extends MessageRecord {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMmsNotification() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpannableString getDisplayBody() {
|
public SpannableString getDisplayBody() {
|
||||||
if (MmsDatabase.Types.isDecryptInProgressType(type)) {
|
if (MmsDatabase.Types.isDecryptInProgressType(type)) {
|
||||||
|
@ -62,6 +62,7 @@ public abstract class MessageRecord extends DisplayRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract boolean isMms();
|
public abstract boolean isMms();
|
||||||
|
public abstract boolean isMmsNotification();
|
||||||
|
|
||||||
public boolean isFailed() {
|
public boolean isFailed() {
|
||||||
return
|
return
|
||||||
|
@ -101,6 +101,11 @@ public class NotificationMmsMessageRecord extends MessageRecord {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMmsNotification() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpannableString getDisplayBody() {
|
public SpannableString getDisplayBody() {
|
||||||
return emphasisAdded(context.getString(R.string.NotificationMmsMessageRecord_multimedia_message));
|
return emphasisAdded(context.getString(R.string.NotificationMmsMessageRecord_multimedia_message));
|
||||||
|
@ -98,6 +98,11 @@ public class SmsMessageRecord extends MessageRecord {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMmsNotification() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private static int getGenericDeliveryStatus(int status) {
|
private static int getGenericDeliveryStatus(int status) {
|
||||||
if (status == SmsDatabase.Status.STATUS_NONE) {
|
if (status == SmsDatabase.Status.STATUS_NONE) {
|
||||||
return MessageRecord.DELIVERY_STATUS_NONE;
|
return MessageRecord.DELIVERY_STATUS_NONE;
|
||||||
|
@ -74,5 +74,15 @@ public class SlideDeck {
|
|||||||
public List<Slide> getSlides() {
|
public List<Slide> getSlides() {
|
||||||
return slides;
|
return slides;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean containsMediaSlide() {
|
||||||
|
for (Slide slide : slides) {
|
||||||
|
if (slide.hasImage() || slide.hasVideo() || slide.hasAudio()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user