Compare commits

...

4 Commits

Author SHA1 Message Date
Torsten Grote
e6e077ff40 [android] enable image shared element transition for API 21+22
There's an Android framework bug (#224270) on these APIs that causes a NPE
when the shared element is not visible anymore when returning.
Since we know restore the list position, the shared element should be
visible and thus not produce NPEs anymore.
2018-12-13 14:20:52 -02:00
Torsten Grote
4e5e354af2 [android] Fix enter transition to fullscreen ImageActivity 2018-12-13 14:20:52 -02:00
Torsten Grote
9ea3623c85 [android] Save and restore list position of conversation across restarts 2018-12-13 14:20:51 -02:00
Torsten Grote
84089f5e16 DO NOT MERGE: Return multiple fake image attachements in MessagingManager
(cherry picked from commit d9cb675)
2018-12-13 14:20:04 -02:00
21 changed files with 95 additions and 30 deletions

View File

@@ -8,6 +8,7 @@ import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
@@ -107,7 +108,6 @@ import static android.support.v4.app.ActivityOptionsCompat.makeSceneTransitionAn
import static android.support.v4.view.ViewCompat.setTransitionName; import static android.support.v4.view.ViewCompat.setTransitionName;
import static android.support.v7.util.SortedList.INVALID_POSITION; import static android.support.v7.util.SortedList.INVALID_POSITION;
import static android.view.Gravity.RIGHT; import static android.view.Gravity.RIGHT;
import static android.widget.Toast.LENGTH_LONG;
import static android.widget.Toast.LENGTH_SHORT; import static android.widget.Toast.LENGTH_SHORT;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static java.util.Collections.sort; import static java.util.Collections.sort;
@@ -121,9 +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.ATTACHMENTS;
import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHMENT_POSITION; 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;
@@ -194,6 +194,8 @@ public class ConversationActivity extends BriarActivity
ViewModelProvider.Factory viewModelFactory; ViewModelProvider.Factory viewModelFactory;
private volatile ContactId contactId; private volatile ContactId contactId;
@Nullable
private Parcelable layoutManagerState;
private final Observer<String> contactNameObserver = name -> { private final Observer<String> contactNameObserver = name -> {
requireNonNull(name); requireNonNull(name);
@@ -309,6 +311,21 @@ public class ConversationActivity extends BriarActivity
list.stopPeriodicUpdate(); list.stopPeriodicUpdate();
} }
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (layoutManager != null) {
layoutManagerState = layoutManager.onSaveInstanceState();
outState.putParcelable("layoutManager", layoutManagerState);
}
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
layoutManagerState = savedInstanceState.getParcelable("layoutManager");
}
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu items for use in the action bar // Inflate the menu items for use in the action bar
@@ -428,8 +445,12 @@ public class ConversationActivity extends BriarActivity
List<ConversationItem> items = createItems(headers); List<ConversationItem> items = createItems(headers);
adapter.addAll(items); adapter.addAll(items);
list.showData(); list.showData();
// Scroll to the bottom if (layoutManagerState == null) {
list.scrollToPosition(adapter.getItemCount() - 1); // Scroll to the bottom
list.scrollToPosition(adapter.getItemCount() - 1);
} else {
layoutManager.onRestoreInstanceState(layoutManagerState);
}
} else { } else {
LOG.info("Concurrent update, reloading"); LOG.info("Concurrent update, reloading");
loadMessages(); loadMessages();
@@ -471,7 +492,8 @@ public class ConversationActivity extends BriarActivity
adapter.getMessageItem(m); adapter.getMessageItem(m);
if (pair != null) { if (pair != null) {
pair.getSecond().setText(text); pair.getSecond().setText(text);
boolean bottom = adapter.isScrolledToBottom(layoutManager); boolean bottom = layoutManagerState == null &&
adapter.isScrolledToBottom(layoutManager);
adapter.notifyItemChanged(pair.getFirst()); adapter.notifyItemChanged(pair.getFirst());
if (bottom) list.scrollToPosition(adapter.getItemCount() - 1); if (bottom) list.scrollToPosition(adapter.getItemCount() - 1);
} }
@@ -502,7 +524,8 @@ public class ConversationActivity extends BriarActivity
adapter.getMessageItem(m); adapter.getMessageItem(m);
if (pair != null) { if (pair != null) {
pair.getSecond().setAttachments(items); pair.getSecond().setAttachments(items);
boolean bottom = adapter.isScrolledToBottom(layoutManager); boolean bottom = layoutManagerState == null &&
adapter.isScrolledToBottom(layoutManager);
adapter.notifyItemChanged(pair.getFirst()); adapter.notifyItemChanged(pair.getFirst());
if (bottom) list.scrollToPosition(adapter.getItemCount() - 1); if (bottom) list.scrollToPosition(adapter.getItemCount() - 1);
} }
@@ -553,7 +576,8 @@ public class ConversationActivity extends BriarActivity
private void addConversationItem(ConversationItem item) { private void addConversationItem(ConversationItem item) {
runOnUiThreadUnlessDestroyed(() -> { runOnUiThreadUnlessDestroyed(() -> {
boolean bottom = adapter.isScrolledToBottom(layoutManager); boolean bottom = layoutManagerState == null &&
adapter.isScrolledToBottom(layoutManager);
adapter.incrementRevision(); adapter.incrementRevision();
adapter.add(item); adapter.add(item);
if (bottom) list.scrollToPosition(adapter.getItemCount() - 1); if (bottom) list.scrollToPosition(adapter.getItemCount() - 1);
@@ -820,15 +844,11 @@ public class ConversationActivity extends BriarActivity
i.putExtra(ATTACHMENT_POSITION, attachments.indexOf(item)); 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) { // restoring list position should not trigger android bug #224270
String transitionName = item.getTransitionName(); String transitionName = item.getTransitionName();
ActivityOptionsCompat options = ActivityOptionsCompat options =
makeSceneTransitionAnimation(this, view, transitionName); makeSceneTransitionAnimation(this, view, transitionName);
ActivityCompat.startActivity(this, i, options.toBundle()); ActivityCompat.startActivity(this, i, options.toBundle());
} else {
// work-around for android bug #224270
startActivity(i);
}
} }
@DatabaseExecutor @DatabaseExecutor

View File

@@ -82,7 +82,7 @@ public class ImageActivity extends BriarActivity
super.onCreate(state); super.onCreate(state);
// Transitions // Transitions
supportPostponeEnterTransition(); if (state == null) supportPostponeEnterTransition();
Window window = getWindow(); Window window = getWindow();
if (SDK_INT >= 21) { if (SDK_INT >= 21) {
Transition transition = new Fade(); Transition transition = new Fade();
@@ -298,13 +298,18 @@ public class ImageActivity extends BriarActivity
private class ImagePagerAdapter extends FragmentStatePagerAdapter { private class ImagePagerAdapter extends FragmentStatePagerAdapter {
private boolean isFirst = true;
private ImagePagerAdapter(FragmentManager fm) { private ImagePagerAdapter(FragmentManager fm) {
super(fm); super(fm);
} }
@Override @Override
public Fragment getItem(int position) { public Fragment getItem(int position) {
return ImageFragment.newInstance(attachments.get(position)); Fragment f = ImageFragment
.newInstance(attachments.get(position), isFirst);
isFirst = false;
return f;
} }
@Override @Override

View File

@@ -35,17 +35,21 @@ import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHME
@ParametersAreNonnullByDefault @ParametersAreNonnullByDefault
public class ImageFragment extends Fragment { public class ImageFragment extends Fragment {
private final static String IS_FIRST = "isFirst";
@Inject @Inject
ViewModelProvider.Factory viewModelFactory; ViewModelProvider.Factory viewModelFactory;
private AttachmentItem attachment; private AttachmentItem attachment;
private boolean isFirst;
private ImageViewModel viewModel; private ImageViewModel viewModel;
private PhotoView photoView; private PhotoView photoView;
static ImageFragment newInstance(AttachmentItem a) { static ImageFragment newInstance(AttachmentItem a, boolean isFirst) {
ImageFragment f = new ImageFragment(); ImageFragment f = new ImageFragment();
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putParcelable(ATTACHMENT_POSITION, a); args.putParcelable(ATTACHMENT_POSITION, a);
args.putBoolean(IS_FIRST, isFirst);
f.setArguments(args); f.setArguments(args);
return f; return f;
} }
@@ -63,6 +67,7 @@ public class ImageFragment extends Fragment {
Bundle args = requireNonNull(getArguments()); Bundle args = requireNonNull(getArguments());
attachment = requireNonNull(args.getParcelable(ATTACHMENT_POSITION)); attachment = requireNonNull(args.getParcelable(ATTACHMENT_POSITION));
isFirst = args.getBoolean(IS_FIRST);
} }
@Nullable @Nullable
@@ -85,7 +90,7 @@ public class ImageFragment extends Fragment {
public boolean onLoadFailed(@Nullable GlideException e, public boolean onLoadFailed(@Nullable GlideException e,
Object model, Target<Drawable> target, Object model, Target<Drawable> target,
boolean isFirstResource) { boolean isFirstResource) {
if (getActivity() != null) if (getActivity() != null && isFirst)
getActivity().supportStartPostponedEnterTransition(); getActivity().supportStartPostponedEnterTransition();
return false; return false;
} }
@@ -104,8 +109,9 @@ public class ImageFragment extends Fragment {
if (viewModel.isOverlappingToolbar(photoView, resource)) { if (viewModel.isOverlappingToolbar(photoView, resource)) {
photoView.setScaleType(FIT_START); photoView.setScaleType(FIT_START);
} }
if (getActivity() != null) if (getActivity() != null && isFirst) {
getActivity().supportStartPostponedEnterTransition(); getActivity().supportStartPostponedEnterTransition();
}
return false; return false;
} }
}; };

View File

@@ -15,6 +15,7 @@ import org.briarproject.briar.android.conversation.glide.BriarImageTransformatio
import org.briarproject.briar.android.conversation.glide.GlideApp; import org.briarproject.briar.android.conversation.glide.GlideApp;
import org.briarproject.briar.android.conversation.glide.Radii; import org.briarproject.briar.android.conversation.glide.Radii;
import static android.os.Build.VERSION.SDK_INT;
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE; import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade; import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
@@ -42,6 +43,9 @@ class ImageViewHolder extends ViewHolder {
} else { } else {
setImageViewDimensions(attachment, single, needsStretch); setImageViewDimensions(attachment, single, needsStretch);
loadImage(attachment, r); loadImage(attachment, r);
if (SDK_INT >= 21) {
imageView.setTransitionName(attachment.getTransitionName());
}
} }
} }

View File

@@ -49,7 +49,8 @@
android:id="@+id/conversationView" android:id="@+id/conversationView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="2"/> android:layout_weight="2"
app:scrollToEnd="false"/>
<org.briarproject.briar.android.view.ImagePreview <org.briarproject.briar.android.view.ImagePreview
android:id="@+id/imagePreview" android:id="@+id/imagePreview"

View File

@@ -32,11 +32,11 @@ import org.briarproject.briar.api.messaging.PrivateMessageHeader;
import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent; import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent;
import org.briarproject.briar.client.ConversationClientImpl; import org.briarproject.briar.client.ConversationClientImpl;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
@@ -44,7 +44,7 @@ import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static org.briarproject.bramble.util.StringUtils.fromHexString; import static java.util.logging.Logger.getLogger;
import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ; import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
@Immutable @Immutable
@@ -219,8 +219,18 @@ class MessagingManagerImpl extends ConversationClientImpl
long timestamp = meta.getLong("timestamp"); long timestamp = meta.getLong("timestamp");
boolean local = meta.getBoolean("local"); boolean local = meta.getBoolean("local");
boolean read = meta.getBoolean("read"); boolean read = meta.getBoolean("read");
// TODO replace fake attachments by real ones
int num = (int) (timestamp % 5);
boolean hasText = num == 0 || id.hashCode() % 2 == 0;
List<AttachmentHeader> attachments = new ArrayList<>(num);
for (int i = 0; i < num; i++) {
byte[] aIdBytes = id.getBytes().clone();
aIdBytes[0] = (byte) i;
MessageId aId = new MessageId(aIdBytes);
attachments.add(new AttachmentHeader(aId, "image/jpeg"));
}
headers.add(new PrivateMessageHeader(id, g, timestamp, local, headers.add(new PrivateMessageHeader(id, g, timestamp, local,
read, s.isSent(), s.isSeen(), true, emptyList())); read, s.isSent(), s.isSeen(), hasText, attachments));
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); throw new DbException(e);
} }
@@ -241,11 +251,30 @@ class MessagingManagerImpl extends ConversationClientImpl
@Override @Override
public Attachment getAttachment(MessageId m) { public Attachment getAttachment(MessageId m) {
// TODO add real implementation // TODO add real implementation
byte[] bytes = fromHexString("89504E470D0A1A0A0000000D49484452" + String[] files = new String[] {
"000000010000000108060000001F15C4" + // "error_animated.gif",
"890000000A49444154789C6300010000" + // "error_high.jpg",
"0500010D0A2DB40000000049454E44AE426082"); // "error_wide.jpg",
return new Attachment(new ByteArrayInputStream(bytes)); // "error_huge.gif",
// "error_large.gif",
// "error_malformed.jpg",
// "wide.jpg",
// "high.jpg",
// "small.png",
"kitten1.jpg",
"kitten2.jpg",
"kitten3.gif",
"kitten4.jpg",
"kitten5.jpg",
"kitten6.png",
};
int index = Math.abs(m.hashCode() % files.length);
String file = files[index];
getLogger(MessagingManagerImpl.class.getName())
.warning("Loading file: " + file);
InputStream is = getClass().getClassLoader().getResourceAsStream(file);
return new Attachment(is);
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB