mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-27 12:05:22 +00:00
parent
60ab71099f
commit
59f2446a2b
@ -1,9 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/drawer_layout"
|
||||
xmlns:wheel="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout android:id="@+id/drawer_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone" />
|
||||
|
||||
<com.pnikosis.materialishprogress.ProgressWheel android:id="@+id/progress_wheel"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_gravity="center"
|
||||
wheel:matProg_progressIndeterminate="true" />
|
||||
|
||||
</FrameLayout>
|
||||
|
@ -16,7 +16,6 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
@ -98,7 +97,7 @@ import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.providers.CaptureProvider;
|
||||
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
||||
@ -201,7 +200,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
private DynamicTheme dynamicTheme = new DynamicTheme();
|
||||
private DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
@Override
|
||||
protected void onPreCreate() {
|
||||
dynamicTheme.onCreate(this);
|
||||
@ -311,13 +309,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
switch (reqCode) {
|
||||
case PICK_IMAGE:
|
||||
boolean isGif = MediaUtil.isGif(MediaUtil.getMimeType(this, data.getData()));
|
||||
setMedia(data.getData(), isGif ? MediaType.GIF : MediaType.IMAGE, false);
|
||||
setMedia(data.getData(), isGif ? MediaType.GIF : MediaType.IMAGE);
|
||||
break;
|
||||
case PICK_VIDEO:
|
||||
setMedia(data.getData(), MediaType.VIDEO, false);
|
||||
setMedia(data.getData(), MediaType.VIDEO);
|
||||
break;
|
||||
case PICK_AUDIO:
|
||||
setMedia(data.getData(), MediaType.AUDIO, false);
|
||||
setMedia(data.getData(), MediaType.AUDIO);
|
||||
break;
|
||||
case PICK_CONTACT_INFO:
|
||||
addAttachmentContactInfo(data.getData());
|
||||
@ -331,7 +329,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
break;
|
||||
case TAKE_PHOTO:
|
||||
if (attachmentManager.getCaptureUri() != null) {
|
||||
setMedia(attachmentManager.getCaptureUri(), MediaType.IMAGE, true);
|
||||
setMedia(attachmentManager.getCaptureUri(), MediaType.IMAGE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -717,9 +715,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
if (draftText != null) composeText.setText(draftText);
|
||||
|
||||
if (draftImage != null) setMedia(draftImage, MediaType.IMAGE, false);
|
||||
else if (draftAudio != null) setMedia(draftAudio, MediaType.AUDIO, false);
|
||||
else if (draftVideo != null) setMedia(draftVideo, MediaType.VIDEO, false);
|
||||
if (draftImage != null) setMedia(draftImage, MediaType.IMAGE);
|
||||
else if (draftAudio != null) setMedia(draftAudio, MediaType.AUDIO);
|
||||
else if (draftVideo != null) setMedia(draftVideo, MediaType.VIDEO);
|
||||
|
||||
if (draftText == null && draftImage == null && draftAudio == null && draftVideo == null) {
|
||||
initializeDraftFromDatabase();
|
||||
@ -753,11 +751,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
if (draft.getType().equals(Draft.TEXT)) {
|
||||
composeText.setText(draft.getValue());
|
||||
} else if (draft.getType().equals(Draft.IMAGE)) {
|
||||
setMedia(Uri.parse(draft.getValue()), MediaType.IMAGE, false);
|
||||
setMedia(Uri.parse(draft.getValue()), MediaType.IMAGE);
|
||||
} else if (draft.getType().equals(Draft.AUDIO)) {
|
||||
setMedia(Uri.parse(draft.getValue()), MediaType.AUDIO, false);
|
||||
setMedia(Uri.parse(draft.getValue()), MediaType.AUDIO);
|
||||
} else if (draft.getType().equals(Draft.VIDEO)) {
|
||||
setMedia(Uri.parse(draft.getValue()), MediaType.VIDEO, false);
|
||||
setMedia(Uri.parse(draft.getValue()), MediaType.VIDEO);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1012,8 +1010,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
}
|
||||
|
||||
private void setMedia(Uri uri, MediaType mediaType, boolean isCapture) {
|
||||
attachmentManager.setMedia(masterSecret, uri, mediaType, getCurrentMediaConstraints(), isCapture);
|
||||
private void setMedia(Uri uri, MediaType mediaType) {
|
||||
attachmentManager.setMedia(masterSecret, uri, mediaType, getCurrentMediaConstraints());
|
||||
}
|
||||
|
||||
private void addAttachmentContactInfo(Uri contactUri) {
|
||||
@ -1053,7 +1051,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
drafts.add(new Draft(Draft.TEXT, composeText.getText().toString()));
|
||||
}
|
||||
|
||||
for (Slide slide : attachmentManager.getSlideDeck().getSlides()) {
|
||||
for (Slide slide : attachmentManager.buildSlideDeck().getSlides()) {
|
||||
if (slide.hasAudio()) drafts.add(new Draft(Draft.AUDIO, slide.getUri().toString()));
|
||||
else if (slide.hasVideo()) drafts.add(new Draft(Draft.VIDEO, slide.getUri().toString()));
|
||||
else if (slide.hasImage()) drafts.add(new Draft(Draft.IMAGE, slide.getUri().toString()));
|
||||
@ -1263,7 +1261,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
{
|
||||
final Context context = getApplicationContext();
|
||||
OutgoingMediaMessage outgoingMessage = new OutgoingMediaMessage(recipients,
|
||||
attachmentManager.getSlideDeck(),
|
||||
attachmentManager.buildSlideDeck(),
|
||||
getMessage(),
|
||||
System.currentTimeMillis(),
|
||||
distributionType);
|
||||
@ -1336,7 +1334,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
@Override
|
||||
public void onImageCapture(@NonNull final byte[] imageBytes) {
|
||||
setMedia(CaptureProvider.getInstance(this).create(masterSecret, recipients, imageBytes), MediaType.IMAGE, true);
|
||||
setMedia(PersistentBlobProvider.getInstance(this).create(masterSecret, recipients, imageBytes), MediaType.IMAGE);
|
||||
quickAttachmentDrawer.hide(false);
|
||||
}
|
||||
|
||||
|
@ -17,21 +17,29 @@
|
||||
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
import java.net.URLDecoder;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import ws.com.google.android.mms.ContentType;
|
||||
|
||||
@ -43,9 +51,17 @@ import ws.com.google.android.mms.ContentType;
|
||||
public class ShareActivity extends PassphraseRequiredActionBarActivity
|
||||
implements ShareFragment.ConversationSelectedListener
|
||||
{
|
||||
private static final String TAG = ShareActivity.class.getSimpleName();
|
||||
|
||||
private final DynamicTheme dynamicTheme = new DynamicTheme ();
|
||||
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
||||
|
||||
private MasterSecret masterSecret;
|
||||
private ViewGroup fragmentContainer;
|
||||
private View progressWheel;
|
||||
private Uri resolvedExtra;
|
||||
private boolean isPassingAlongMedia;
|
||||
|
||||
@Override
|
||||
protected void onPreCreate() {
|
||||
dynamicTheme.onCreate(this);
|
||||
@ -54,14 +70,21 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle icicle, @NonNull MasterSecret masterSecret) {
|
||||
this.masterSecret = masterSecret;
|
||||
setContentView(R.layout.share_activity);
|
||||
|
||||
fragmentContainer = ViewUtil.findById(this, R.id.drawer_layout);
|
||||
progressWheel = ViewUtil.findById(this, R.id.progress_wheel);
|
||||
|
||||
initFragment(R.id.drawer_layout, new ShareFragment(), masterSecret);
|
||||
initializeMedia();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
setIntent(intent);
|
||||
initializeMedia();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -75,7 +98,46 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
if (!isFinishing()) finish();
|
||||
if (!isPassingAlongMedia && resolvedExtra != null) {
|
||||
PersistentBlobProvider.getInstance(this).delete(resolvedExtra);
|
||||
}
|
||||
if (!isFinishing()) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeMedia() {
|
||||
final Context context = this;
|
||||
isPassingAlongMedia = false;
|
||||
fragmentContainer.setVisibility(View.GONE);
|
||||
progressWheel.setVisibility(View.VISIBLE);
|
||||
new AsyncTask<Uri, Void, Uri>() {
|
||||
@Override
|
||||
protected Uri doInBackground(Uri... uris) {
|
||||
try {
|
||||
if (uris.length != 1 || uris[0] == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
InputStream input = context.getContentResolver().openInputStream(uris[0]);
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return PersistentBlobProvider.getInstance(context).create(masterSecret, input);
|
||||
} catch (IOException ioe) {
|
||||
Log.w(TAG, ioe);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Uri uri) {
|
||||
resolvedExtra = uri;
|
||||
ViewUtil.fadeIn(fragmentContainer, 300);
|
||||
ViewUtil.fadeOut(progressWheel, 300);
|
||||
}
|
||||
}.execute(getIntent().<Uri>getParcelableExtra(Intent.EXTRA_STREAM));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -100,6 +162,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
private void handleNewConversation() {
|
||||
Intent intent = getBaseShareIntent(NewConversationActivity.class);
|
||||
isPassingAlongMedia = true;
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
@ -114,38 +177,24 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
|
||||
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
|
||||
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);
|
||||
|
||||
isPassingAlongMedia = true;
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private Uri getStreamExtra() {
|
||||
Uri streamUri = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
if (streamUri == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (streamUri.getAuthority().equals("com.google.android.apps.photos.contentprovider") &&
|
||||
streamUri.toString().endsWith("/ACTUAL"))
|
||||
{
|
||||
String[] parts = streamUri.toString().split("/");
|
||||
if (parts.length > 3) {
|
||||
return Uri.parse(URLDecoder.decode(parts[parts.length - 2]));
|
||||
}
|
||||
}
|
||||
return streamUri;
|
||||
}
|
||||
|
||||
private Intent getBaseShareIntent(final Class<?> target) {
|
||||
private Intent getBaseShareIntent(final @NonNull Class<?> target) {
|
||||
final Intent intent = new Intent(this, target);
|
||||
final String textExtra = getIntent().getStringExtra(Intent.EXTRA_TEXT);
|
||||
final Uri streamExtra = getStreamExtra();
|
||||
final Uri streamExtra = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
final String type = streamExtra != null ? getMimeType(streamExtra) : getIntent().getType();
|
||||
|
||||
if (resolvedExtra != null) {
|
||||
if (ContentType.isImageType(type)) {
|
||||
intent.putExtra(ConversationActivity.DRAFT_IMAGE_EXTRA, streamExtra);
|
||||
intent.putExtra(ConversationActivity.DRAFT_IMAGE_EXTRA, resolvedExtra);
|
||||
} else if (ContentType.isAudioType(type)) {
|
||||
intent.putExtra(ConversationActivity.DRAFT_AUDIO_EXTRA, streamExtra);
|
||||
intent.putExtra(ConversationActivity.DRAFT_AUDIO_EXTRA, resolvedExtra);
|
||||
} else if (ContentType.isVideoType(type)) {
|
||||
intent.putExtra(ConversationActivity.DRAFT_VIDEO_EXTRA, streamExtra);
|
||||
intent.putExtra(ConversationActivity.DRAFT_VIDEO_EXTRA, resolvedExtra);
|
||||
}
|
||||
}
|
||||
intent.putExtra(ConversationActivity.DRAFT_TEXT_EXTRA, textExtra);
|
||||
|
||||
|
@ -38,9 +38,11 @@ import org.thoughtcrime.securesms.components.AudioView;
|
||||
import org.thoughtcrime.securesms.components.RemovableMediaView;
|
||||
import org.thoughtcrime.securesms.components.ThumbnailView;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.providers.CaptureProvider;
|
||||
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.whispersystems.libaxolotl.util.guava.Optional;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@ -53,18 +55,17 @@ public class AttachmentManager {
|
||||
private final @NonNull RemovableMediaView removableMediaView;
|
||||
private final @NonNull ThumbnailView thumbnail;
|
||||
private final @NonNull AudioView audioView;
|
||||
private final @NonNull SlideDeck slideDeck;
|
||||
private final @NonNull AttachmentListener attachmentListener;
|
||||
|
||||
private Uri captureUri;
|
||||
private @NonNull Optional<Slide> slide = Optional.absent();
|
||||
private @Nullable Uri captureUri;
|
||||
|
||||
public AttachmentManager(@NonNull Activity view, @NonNull AttachmentListener listener) {
|
||||
this.attachmentView = view.findViewById(R.id.attachment_editor);
|
||||
this.thumbnail = (ThumbnailView) view.findViewById(R.id.attachment_thumbnail);
|
||||
this.audioView = (AudioView) view.findViewById(R.id.attachment_audio);
|
||||
this.removableMediaView = (RemovableMediaView) view.findViewById(R.id.removable_media_view);
|
||||
this.slideDeck = new SlideDeck();
|
||||
this.context = view;
|
||||
public AttachmentManager(@NonNull Activity activity, @NonNull AttachmentListener listener) {
|
||||
this.attachmentView = ViewUtil.findById(activity, R.id.attachment_editor);
|
||||
this.thumbnail = ViewUtil.findById(activity, R.id.attachment_thumbnail);
|
||||
this.audioView = ViewUtil.findById(activity, R.id.attachment_audio);
|
||||
this.removableMediaView = ViewUtil.findById(activity, R.id.removable_media_view);
|
||||
this.context = activity;
|
||||
this.attachmentListener = listener;
|
||||
|
||||
removableMediaView.setRemoveClickListener(new RemoveButtonListener());
|
||||
@ -76,11 +77,13 @@ public class AttachmentManager {
|
||||
animation.setAnimationListener(new Animation.AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
slideDeck.clear();
|
||||
slide = Optional.absent();
|
||||
thumbnail.clear();
|
||||
attachmentView.setVisibility(View.GONE);
|
||||
attachmentListener.onAttachmentChanged();
|
||||
@ -92,26 +95,39 @@ public class AttachmentManager {
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
if (captureUri != null) CaptureProvider.getInstance(context).delete(captureUri);
|
||||
cleanup(captureUri);
|
||||
cleanup(getSlideUri());
|
||||
|
||||
captureUri = null;
|
||||
slide = Optional.absent();
|
||||
}
|
||||
|
||||
private void cleanup(final @Nullable Uri uri) {
|
||||
if (uri != null && PersistentBlobProvider.isAuthority(context, uri)) {
|
||||
Log.w(TAG, "cleaning up " + uri);
|
||||
PersistentBlobProvider.getInstance(context).delete(uri);
|
||||
}
|
||||
}
|
||||
|
||||
private void setSlide(@NonNull Slide slide) {
|
||||
if (getSlideUri() != null) cleanup(getSlideUri());
|
||||
if (captureUri != null && slide.getUri() != captureUri) cleanup(captureUri);
|
||||
|
||||
this.captureUri = null;
|
||||
this.slide = Optional.of(slide);
|
||||
}
|
||||
|
||||
public void setMedia(@NonNull final MasterSecret masterSecret,
|
||||
@NonNull final Uri uri,
|
||||
@NonNull final MediaType mediaType,
|
||||
@NonNull final MediaConstraints constraints,
|
||||
final boolean isCapture)
|
||||
@NonNull final MediaConstraints constraints)
|
||||
{
|
||||
new AsyncTask<Void, Void, Slide>() {
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
slideDeck.clear();
|
||||
thumbnail.clear();
|
||||
thumbnail.showProgressSpinner();
|
||||
attachmentView.setVisibility(View.VISIBLE);
|
||||
|
||||
if (isCapture) captureUri = uri;
|
||||
if (!uri.equals(captureUri)) cleanup();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -141,7 +157,7 @@ public class AttachmentManager {
|
||||
R.string.ConversationActivity_attachment_exceeds_size_limits,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
slideDeck.addSlide(slide);
|
||||
setSlide(slide);
|
||||
attachmentView.setVisibility(View.VISIBLE);
|
||||
|
||||
if (slide.hasAudio()) {
|
||||
@ -162,9 +178,10 @@ public class AttachmentManager {
|
||||
return attachmentView.getVisibility() == View.VISIBLE;
|
||||
}
|
||||
|
||||
|
||||
public @NonNull SlideDeck getSlideDeck() {
|
||||
return slideDeck;
|
||||
public @NonNull SlideDeck buildSlideDeck() {
|
||||
SlideDeck deck = new SlideDeck();
|
||||
if (slide.isPresent()) deck.addSlide(slide.get());
|
||||
return deck;
|
||||
}
|
||||
|
||||
public static void selectVideo(Activity activity, int requestCode) {
|
||||
@ -184,7 +201,11 @@ public class AttachmentManager {
|
||||
activity.startActivityForResult(intent, requestCode);
|
||||
}
|
||||
|
||||
public Uri getCaptureUri() {
|
||||
private @Nullable Uri getSlideUri() {
|
||||
return slide.isPresent() ? slide.get().getUri() : null;
|
||||
}
|
||||
|
||||
public @Nullable Uri getCaptureUri() {
|
||||
return captureUri;
|
||||
}
|
||||
|
||||
@ -192,7 +213,10 @@ public class AttachmentManager {
|
||||
try {
|
||||
Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
if (captureIntent.resolveActivity(activity.getPackageManager()) != null) {
|
||||
captureUri = CaptureProvider.getInstance(context).createForExternal(recipients);
|
||||
if (captureUri == null) {
|
||||
captureUri = PersistentBlobProvider.getInstance(context).createForExternal(recipients);
|
||||
}
|
||||
Log.w(TAG, "captureUri path is " + captureUri.getPath());
|
||||
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, captureUri);
|
||||
activity.startActivityForResult(captureIntent, requestCode);
|
||||
}
|
||||
@ -237,8 +261,8 @@ public class AttachmentManager {
|
||||
private class RemoveButtonListener implements View.OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
clear();
|
||||
cleanup();
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ import android.support.annotation.NonNull;
|
||||
import org.thoughtcrime.securesms.attachments.AttachmentId;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.providers.CaptureProvider;
|
||||
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
|
||||
import org.thoughtcrime.securesms.providers.PartProvider;
|
||||
import org.thoughtcrime.securesms.providers.SingleUseBlobProvider;
|
||||
|
||||
@ -25,7 +25,7 @@ public class PartAuthority {
|
||||
|
||||
private static final int PART_ROW = 1;
|
||||
private static final int THUMB_ROW = 2;
|
||||
private static final int CAPTURE_ROW = 3;
|
||||
private static final int PERSISTENT_ROW = 3;
|
||||
private static final int SINGLE_USE_ROW = 4;
|
||||
|
||||
private static final UriMatcher uriMatcher;
|
||||
@ -34,7 +34,7 @@ public class PartAuthority {
|
||||
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
uriMatcher.addURI("org.thoughtcrime.securesms", "part/*/#", PART_ROW);
|
||||
uriMatcher.addURI("org.thoughtcrime.securesms", "thumb/*/#", THUMB_ROW);
|
||||
uriMatcher.addURI(CaptureProvider.AUTHORITY, CaptureProvider.EXPECTED_PATH, CAPTURE_ROW);
|
||||
uriMatcher.addURI(PersistentBlobProvider.AUTHORITY, PersistentBlobProvider.EXPECTED_PATH, PERSISTENT_ROW);
|
||||
uriMatcher.addURI(SingleUseBlobProvider.AUTHORITY, SingleUseBlobProvider.PATH, SINGLE_USE_ROW);
|
||||
}
|
||||
|
||||
@ -50,8 +50,8 @@ public class PartAuthority {
|
||||
case THUMB_ROW:
|
||||
partUri = new PartUriParser(uri);
|
||||
return DatabaseFactory.getAttachmentDatabase(context).getThumbnailStream(masterSecret, partUri.getPartId());
|
||||
case CAPTURE_ROW:
|
||||
return CaptureProvider.getInstance(context).getStream(masterSecret, ContentUris.parseId(uri));
|
||||
case PERSISTENT_ROW:
|
||||
return PersistentBlobProvider.getInstance(context).getStream(masterSecret, ContentUris.parseId(uri));
|
||||
case SINGLE_USE_ROW:
|
||||
return SingleUseBlobProvider.getInstance().getStream(ContentUris.parseId(uri));
|
||||
default:
|
||||
|
@ -6,7 +6,6 @@ import android.content.UriMatcher;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.util.SparseArrayCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.DecryptingPartInputStream;
|
||||
@ -21,25 +20,27 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class CaptureProvider {
|
||||
private static final String TAG = CaptureProvider.class.getSimpleName();
|
||||
public class PersistentBlobProvider {
|
||||
private static final String TAG = PersistentBlobProvider.class.getSimpleName();
|
||||
private static final String URI_STRING = "content://org.thoughtcrime.securesms/capture";
|
||||
public static final Uri CONTENT_URI = Uri.parse(URI_STRING);
|
||||
public static final String AUTHORITY = "org.thoughtcrime.securesms";
|
||||
public static final String EXPECTED_PATH = "capture/*/#";
|
||||
private static final int MATCH = 1;
|
||||
public static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH) {{
|
||||
private static final UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH) {{
|
||||
addURI(AUTHORITY, EXPECTED_PATH, MATCH);
|
||||
}};
|
||||
|
||||
private static volatile CaptureProvider instance;
|
||||
private static volatile PersistentBlobProvider instance;
|
||||
|
||||
public static CaptureProvider getInstance(Context context) {
|
||||
public static PersistentBlobProvider getInstance(Context context) {
|
||||
if (instance == null) {
|
||||
synchronized (CaptureProvider.class) {
|
||||
synchronized (PersistentBlobProvider.class) {
|
||||
if (instance == null) {
|
||||
instance = new CaptureProvider(context);
|
||||
instance = new PersistentBlobProvider(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -47,9 +48,9 @@ public class CaptureProvider {
|
||||
}
|
||||
|
||||
private final Context context;
|
||||
private final SparseArrayCompat<byte[]> cache = new SparseArrayCompat<>();
|
||||
private final Map<Long, byte[]> cache = new HashMap<>();
|
||||
|
||||
private CaptureProvider(Context context) {
|
||||
private PersistentBlobProvider(Context context) {
|
||||
this.context = context.getApplicationContext();
|
||||
}
|
||||
|
||||
@ -57,19 +58,31 @@ public class CaptureProvider {
|
||||
@NonNull Recipients recipients,
|
||||
@NonNull byte[] imageBytes)
|
||||
{
|
||||
final int id = generateId(recipients);
|
||||
final long id = generateId(recipients);
|
||||
cache.put(id, imageBytes);
|
||||
persistToDisk(masterSecret, id, imageBytes);
|
||||
return create(masterSecret, new ByteArrayInputStream(imageBytes), id);
|
||||
}
|
||||
|
||||
public Uri create(@NonNull MasterSecret masterSecret,
|
||||
@NonNull InputStream input)
|
||||
{
|
||||
return create(masterSecret, input, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
private Uri create(MasterSecret masterSecret, InputStream input, long id) {
|
||||
persistToDisk(masterSecret, id, input);
|
||||
final Uri uniqueUri = Uri.withAppendedPath(CONTENT_URI, String.valueOf(System.currentTimeMillis()));
|
||||
return ContentUris.withAppendedId(uniqueUri, id);
|
||||
}
|
||||
|
||||
private void persistToDisk(final MasterSecret masterSecret, final int id, final byte[] imageBytes) {
|
||||
private void persistToDisk(final MasterSecret masterSecret, final long id,
|
||||
final InputStream input)
|
||||
{
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override protected Void doInBackground(Void... params) {
|
||||
try {
|
||||
final OutputStream output = new EncryptingPartOutputStream(getFile(id), masterSecret);
|
||||
Util.copy(new ByteArrayInputStream(imageBytes), output);
|
||||
Util.copy(input, output);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
@ -83,23 +96,21 @@ public class CaptureProvider {
|
||||
}
|
||||
|
||||
public Uri createForExternal(@NonNull Recipients recipients) throws IOException {
|
||||
final File externalDir = context.getExternalFilesDir(null);
|
||||
if (externalDir == null) throw new IOException("no external files directory");
|
||||
return Uri.fromFile(new File(externalDir, String.valueOf(generateId(recipients)) + ".jpg"))
|
||||
return Uri.fromFile(new File(getExternalDir(context), String.valueOf(generateId(recipients)) + ".jpg"))
|
||||
.buildUpon()
|
||||
.appendQueryParameter("unique", String.valueOf(System.currentTimeMillis()))
|
||||
.build();
|
||||
}
|
||||
|
||||
public boolean delete(@NonNull Uri uri) {
|
||||
switch (uriMatcher.match(uri)) {
|
||||
switch (MATCHER.match(uri)) {
|
||||
case MATCH: return getFile(ContentUris.parseId(uri)).delete();
|
||||
default: return new File(uri.getPath()).delete();
|
||||
}
|
||||
}
|
||||
|
||||
public @NonNull InputStream getStream(MasterSecret masterSecret, long id) throws IOException {
|
||||
final byte[] cached = cache.get((int)id);
|
||||
final byte[] cached = cache.get(id);
|
||||
return cached != null ? new ByteArrayInputStream(cached)
|
||||
: new DecryptingPartInputStream(getFile(id), masterSecret);
|
||||
}
|
||||
@ -111,4 +122,18 @@ public class CaptureProvider {
|
||||
private File getFile(long id) {
|
||||
return new File(context.getDir("captures", Context.MODE_PRIVATE), id + ".jpg");
|
||||
}
|
||||
|
||||
private static @NonNull File getExternalDir(Context context) throws IOException {
|
||||
final File externalDir = context.getExternalFilesDir(null);
|
||||
if (externalDir == null) throw new IOException("no external files directory");
|
||||
return externalDir;
|
||||
}
|
||||
|
||||
public static boolean isAuthority(@NonNull Context context, @NonNull Uri uri) {
|
||||
try {
|
||||
return MATCHER.match(uri) == MATCH || uri.getPath().startsWith(getExternalDir(context).getAbsolutePath());
|
||||
} catch (IOException ioe) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -27,7 +27,7 @@ import java.util.Map;
|
||||
|
||||
public class SingleUseBlobProvider {
|
||||
|
||||
private static final String TAG = CaptureProvider.class.getSimpleName();
|
||||
private static final String TAG = SingleUseBlobProvider.class.getSimpleName();
|
||||
|
||||
public static final String AUTHORITY = "org.thoughtcrime.securesms";
|
||||
public static final String PATH = "memory/*/#";
|
||||
|
Loading…
Reference in New Issue
Block a user