Konverting MediaSendActivity (#921)

pull/1710/head
ThomasSession 2 months ago committed by GitHub
parent dc432ccaa5
commit b134b82daa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1,446 +0,0 @@
package org.thoughtcrime.securesms.mediasend;
import static org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.OvershootInterpolator;
import android.view.animation.ScaleAnimation;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.ViewModelProvider;
import com.squareup.phrase.Phrase;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import network.loki.messenger.R;
import org.session.libsession.utilities.Address;
import org.session.libsession.utilities.MediaTypes;
import org.session.libsession.utilities.Util;
import org.session.libsession.utilities.concurrent.SimpleTask;
import org.session.libsession.utilities.recipients.Recipient;
import org.session.libsignal.utilities.Log;
import org.session.libsignal.utilities.guava.Optional;
import org.thoughtcrime.securesms.ScreenLockActionBarActivity;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.scribbles.ImageEditorFragment;
import org.thoughtcrime.securesms.util.FilenameUtils;
/**
* Encompasses the entire flow of sending media, starting from the selection process to the actual
* captioning and editing of the content.
*
* This activity is intended to be launched via {@link #startActivityForResult(Intent, int)}.
* It will return the {@link Media} that the user decided to send.
*/
public class MediaSendActivity extends ScreenLockActionBarActivity implements MediaPickerFolderFragment.Controller,
MediaPickerItemFragment.Controller,
MediaSendFragment.Controller,
ImageEditorFragment.Controller,
Camera1Fragment.Controller
{
private static final String TAG = MediaSendActivity.class.getSimpleName();
public static final String EXTRA_MEDIA = "media";
public static final String EXTRA_MESSAGE = "message";
private static final String KEY_ADDRESS = "address";
private static final String KEY_BODY = "body";
private static final String KEY_MEDIA = "media";
private static final String KEY_IS_CAMERA = "is_camera";
private static final String TAG_FOLDER_PICKER = "folder_picker";
private static final String TAG_ITEM_PICKER = "item_picker";
private static final String TAG_SEND = "send";
private static final String TAG_CAMERA = "camera";
private Recipient recipient;
private MediaSendViewModel viewModel;
private View countButton;
private TextView countButtonText;
private View cameraButton;
/**
* Get an intent to launch the media send flow starting with the picker.
*/
public static Intent buildGalleryIntent(@NonNull Context context, @NonNull Recipient recipient, @NonNull String body) {
Intent intent = new Intent(context, MediaSendActivity.class);
intent.putExtra(KEY_ADDRESS, recipient.getAddress().serialize());
intent.putExtra(KEY_BODY, body);
return intent;
}
/**
* Get an intent to launch the media send flow starting with the camera.
*/
public static Intent buildCameraIntent(@NonNull Context context, @NonNull Recipient recipient) {
Intent intent = buildGalleryIntent(context, recipient, "");
intent.putExtra(KEY_IS_CAMERA, true);
return intent;
}
/**
* Get an intent to launch the media send flow with a specific list of media. Will jump right to
* the editor screen.
*/
public static Intent buildEditorIntent(@NonNull Context context,
@NonNull List<Media> media,
@NonNull Recipient recipient,
@NonNull String body)
{
Intent intent = buildGalleryIntent(context, recipient, body);
intent.putParcelableArrayListExtra(KEY_MEDIA, new ArrayList<>(media));
return intent;
}
@Override
protected void onCreate(Bundle savedInstanceState, boolean ready) {
super.onCreate(savedInstanceState, ready);
setContentView(R.layout.mediasend_activity);
setResult(RESULT_CANCELED);
if (savedInstanceState != null) { return; }
countButton = findViewById(R.id.mediasend_count_button);
countButtonText = findViewById(R.id.mediasend_count_button_text);
cameraButton = findViewById(R.id.mediasend_camera_button);
viewModel = new ViewModelProvider(this, new MediaSendViewModel.Factory(getApplication(), new MediaRepository())).get(MediaSendViewModel.class);
recipient = Recipient.from(this, Address.fromSerialized(getIntent().getStringExtra(KEY_ADDRESS)), true);
viewModel.onBodyChanged(getIntent().getStringExtra(KEY_BODY));
List<Media> media = getIntent().getParcelableArrayListExtra(KEY_MEDIA);
boolean isCamera = getIntent().getBooleanExtra(KEY_IS_CAMERA, false);
if (isCamera) {
Fragment fragment = Camera1Fragment.newInstance();
getSupportFragmentManager().beginTransaction()
.replace(R.id.mediasend_fragment_container, fragment, TAG_CAMERA)
.commit();
} else if (!Util.isEmpty(media)) {
viewModel.onSelectedMediaChanged(this, media);
Fragment fragment = MediaSendFragment.newInstance(recipient);
getSupportFragmentManager().beginTransaction()
.replace(R.id.mediasend_fragment_container, fragment, TAG_SEND)
.commit();
} else {
MediaPickerFolderFragment fragment = MediaPickerFolderFragment.newInstance(recipient);
getSupportFragmentManager().beginTransaction()
.replace(R.id.mediasend_fragment_container, fragment, TAG_FOLDER_PICKER)
.commit();
}
initializeCountButtonObserver();
initializeCameraButtonObserver();
initializeErrorObserver();
cameraButton.setOnClickListener(v -> {
int maxSelection = viewModel.getMaxSelection();
if (viewModel.getSelectedMedia().getValue() != null && viewModel.getSelectedMedia().getValue().size() >= maxSelection) {
Toast.makeText(this, getString(R.string.attachmentsErrorNumber), Toast.LENGTH_SHORT).show();
} else {
navigateToCamera();
}
});
}
@Override
public void onBackPressed() {
MediaSendFragment sendFragment = (MediaSendFragment) getSupportFragmentManager().findFragmentByTag(TAG_SEND);
if (sendFragment == null || !sendFragment.isVisible() || !sendFragment.handleBackPress()) {
super.onBackPressed();
if (getIntent().getBooleanExtra(KEY_IS_CAMERA, false) && getSupportFragmentManager().getBackStackEntryCount() == 0) {
viewModel.onImageCaptureUndo(this);
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
}
@Override
public void onFolderSelected(@NonNull MediaFolder folder) {
if (viewModel == null) { return; }
viewModel.onFolderSelected(folder.getBucketId());
MediaPickerItemFragment fragment = MediaPickerItemFragment.newInstance(folder.getBucketId(), folder.getTitle(), viewModel.getMaxSelection());
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.slide_from_right, R.anim.slide_to_left, R.anim.slide_from_left, R.anim.slide_to_right)
.replace(R.id.mediasend_fragment_container, fragment, TAG_ITEM_PICKER)
.addToBackStack(null)
.commit();
}
@Override
public void onMediaSelected(@NonNull Media media) {
viewModel.onSingleMediaSelected(this, media);
navigateToMediaSend(recipient);
}
@Override
public void onAddMediaClicked(@NonNull String bucketId) {
MediaPickerFolderFragment folderFragment = MediaPickerFolderFragment.newInstance(recipient);
MediaPickerItemFragment itemFragment = MediaPickerItemFragment.newInstance(bucketId, "", viewModel.getMaxSelection());
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.stationary, R.anim.slide_to_left, R.anim.slide_from_left, R.anim.slide_to_right)
.replace(R.id.mediasend_fragment_container, folderFragment, TAG_FOLDER_PICKER)
.addToBackStack(null)
.commit();
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.slide_from_right, R.anim.stationary, R.anim.slide_from_left, R.anim.slide_to_right)
.replace(R.id.mediasend_fragment_container, itemFragment, TAG_ITEM_PICKER)
.addToBackStack(null)
.commit();
}
@Override
public void onSendClicked(@NonNull List<Media> media, @NonNull String message) {
viewModel.onSendClicked();
ArrayList<Media> mediaList = new ArrayList<>(media);
Intent intent = new Intent();
intent.putParcelableArrayListExtra(EXTRA_MEDIA, mediaList);
intent.putExtra(EXTRA_MESSAGE, message);
setResult(RESULT_OK, intent);
finish();
overridePendingTransition(R.anim.stationary, R.anim.camera_slide_to_bottom);
}
@Override
public void onNoMediaAvailable() {
setResult(RESULT_CANCELED);
finish();
}
@Override
public void onTouchEventsNeeded(boolean needed) {
MediaSendFragment fragment = (MediaSendFragment) getSupportFragmentManager().findFragmentByTag(TAG_SEND);
if (fragment != null) {
fragment.onTouchEventsNeeded(needed);
}
}
@Override
public void onCameraError() {
Toast.makeText(this, R.string.cameraErrorUnavailable, Toast.LENGTH_SHORT).show();
setResult(RESULT_CANCELED, new Intent());
finish();
}
@Override
public void onImageCaptured(@NonNull byte[] data, int width, int height) {
Log.i(TAG, "Camera image captured.");
SimpleTask.run(getLifecycle(), () -> {
try {
Uri uri = BlobProvider.getInstance()
.forData(data)
.withMimeType(MediaTypes.IMAGE_JPEG)
.createForSingleSessionOnDisk(this, e -> Log.w(TAG, "Failed to write to disk.", e));
return new Media(uri,
FilenameUtils.constructPhotoFilename(this),
MediaTypes.IMAGE_JPEG,
System.currentTimeMillis(),
width,
height,
data.length,
Optional.of(Media.ALL_MEDIA_BUCKET_ID),
Optional.absent()
);
} catch (IOException e) {
return null;
}
}, media -> {
if (media == null) {
onNoMediaAvailable();
return;
}
Log.i(TAG, "Camera capture stored: " + media.getUri().toString());
viewModel.onImageCaptured(media);
navigateToMediaSend(recipient);
});
}
@Override
public int getDisplayRotation() {
return getWindowManager().getDefaultDisplay().getRotation();
}
private void initializeCountButtonObserver() {
viewModel.getCountButtonState().observe(this, buttonState -> {
if (buttonState == null) return;
countButtonText.setText(String.valueOf(buttonState.getCount()));
countButton.setEnabled(buttonState.isVisible());
animateButtonVisibility(countButton, countButton.getVisibility(), buttonState.isVisible() ? View.VISIBLE : View.GONE);
if (buttonState.getCount() > 0) {
countButton.setOnClickListener(v -> navigateToMediaSend(recipient));
if (buttonState.isVisible()) {
animateButtonTextChange(countButton);
}
} else {
countButton.setOnClickListener(null);
}
});
}
private void initializeCameraButtonObserver() {
viewModel.getCameraButtonVisibility().observe(this, visible -> {
if (visible == null) return;
animateButtonVisibility(cameraButton, cameraButton.getVisibility(), visible ? View.VISIBLE : View.GONE);
});
}
private void initializeErrorObserver() {
viewModel.getError().observe(this, error -> {
if (error == null) return;
switch (error) {
case ITEM_TOO_LARGE:
Toast.makeText(this, R.string.attachmentsErrorSize, Toast.LENGTH_LONG).show();
break;
case TOO_MANY_ITEMS:
// In modern session we'll say you can't sent more than 32 items, but if we ever want
// the exact count of how many items the user attempted to send it's: viewModel.getMaxSelection()
Toast.makeText(this, getString(R.string.attachmentsErrorNumber), Toast.LENGTH_SHORT).show();
break;
}
});
}
private void navigateToMediaSend(@NonNull Recipient recipient) {
MediaSendFragment fragment = MediaSendFragment.newInstance(recipient);
String backstackTag = null;
if (getSupportFragmentManager().findFragmentByTag(TAG_SEND) != null) {
getSupportFragmentManager().popBackStack(TAG_SEND, FragmentManager.POP_BACK_STACK_INCLUSIVE);
backstackTag = TAG_SEND;
}
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.slide_from_right, R.anim.slide_to_left, R.anim.slide_from_left, R.anim.slide_to_right)
.replace(R.id.mediasend_fragment_container, fragment, TAG_SEND)
.addToBackStack(backstackTag)
.commit();
}
private void navigateToCamera() {
Context c = getApplicationContext();
String permanentDenialTxt = Phrase.from(c, R.string.permissionsCameraDenied)
.put(APP_NAME_KEY, c.getString(R.string.app_name))
.format().toString();
String requireCameraPermissionsTxt = Phrase.from(c, R.string.cameraGrantAccessDescription)
.put(APP_NAME_KEY, c.getString(R.string.app_name))
.format().toString();
Permissions.with(this)
.request(Manifest.permission.CAMERA)
.withPermanentDenialDialog(permanentDenialTxt)
.onAllGranted(() -> {
Camera1Fragment fragment = getOrCreateCameraFragment();
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.slide_from_right, R.anim.slide_to_left, R.anim.slide_from_left, R.anim.slide_to_right)
.replace(R.id.mediasend_fragment_container, fragment, TAG_CAMERA)
.addToBackStack(null)
.commit();
})
.onAnyDenied(() -> Toast.makeText(MediaSendActivity.this, requireCameraPermissionsTxt, Toast.LENGTH_LONG).show())
.execute();
}
private Camera1Fragment getOrCreateCameraFragment() {
Camera1Fragment fragment = (Camera1Fragment) getSupportFragmentManager().findFragmentByTag(TAG_CAMERA);
return fragment != null ? fragment
: Camera1Fragment.newInstance();
}
private void animateButtonVisibility(@NonNull View button, int oldVisibility, int newVisibility) {
if (oldVisibility == newVisibility) return;
if (button.getAnimation() != null) {
button.clearAnimation();
button.setVisibility(newVisibility);
} else if (newVisibility == View.VISIBLE) {
button.setVisibility(View.VISIBLE);
Animation animation = new ScaleAnimation(0, 1, 0, 1, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
animation.setDuration(250);
animation.setInterpolator(new OvershootInterpolator());
button.startAnimation(animation);
} else {
Animation animation = new ScaleAnimation(1, 0, 1, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
animation.setDuration(150);
animation.setInterpolator(new AccelerateDecelerateInterpolator());
animation.setAnimationListener(new SimpleAnimationListener() {
@Override
public void onAnimationEnd(Animation animation) {
button.clearAnimation();
button.setVisibility(View.GONE);
}
});
button.startAnimation(animation);
}
}
private void animateButtonTextChange(@NonNull View button) {
if (button.getAnimation() != null) {
button.clearAnimation();
}
Animation grow = new ScaleAnimation(1f, 1.3f, 1f, 1.3f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
grow.setDuration(125);
grow.setInterpolator(new AccelerateInterpolator());
grow.setAnimationListener(new SimpleAnimationListener() {
@Override
public void onAnimationEnd(Animation animation) {
Animation shrink = new ScaleAnimation(1.3f, 1f, 1.3f, 1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
shrink.setDuration(125);
shrink.setInterpolator(new DecelerateInterpolator());
button.startAnimation(shrink);
}
});
button.startAnimation(grow);
}
@Override
public void onRequestFullScreen(boolean fullScreen) {
MediaSendFragment sendFragment = (MediaSendFragment) getSupportFragmentManager().findFragmentByTag(TAG_SEND);
if (sendFragment != null && sendFragment.isVisible()) {
sendFragment.onRequestFullScreen(fullScreen);
}
}
}

@ -0,0 +1,557 @@
package org.thoughtcrime.securesms.mediasend
import android.Manifest
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
import android.view.animation.AccelerateInterpolator
import android.view.animation.Animation
import android.view.animation.DecelerateInterpolator
import android.view.animation.OvershootInterpolator
import android.view.animation.ScaleAnimation
import android.widget.TextView
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.ViewModelProvider
import com.squareup.phrase.Phrase
import network.loki.messenger.R
import org.session.libsession.utilities.Address.Companion.fromSerialized
import org.session.libsession.utilities.MediaTypes
import org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY
import org.session.libsession.utilities.Util.isEmpty
import org.session.libsession.utilities.concurrent.SimpleTask
import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.guava.Optional
import org.thoughtcrime.securesms.ScreenLockActionBarActivity
import org.thoughtcrime.securesms.mediasend.MediaSendViewModel.CountButtonState
import org.thoughtcrime.securesms.permissions.Permissions
import org.thoughtcrime.securesms.providers.BlobProvider
import org.thoughtcrime.securesms.scribbles.ImageEditorFragment
import org.thoughtcrime.securesms.util.FilenameUtils.constructPhotoFilename
import java.io.IOException
/**
* Encompasses the entire flow of sending media, starting from the selection process to the actual
* captioning and editing of the content.
*
* This activity is intended to be launched via [.startActivityForResult].
* It will return the [Media] that the user decided to send.
*/
class MediaSendActivity : ScreenLockActionBarActivity(), MediaPickerFolderFragment.Controller,
MediaPickerItemFragment.Controller, MediaSendFragment.Controller,
ImageEditorFragment.Controller,
Camera1Fragment.Controller {
private var recipient: Recipient? = null
private var viewModel: MediaSendViewModel? = null
private var countButton: View? = null
private var countButtonText: TextView? = null
private var cameraButton: View? = null
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
super.onCreate(savedInstanceState, ready)
setContentView(R.layout.mediasend_activity)
setResult(RESULT_CANCELED)
if (savedInstanceState != null) {
return
}
countButton = findViewById(R.id.mediasend_count_button)
countButtonText = findViewById(R.id.mediasend_count_button_text)
cameraButton = findViewById(R.id.mediasend_camera_button)
viewModel = ViewModelProvider(
this, MediaSendViewModel.Factory(
application, MediaRepository()
)
).get(
MediaSendViewModel::class.java
)
recipient = Recipient.from(
this, fromSerialized(
intent.getStringExtra(KEY_ADDRESS)!!
), true
)
viewModel!!.onBodyChanged(intent.getStringExtra(KEY_BODY)!!)
val media: List<Media?>? = intent.getParcelableArrayListExtra(KEY_MEDIA)
val isCamera = intent.getBooleanExtra(KEY_IS_CAMERA, false)
if (isCamera) {
val fragment: Fragment = Camera1Fragment.newInstance()
supportFragmentManager.beginTransaction()
.replace(R.id.mediasend_fragment_container, fragment, TAG_CAMERA)
.commit()
} else if (!isEmpty(media)) {
viewModel!!.onSelectedMediaChanged(this, media!!)
val fragment: Fragment = MediaSendFragment.newInstance(recipient!!)
supportFragmentManager.beginTransaction()
.replace(R.id.mediasend_fragment_container, fragment, TAG_SEND)
.commit()
} else {
val fragment = MediaPickerFolderFragment.newInstance(
recipient!!
)
supportFragmentManager.beginTransaction()
.replace(R.id.mediasend_fragment_container, fragment, TAG_FOLDER_PICKER)
.commit()
}
initializeCountButtonObserver()
initializeCameraButtonObserver()
initializeErrorObserver()
cameraButton?.setOnClickListener { v: View? ->
val maxSelection = viewModel!!.maxSelection
if (viewModel!!.selectedMedia.value != null && viewModel!!.selectedMedia.value!!.size >= maxSelection) {
Toast.makeText(this, getString(R.string.attachmentsErrorNumber), Toast.LENGTH_SHORT)
.show()
} else {
navigateToCamera()
}
}
}
override fun onBackPressed() {
val sendFragment = supportFragmentManager.findFragmentByTag(TAG_SEND) as MediaSendFragment?
if (sendFragment == null || !sendFragment.isVisible || !sendFragment.handleBackPress()) {
super.onBackPressed()
if (intent.getBooleanExtra(
KEY_IS_CAMERA,
false
) && supportFragmentManager.backStackEntryCount == 0
) {
viewModel!!.onImageCaptureUndo(this)
}
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults)
}
override fun onFolderSelected(folder: MediaFolder) {
if (viewModel == null) {
return
}
viewModel!!.onFolderSelected(folder.bucketId)
val fragment = MediaPickerItemFragment.newInstance(
folder.bucketId,
folder.title,
viewModel!!.maxSelection
)
supportFragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.slide_from_right,
R.anim.slide_to_left,
R.anim.slide_from_left,
R.anim.slide_to_right
)
.replace(R.id.mediasend_fragment_container, fragment, TAG_ITEM_PICKER)
.addToBackStack(null)
.commit()
}
override fun onMediaSelected(media: Media) {
viewModel!!.onSingleMediaSelected(this, media)
navigateToMediaSend(recipient!!)
}
override fun onAddMediaClicked(bucketId: String) {
val folderFragment = MediaPickerFolderFragment.newInstance(
recipient!!
)
val itemFragment =
MediaPickerItemFragment.newInstance(bucketId, "", viewModel!!.maxSelection)
supportFragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.stationary,
R.anim.slide_to_left,
R.anim.slide_from_left,
R.anim.slide_to_right
)
.replace(R.id.mediasend_fragment_container, folderFragment, TAG_FOLDER_PICKER)
.addToBackStack(null)
.commit()
supportFragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.slide_from_right,
R.anim.stationary,
R.anim.slide_from_left,
R.anim.slide_to_right
)
.replace(R.id.mediasend_fragment_container, itemFragment, TAG_ITEM_PICKER)
.addToBackStack(null)
.commit()
}
override fun onSendClicked(media: List<Media>, message: String) {
viewModel!!.onSendClicked()
val mediaList = ArrayList(media)
val intent = Intent()
intent.putParcelableArrayListExtra(EXTRA_MEDIA, mediaList)
intent.putExtra(EXTRA_MESSAGE, message)
setResult(RESULT_OK, intent)
finish()
overridePendingTransition(R.anim.stationary, R.anim.camera_slide_to_bottom)
}
override fun onNoMediaAvailable() {
setResult(RESULT_CANCELED)
finish()
}
override fun onTouchEventsNeeded(needed: Boolean) {
val fragment = supportFragmentManager.findFragmentByTag(TAG_SEND) as MediaSendFragment?
fragment?.onTouchEventsNeeded(needed)
}
override fun onCameraError() {
Toast.makeText(this, R.string.cameraErrorUnavailable, Toast.LENGTH_SHORT).show()
setResult(RESULT_CANCELED, Intent())
finish()
}
override fun onImageCaptured(data: ByteArray, width: Int, height: Int) {
Log.i(TAG, "Camera image captured.")
SimpleTask.run(lifecycle, {
try {
val uri = BlobProvider.getInstance()
.forData(data)
.withMimeType(MediaTypes.IMAGE_JPEG)
.createForSingleSessionOnDisk(
this
) { e: IOException? ->
Log.w(
TAG,
"Failed to write to disk.",
e
)
}
return@run Media(
uri,
constructPhotoFilename(this),
MediaTypes.IMAGE_JPEG,
System.currentTimeMillis(),
width,
height,
data.size.toLong(),
Optional.of<String>(Media.ALL_MEDIA_BUCKET_ID),
Optional.absent<String>()
)
} catch (e: IOException) {
return@run null
}
}, { media: Media? ->
if (media == null) {
onNoMediaAvailable()
return@run
}
Log.i(TAG, "Camera capture stored: " + media.uri.toString())
viewModel!!.onImageCaptured(media)
navigateToMediaSend(recipient!!)
})
}
override fun getDisplayRotation(): Int {
return windowManager.defaultDisplay.rotation
}
private fun initializeCountButtonObserver() {
viewModel!!.countButtonState.observe(
this
) { buttonState: CountButtonState? ->
if (buttonState == null) return@observe
countButtonText!!.text = buttonState.count.toString()
countButton!!.isEnabled = buttonState.isVisible
animateButtonVisibility(
countButton!!,
countButton!!.visibility,
if (buttonState.isVisible) View.VISIBLE else View.GONE
)
if (buttonState.count > 0) {
countButton!!.setOnClickListener { v: View? ->
navigateToMediaSend(
recipient!!
)
}
if (buttonState.isVisible) {
animateButtonTextChange(countButton!!)
}
} else {
countButton!!.setOnClickListener(null)
}
}
}
private fun initializeCameraButtonObserver() {
viewModel!!.cameraButtonVisibility.observe(
this
) { visible: Boolean? ->
if (visible == null) return@observe
animateButtonVisibility(
cameraButton!!,
cameraButton!!.visibility,
if (visible) View.VISIBLE else View.GONE
)
}
}
private fun initializeErrorObserver() {
viewModel!!.error.observe(
this
) { error: MediaSendViewModel.Error? ->
if (error == null) return@observe
when (error) {
MediaSendViewModel.Error.ITEM_TOO_LARGE -> Toast.makeText(
this,
R.string.attachmentsErrorSize,
Toast.LENGTH_LONG
).show()
MediaSendViewModel.Error.TOO_MANY_ITEMS -> // In modern session we'll say you can't sent more than 32 items, but if we ever want
// the exact count of how many items the user attempted to send it's: viewModel.getMaxSelection()
Toast.makeText(
this,
getString(R.string.attachmentsErrorNumber),
Toast.LENGTH_SHORT
).show()
}
}
}
private fun navigateToMediaSend(recipient: Recipient) {
val fragment = MediaSendFragment.newInstance(recipient)
var backstackTag: String? = null
if (supportFragmentManager.findFragmentByTag(TAG_SEND) != null) {
supportFragmentManager.popBackStack(TAG_SEND, FragmentManager.POP_BACK_STACK_INCLUSIVE)
backstackTag = TAG_SEND
}
supportFragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.slide_from_right,
R.anim.slide_to_left,
R.anim.slide_from_left,
R.anim.slide_to_right
)
.replace(R.id.mediasend_fragment_container, fragment, TAG_SEND)
.addToBackStack(backstackTag)
.commit()
}
private fun navigateToCamera() {
val c = applicationContext
val permanentDenialTxt = Phrase.from(c, R.string.permissionsCameraDenied)
.put(APP_NAME_KEY, c.getString(R.string.app_name))
.format().toString()
val requireCameraPermissionsTxt = Phrase.from(c, R.string.cameraGrantAccessDescription)
.put(APP_NAME_KEY, c.getString(R.string.app_name))
.format().toString()
Permissions.with(this)
.request(Manifest.permission.CAMERA)
.withPermanentDenialDialog(permanentDenialTxt)
.onAllGranted {
val fragment = orCreateCameraFragment
supportFragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.slide_from_right,
R.anim.slide_to_left,
R.anim.slide_from_left,
R.anim.slide_to_right
)
.replace(
R.id.mediasend_fragment_container,
fragment,
TAG_CAMERA
)
.addToBackStack(null)
.commit()
}
.onAnyDenied {
Toast.makeText(
this@MediaSendActivity,
requireCameraPermissionsTxt,
Toast.LENGTH_LONG
).show()
}
.execute()
}
private val orCreateCameraFragment: Camera1Fragment
get() {
val fragment =
supportFragmentManager.findFragmentByTag(TAG_CAMERA) as Camera1Fragment?
return fragment ?: Camera1Fragment.newInstance()
}
private fun animateButtonVisibility(button: View, oldVisibility: Int, newVisibility: Int) {
if (oldVisibility == newVisibility) return
if (button.animation != null) {
button.clearAnimation()
button.visibility = newVisibility
} else if (newVisibility == View.VISIBLE) {
button.visibility = View.VISIBLE
val animation: Animation = ScaleAnimation(
0f,
1f,
0f,
1f,
Animation.RELATIVE_TO_SELF,
0.5f,
Animation.RELATIVE_TO_SELF,
0.5f
)
animation.duration = 250
animation.interpolator = OvershootInterpolator()
button.startAnimation(animation)
} else {
val animation: Animation = ScaleAnimation(
1f,
0f,
1f,
0f,
Animation.RELATIVE_TO_SELF,
0.5f,
Animation.RELATIVE_TO_SELF,
0.5f
)
animation.duration = 150
animation.interpolator = AccelerateDecelerateInterpolator()
animation.setAnimationListener(object : SimpleAnimationListener() {
override fun onAnimationEnd(animation: Animation) {
button.clearAnimation()
button.visibility = View.GONE
}
})
button.startAnimation(animation)
}
}
private fun animateButtonTextChange(button: View) {
if (button.animation != null) {
button.clearAnimation()
}
val grow: Animation = ScaleAnimation(
1f,
1.3f,
1f,
1.3f,
Animation.RELATIVE_TO_SELF,
0.5f,
Animation.RELATIVE_TO_SELF,
0.5f
)
grow.duration = 125
grow.interpolator = AccelerateInterpolator()
grow.setAnimationListener(object : SimpleAnimationListener() {
override fun onAnimationEnd(animation: Animation) {
val shrink: Animation = ScaleAnimation(
1.3f,
1f,
1.3f,
1f,
Animation.RELATIVE_TO_SELF,
0.5f,
Animation.RELATIVE_TO_SELF,
0.5f
)
shrink.duration = 125
shrink.interpolator = DecelerateInterpolator()
button.startAnimation(shrink)
}
})
button.startAnimation(grow)
}
override fun onRequestFullScreen(fullScreen: Boolean) {
val sendFragment = supportFragmentManager.findFragmentByTag(TAG_SEND) as MediaSendFragment?
if (sendFragment != null && sendFragment.isVisible) {
sendFragment.onRequestFullScreen(fullScreen)
}
}
companion object {
private val TAG: String = MediaSendActivity::class.java.simpleName
const val EXTRA_MEDIA: String = "media"
const val EXTRA_MESSAGE: String = "message"
private const val KEY_ADDRESS = "address"
private const val KEY_BODY = "body"
private const val KEY_MEDIA = "media"
private const val KEY_IS_CAMERA = "is_camera"
private const val TAG_FOLDER_PICKER = "folder_picker"
private const val TAG_ITEM_PICKER = "item_picker"
private const val TAG_SEND = "send"
private const val TAG_CAMERA = "camera"
/**
* Get an intent to launch the media send flow starting with the picker.
*/
@JvmStatic
fun buildGalleryIntent(context: Context, recipient: Recipient, body: String): Intent {
val intent = Intent(context, MediaSendActivity::class.java)
intent.putExtra(KEY_ADDRESS, recipient.address.serialize())
intent.putExtra(KEY_BODY, body)
return intent
}
/**
* Get an intent to launch the media send flow starting with the camera.
*/
@JvmStatic
fun buildCameraIntent(context: Context, recipient: Recipient): Intent {
val intent = buildGalleryIntent(context, recipient, "")
intent.putExtra(KEY_IS_CAMERA, true)
return intent
}
/**
* Get an intent to launch the media send flow with a specific list of media. Will jump right to
* the editor screen.
*/
fun buildEditorIntent(
context: Context,
media: List<Media>,
recipient: Recipient,
body: String
): Intent {
val intent = buildGalleryIntent(context, recipient, body)
intent.putParcelableArrayListExtra(KEY_MEDIA, ArrayList(media))
return intent
}
}
}
Loading…
Cancel
Save