mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-16 12:49:55 +01:00
[android] Swipe left/right in image screen to see other images from the same message
This commit is contained in:
@@ -20,6 +20,7 @@ import org.briarproject.briar.android.contact.ContactModule;
|
|||||||
import org.briarproject.briar.android.conversation.AliasDialogFragment;
|
import org.briarproject.briar.android.conversation.AliasDialogFragment;
|
||||||
import org.briarproject.briar.android.conversation.ConversationActivity;
|
import org.briarproject.briar.android.conversation.ConversationActivity;
|
||||||
import org.briarproject.briar.android.conversation.ImageActivity;
|
import org.briarproject.briar.android.conversation.ImageActivity;
|
||||||
|
import org.briarproject.briar.android.conversation.ImageFragment;
|
||||||
import org.briarproject.briar.android.forum.CreateForumActivity;
|
import org.briarproject.briar.android.forum.CreateForumActivity;
|
||||||
import org.briarproject.briar.android.forum.ForumActivity;
|
import org.briarproject.briar.android.forum.ForumActivity;
|
||||||
import org.briarproject.briar.android.forum.ForumListFragment;
|
import org.briarproject.briar.android.forum.ForumListFragment;
|
||||||
@@ -218,4 +219,6 @@ public interface ActivityComponent {
|
|||||||
|
|
||||||
void inject(AliasDialogFragment aliasDialogFragment);
|
void inject(AliasDialogFragment aliasDialogFragment);
|
||||||
|
|
||||||
|
void inject(ImageFragment imageFragment);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.briarproject.briar.android.conversation;
|
|||||||
|
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
@@ -111,4 +112,10 @@ public class AttachmentItem implements Parcelable {
|
|||||||
dest.writeByte((byte) (hasError ? 1 : 0));
|
dest.writeByte((byte) (hasError ? 1 : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(@Nullable Object o) {
|
||||||
|
return o instanceof AttachmentItem &&
|
||||||
|
messageId.equals(((AttachmentItem) o).messageId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,8 +121,9 @@ import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
|||||||
import static org.briarproject.briar.android.TestingConstants.FEATURE_FLAG_IMAGE_ATTACHMENTS;
|
import static org.briarproject.briar.android.TestingConstants.FEATURE_FLAG_IMAGE_ATTACHMENTS;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_ATTACH_IMAGE;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_ATTACH_IMAGE;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_INTRODUCTION;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_INTRODUCTION;
|
||||||
import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHMENT;
|
import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHMENT_POSITION;
|
||||||
import static org.briarproject.briar.android.conversation.ImageActivity.DATE;
|
import static org.briarproject.briar.android.conversation.ImageActivity.DATE;
|
||||||
|
import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHMENTS;
|
||||||
import static org.briarproject.briar.android.conversation.ImageActivity.NAME;
|
import static org.briarproject.briar.android.conversation.ImageActivity.NAME;
|
||||||
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName;
|
import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName;
|
||||||
@@ -812,8 +813,11 @@ public class ConversationActivity extends BriarActivity
|
|||||||
} else {
|
} else {
|
||||||
name = getString(R.string.you);
|
name = getString(R.string.you);
|
||||||
}
|
}
|
||||||
|
ArrayList<AttachmentItem> attachments =
|
||||||
|
new ArrayList<>(messageItem.getAttachments());
|
||||||
Intent i = new Intent(this, ImageActivity.class);
|
Intent i = new Intent(this, ImageActivity.class);
|
||||||
i.putExtra(ATTACHMENT, item);
|
i.putParcelableArrayListExtra(ATTACHMENTS, attachments);
|
||||||
|
i.putExtra(ATTACHMENT_POSITION, attachments.indexOf(item));
|
||||||
i.putExtra(NAME, name);
|
i.putExtra(NAME, name);
|
||||||
i.putExtra(DATE, messageItem.getTime());
|
i.putExtra(DATE, messageItem.getTime());
|
||||||
if (SDK_INT >= 23) {
|
if (SDK_INT >= 23) {
|
||||||
|
|||||||
@@ -4,15 +4,18 @@ import android.arch.lifecycle.ViewModelProvider;
|
|||||||
import android.arch.lifecycle.ViewModelProviders;
|
import android.arch.lifecycle.ViewModelProviders;
|
||||||
import android.content.DialogInterface.OnClickListener;
|
import android.content.DialogInterface.OnClickListener;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.drawable.Animatable;
|
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.RequiresApi;
|
import android.support.annotation.RequiresApi;
|
||||||
import android.support.design.widget.AppBarLayout;
|
import android.support.design.widget.AppBarLayout;
|
||||||
import android.support.design.widget.Snackbar;
|
import android.support.design.widget.Snackbar;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v4.app.FragmentManager;
|
||||||
|
import android.support.v4.app.FragmentStatePagerAdapter;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.support.v4.graphics.drawable.DrawableCompat;
|
import android.support.v4.graphics.drawable.DrawableCompat;
|
||||||
|
import android.support.v4.view.ViewPager;
|
||||||
import android.support.v7.app.AlertDialog.Builder;
|
import android.support.v7.app.AlertDialog.Builder;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.transition.Fade;
|
import android.transition.Fade;
|
||||||
@@ -20,23 +23,18 @@ import android.transition.Transition;
|
|||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.bumptech.glide.load.DataSource;
|
|
||||||
import com.bumptech.glide.load.engine.GlideException;
|
|
||||||
import com.bumptech.glide.request.RequestListener;
|
|
||||||
import com.bumptech.glide.request.target.Target;
|
|
||||||
import com.github.chrisbanes.photoview.PhotoView;
|
|
||||||
|
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.activity.BriarActivity;
|
import org.briarproject.briar.android.activity.BriarActivity;
|
||||||
import org.briarproject.briar.android.conversation.glide.GlideApp;
|
|
||||||
import org.briarproject.briar.android.view.PullDownLayout;
|
import org.briarproject.briar.android.view.PullDownLayout;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@@ -53,16 +51,15 @@ import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
|
|||||||
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
|
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
|
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
|
||||||
import static android.widget.ImageView.ScaleType.FIT_START;
|
|
||||||
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_SAVE_ATTACHMENT;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_SAVE_ATTACHMENT;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.formatDateAbsolute;
|
import static org.briarproject.briar.android.util.UiUtils.formatDateAbsolute;
|
||||||
|
|
||||||
public class ImageActivity extends BriarActivity
|
public class ImageActivity extends BriarActivity
|
||||||
implements PullDownLayout.Callback {
|
implements PullDownLayout.Callback, OnGlobalLayoutListener {
|
||||||
|
|
||||||
final static String ATTACHMENT = "attachment";
|
final static String ATTACHMENTS = "attachments";
|
||||||
|
final static String ATTACHMENT_POSITION = "position";
|
||||||
final static String NAME = "name";
|
final static String NAME = "name";
|
||||||
final static String DATE = "date";
|
final static String DATE = "date";
|
||||||
|
|
||||||
@@ -72,8 +69,8 @@ public class ImageActivity extends BriarActivity
|
|||||||
private ImageViewModel viewModel;
|
private ImageViewModel viewModel;
|
||||||
private PullDownLayout layout;
|
private PullDownLayout layout;
|
||||||
private AppBarLayout appBarLayout;
|
private AppBarLayout appBarLayout;
|
||||||
private PhotoView photoView;
|
private ViewPager viewPager;
|
||||||
private AttachmentItem attachment;
|
private List<AttachmentItem> attachments;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void injectActivity(ActivityComponent component) {
|
public void injectActivity(ActivityComponent component) {
|
||||||
@@ -102,6 +99,7 @@ public class ImageActivity extends BriarActivity
|
|||||||
layout = findViewById(R.id.layout);
|
layout = findViewById(R.id.layout);
|
||||||
layout.getBackground().setAlpha(255);
|
layout.getBackground().setAlpha(255);
|
||||||
layout.setCallback(this);
|
layout.setCallback(this);
|
||||||
|
layout.getViewTreeObserver().addOnGlobalLayoutListener(this);
|
||||||
|
|
||||||
// Status Bar
|
// Status Bar
|
||||||
if (SDK_INT >= 21) {
|
if (SDK_INT >= 21) {
|
||||||
@@ -118,59 +116,29 @@ public class ImageActivity extends BriarActivity
|
|||||||
TextView dateView = toolbar.findViewById(R.id.dateView);
|
TextView dateView = toolbar.findViewById(R.id.dateView);
|
||||||
|
|
||||||
// Intent Extras
|
// Intent Extras
|
||||||
attachment = getIntent().getParcelableExtra(ATTACHMENT);
|
Intent i = getIntent();
|
||||||
String name = getIntent().getStringExtra(NAME);
|
attachments = i.getParcelableArrayListExtra(ATTACHMENTS);
|
||||||
long time = getIntent().getLongExtra(DATE, 0);
|
int position = i.getIntExtra(ATTACHMENT_POSITION, -1);
|
||||||
|
if (position == -1) throw new IllegalStateException();
|
||||||
|
String name = i.getStringExtra(NAME);
|
||||||
|
long time = i.getLongExtra(DATE, 0);
|
||||||
String date = formatDateAbsolute(this, time);
|
String date = formatDateAbsolute(this, time);
|
||||||
contactName.setText(name);
|
contactName.setText(name);
|
||||||
dateView.setText(date);
|
dateView.setText(date);
|
||||||
|
|
||||||
// Image View
|
// Set up image ViewPager
|
||||||
photoView = findViewById(R.id.photoView);
|
viewPager = findViewById(R.id.viewPager);
|
||||||
|
ImagePagerAdapter pagerAdapter =
|
||||||
|
new ImagePagerAdapter(getSupportFragmentManager());
|
||||||
|
viewPager.setAdapter(pagerAdapter);
|
||||||
|
viewPager.setCurrentItem(position);
|
||||||
|
|
||||||
if (SDK_INT >= 16) {
|
if (SDK_INT >= 16) {
|
||||||
photoView.setOnClickListener(view -> toggleSystemUi());
|
viewModel.getOnImageClicked().observe(this, this::onPhotoClicked);
|
||||||
window.getDecorView().setSystemUiVisibility(
|
window.getDecorView().setSystemUiVisibility(
|
||||||
SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||||
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request Listener
|
|
||||||
RequestListener<Drawable> listener = new RequestListener<Drawable>() {
|
|
||||||
@Override
|
|
||||||
public boolean onLoadFailed(@Nullable GlideException e,
|
|
||||||
Object model, Target<Drawable> target,
|
|
||||||
boolean isFirstResource) {
|
|
||||||
supportStartPostponedEnterTransition();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onResourceReady(Drawable resource, Object model,
|
|
||||||
Target<Drawable> target, DataSource dataSource,
|
|
||||||
boolean isFirstResource) {
|
|
||||||
if (SDK_INT >= 21 && !(resource instanceof Animatable)) {
|
|
||||||
// set transition name only when not animatable,
|
|
||||||
// because the animation won't start otherwise
|
|
||||||
photoView.setTransitionName(
|
|
||||||
attachment.getTransitionName());
|
|
||||||
}
|
|
||||||
// Move image to the top if overlapping toolbar
|
|
||||||
if (isOverlappingToolbar(resource)) {
|
|
||||||
photoView.setScaleType(FIT_START);
|
|
||||||
}
|
|
||||||
supportStartPostponedEnterTransition();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load Image
|
|
||||||
GlideApp.with(this)
|
|
||||||
.load(attachment)
|
|
||||||
.diskCacheStrategy(NONE)
|
|
||||||
.error(R.drawable.ic_image_broken)
|
|
||||||
.dontTransform()
|
|
||||||
.addListener(listener)
|
|
||||||
.into(photoView);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -193,11 +161,21 @@ public class ImageActivity extends BriarActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGlobalLayout() {
|
||||||
|
viewModel.setToolbarPosition(
|
||||||
|
appBarLayout.getTop(), appBarLayout.getBottom()
|
||||||
|
);
|
||||||
|
if (SDK_INT >= 16) {
|
||||||
|
layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int request, int result, Intent data) {
|
protected void onActivityResult(int request, int result, Intent data) {
|
||||||
super.onActivityResult(request, result, data);
|
super.onActivityResult(request, result, data);
|
||||||
if (request == REQUEST_SAVE_ATTACHMENT && result == RESULT_OK) {
|
if (request == REQUEST_SAVE_ATTACHMENT && result == RESULT_OK) {
|
||||||
viewModel.saveImage(attachment, data.getData());
|
viewModel.saveImage(getVisibleAttachment(), data.getData());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,6 +203,13 @@ public class ImageActivity extends BriarActivity
|
|||||||
supportFinishAfterTransition();
|
supportFinishAfterTransition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onPhotoClicked(@Nullable Boolean clicked) {
|
||||||
|
if (clicked != null&& clicked && SDK_INT >= 16) {
|
||||||
|
toggleSystemUi();
|
||||||
|
viewModel.onOnImageClickSeen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@RequiresApi(api = 16)
|
@RequiresApi(api = 16)
|
||||||
private void toggleSystemUi() {
|
private void toggleSystemUi() {
|
||||||
View decorView = getWindow().getDecorView();
|
View decorView = getWindow().getDecorView();
|
||||||
@@ -259,29 +244,13 @@ public class ImageActivity extends BriarActivity
|
|||||||
.start();
|
.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isOverlappingToolbar(Drawable drawable) {
|
|
||||||
int width = drawable.getIntrinsicWidth();
|
|
||||||
int height = drawable.getIntrinsicHeight();
|
|
||||||
float widthPercentage = photoView.getWidth() / (float) width;
|
|
||||||
float heightPercentage = photoView.getHeight() / (float) height;
|
|
||||||
float scaleFactor = Math.min(widthPercentage, heightPercentage);
|
|
||||||
int realWidth = (int) (width * scaleFactor);
|
|
||||||
int realHeight = (int) (height * scaleFactor);
|
|
||||||
// return if photo doesn't use the full width,
|
|
||||||
// because it will be moved to the right otherwise
|
|
||||||
if (realWidth < photoView.getWidth()) return false;
|
|
||||||
int drawableTop = (photoView.getHeight() - realHeight) / 2;
|
|
||||||
return drawableTop < appBarLayout.getBottom() &&
|
|
||||||
drawableTop != appBarLayout.getTop();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showSaveImageDialog() {
|
private void showSaveImageDialog() {
|
||||||
OnClickListener okListener = (dialog, which) -> {
|
OnClickListener okListener = (dialog, which) -> {
|
||||||
if (SDK_INT >= 19) {
|
if (SDK_INT >= 19) {
|
||||||
Intent intent = getCreationIntent();
|
Intent intent = getCreationIntent();
|
||||||
startActivityForResult(intent, REQUEST_SAVE_ATTACHMENT);
|
startActivityForResult(intent, REQUEST_SAVE_ATTACHMENT);
|
||||||
} else {
|
} else {
|
||||||
viewModel.saveImage(attachment);
|
viewModel.saveImage(getVisibleAttachment());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Builder builder = new Builder(this, R.style.BriarDialogTheme);
|
Builder builder = new Builder(this, R.style.BriarDialogTheme);
|
||||||
@@ -303,7 +272,7 @@ public class ImageActivity extends BriarActivity
|
|||||||
String fileName = sdf.format(new Date());
|
String fileName = sdf.format(new Date());
|
||||||
Intent intent = new Intent(ACTION_CREATE_DOCUMENT);
|
Intent intent = new Intent(ACTION_CREATE_DOCUMENT);
|
||||||
intent.addCategory(CATEGORY_OPENABLE);
|
intent.addCategory(CATEGORY_OPENABLE);
|
||||||
intent.setType(attachment.getMimeType());
|
intent.setType(getVisibleAttachment().getMimeType());
|
||||||
intent.putExtra(EXTRA_TITLE, fileName);
|
intent.putExtra(EXTRA_TITLE, fileName);
|
||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
@@ -320,4 +289,26 @@ public class ImageActivity extends BriarActivity
|
|||||||
viewModel.onSaveStateSeen();
|
viewModel.onSaveStateSeen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AttachmentItem getVisibleAttachment() {
|
||||||
|
return attachments.get(viewPager.getCurrentItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ImagePagerAdapter extends FragmentStatePagerAdapter {
|
||||||
|
|
||||||
|
private ImagePagerAdapter(FragmentManager fm) {
|
||||||
|
super(fm);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Fragment getItem(int position) {
|
||||||
|
return ImageFragment.newInstance(attachments.get(position));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return attachments.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
package org.briarproject.briar.android.conversation;
|
||||||
|
|
||||||
|
import android.arch.lifecycle.ViewModelProvider;
|
||||||
|
import android.arch.lifecycle.ViewModelProviders;
|
||||||
|
import android.graphics.drawable.Animatable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import com.bumptech.glide.load.DataSource;
|
||||||
|
import com.bumptech.glide.load.engine.GlideException;
|
||||||
|
import com.bumptech.glide.request.RequestListener;
|
||||||
|
import com.bumptech.glide.request.target.Target;
|
||||||
|
import com.github.chrisbanes.photoview.PhotoView;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.activity.BaseActivity;
|
||||||
|
import org.briarproject.briar.android.conversation.glide.GlideApp;
|
||||||
|
|
||||||
|
import javax.annotation.ParametersAreNonnullByDefault;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
import static android.widget.ImageView.ScaleType.FIT_START;
|
||||||
|
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
|
||||||
|
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||||
|
import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHMENT_POSITION;
|
||||||
|
|
||||||
|
@MethodsNotNullByDefault
|
||||||
|
@ParametersAreNonnullByDefault
|
||||||
|
public class ImageFragment extends Fragment {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ViewModelProvider.Factory viewModelFactory;
|
||||||
|
|
||||||
|
private AttachmentItem attachment;
|
||||||
|
private ImageViewModel viewModel;
|
||||||
|
private PhotoView photoView;
|
||||||
|
|
||||||
|
static ImageFragment newInstance(AttachmentItem a) {
|
||||||
|
ImageFragment f = new ImageFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putParcelable(ATTACHMENT_POSITION, a);
|
||||||
|
f.setArguments(args);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onActivityCreated(savedInstanceState);
|
||||||
|
BaseActivity a = (BaseActivity) requireNonNull(getActivity());
|
||||||
|
a.getActivityComponent().inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
Bundle args = requireNonNull(getArguments());
|
||||||
|
attachment = requireNonNull(args.getParcelable(ATTACHMENT_POSITION));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater,
|
||||||
|
@Nullable ViewGroup container,
|
||||||
|
@Nullable Bundle savedInstanceState) {
|
||||||
|
View v = inflater.inflate(R.layout.fragment_image, container,
|
||||||
|
false);
|
||||||
|
|
||||||
|
viewModel = ViewModelProviders.of(requireNonNull(getActivity()),
|
||||||
|
viewModelFactory).get(ImageViewModel.class);
|
||||||
|
|
||||||
|
photoView = v.findViewById(R.id.photoView);
|
||||||
|
photoView.setOnClickListener(view -> viewModel.clickPhoto());
|
||||||
|
|
||||||
|
// Request Listener
|
||||||
|
RequestListener<Drawable> listener = new RequestListener<Drawable>() {
|
||||||
|
@Override
|
||||||
|
public boolean onLoadFailed(@Nullable GlideException e,
|
||||||
|
Object model, Target<Drawable> target,
|
||||||
|
boolean isFirstResource) {
|
||||||
|
if (getActivity() != null)
|
||||||
|
getActivity().supportStartPostponedEnterTransition();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onResourceReady(Drawable resource, Object model,
|
||||||
|
Target<Drawable> target, DataSource dataSource,
|
||||||
|
boolean isFirstResource) {
|
||||||
|
if (SDK_INT >= 21 && !(resource instanceof Animatable)) {
|
||||||
|
// set transition name only when not animatable,
|
||||||
|
// because the animation won't start otherwise
|
||||||
|
photoView.setTransitionName(
|
||||||
|
attachment.getTransitionName());
|
||||||
|
}
|
||||||
|
// Move image to the top if overlapping toolbar
|
||||||
|
if (viewModel.isOverlappingToolbar(photoView, resource)) {
|
||||||
|
photoView.setScaleType(FIT_START);
|
||||||
|
}
|
||||||
|
if (getActivity() != null)
|
||||||
|
getActivity().supportStartPostponedEnterTransition();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load Image
|
||||||
|
GlideApp.with(this)
|
||||||
|
.load(attachment)
|
||||||
|
.diskCacheStrategy(NONE)
|
||||||
|
.error(R.drawable.ic_image_broken)
|
||||||
|
.dontTransform()
|
||||||
|
.addListener(listener)
|
||||||
|
.into(photoView);
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -4,9 +4,11 @@ import android.app.Application;
|
|||||||
import android.arch.lifecycle.AndroidViewModel;
|
import android.arch.lifecycle.AndroidViewModel;
|
||||||
import android.arch.lifecycle.LiveData;
|
import android.arch.lifecycle.LiveData;
|
||||||
import android.arch.lifecycle.MutableLiveData;
|
import android.arch.lifecycle.MutableLiveData;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
@@ -48,7 +50,9 @@ public class ImageViewModel extends AndroidViewModel {
|
|||||||
@IoExecutor
|
@IoExecutor
|
||||||
private final Executor ioExecutor;
|
private final Executor ioExecutor;
|
||||||
|
|
||||||
|
private final MutableLiveData<Boolean> imageClicked = new MutableLiveData<>();
|
||||||
private final MutableLiveData<Boolean> saveState = new MutableLiveData<>();
|
private final MutableLiveData<Boolean> saveState = new MutableLiveData<>();
|
||||||
|
private int toolbarTop, toolbarBottom;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ImageViewModel(Application application,
|
ImageViewModel(Application application,
|
||||||
@@ -61,6 +65,45 @@ public class ImageViewModel extends AndroidViewModel {
|
|||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clickPhoto() {
|
||||||
|
imageClicked.setValue(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A LiveData that is true if the image was clicked,
|
||||||
|
* false if it wasn't.
|
||||||
|
*
|
||||||
|
* Call {@link #onOnImageClickSeen()} after consuming an update.
|
||||||
|
*/
|
||||||
|
LiveData<Boolean> getOnImageClicked() {
|
||||||
|
return imageClicked;
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
void onOnImageClickSeen() {
|
||||||
|
imageClicked.setValue(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setToolbarPosition(int top, int bottom) {
|
||||||
|
toolbarTop = top;
|
||||||
|
toolbarBottom = bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isOverlappingToolbar(View screenView, Drawable drawable) {
|
||||||
|
int width = drawable.getIntrinsicWidth();
|
||||||
|
int height = drawable.getIntrinsicHeight();
|
||||||
|
float widthPercentage = screenView.getWidth() / (float) width;
|
||||||
|
float heightPercentage = screenView.getHeight() / (float) height;
|
||||||
|
float scaleFactor = Math.min(widthPercentage, heightPercentage);
|
||||||
|
int realWidth = (int) (width * scaleFactor);
|
||||||
|
int realHeight = (int) (height * scaleFactor);
|
||||||
|
// return if photo doesn't use the full width,
|
||||||
|
// because it will be moved to the right otherwise
|
||||||
|
if (realWidth < screenView.getWidth()) return false;
|
||||||
|
int drawableTop = (screenView.getHeight() - realHeight) / 2;
|
||||||
|
return drawableTop < toolbarBottom && drawableTop != toolbarTop;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A LiveData that is true if the image was saved,
|
* A LiveData that is true if the image was saved,
|
||||||
* false if there was an error and null otherwise.
|
* false if there was an error and null otherwise.
|
||||||
|
|||||||
@@ -8,12 +8,11 @@
|
|||||||
android:background="@color/briar_black"
|
android:background="@color/briar_black"
|
||||||
tools:context=".android.conversation.ImageActivity">
|
tools:context=".android.conversation.ImageActivity">
|
||||||
|
|
||||||
<com.github.chrisbanes.photoview.PhotoView
|
<android.support.v4.view.ViewPager
|
||||||
android:id="@+id/photoView"
|
android:id="@+id/viewPager"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:ignore="ContentDescription"
|
tools:background="@color/briar_green_light"/>
|
||||||
tools:srcCompat="@tools:sample/backgrounds/scenic"/>
|
|
||||||
|
|
||||||
<android.support.design.widget.AppBarLayout
|
<android.support.design.widget.AppBarLayout
|
||||||
android:id="@+id/appBarLayout"
|
android:id="@+id/appBarLayout"
|
||||||
|
|||||||
9
briar-android/src/main/res/layout/fragment_image.xml
Normal file
9
briar-android/src/main/res/layout/fragment_image.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.github.chrisbanes.photoview.PhotoView
|
||||||
|
android:id="@+id/photoView"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:ignore="ContentDescription"
|
||||||
|
tools:srcCompat="@tools:sample/backgrounds/scenic"/>
|
||||||
Reference in New Issue
Block a user