diff --git a/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.java b/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.java
index b22d452f..b6e453a7 100644
--- a/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.java
+++ b/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.java
@@ -16,10 +16,13 @@
package com.keylesspalace.tusky;
import android.Manifest;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.app.DownloadManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@@ -45,20 +48,25 @@ import com.keylesspalace.tusky.view.ImageViewPager;
import java.io.File;
-public class ViewMediaActivity extends BaseActivity implements ViewMediaFragment.OnDismissListener {
+public class ViewMediaActivity extends BaseActivity implements ViewMediaFragment.PhotoActionsListener {
private static final int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1;
private ImageViewPager viewPager;
private View anyView;
private String[] imageUrls;
+ private Toolbar toolbar;
+
+ private boolean isToolbarVisible = true;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_media);
+ supportPostponeEnterTransition();
+
// Obtain the views.
- final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ toolbar = (Toolbar) findViewById(R.id.toolbar);
viewPager = (ImageViewPager) findViewById(R.id.view_pager);
anyView = toolbar;
@@ -69,13 +77,14 @@ public class ViewMediaActivity extends BaseActivity implements ViewMediaFragment
// Setup the view pager.
final ImagePagerAdapter adapter = new ImagePagerAdapter(getSupportFragmentManager(),
- imageUrls);
+ imageUrls, initialPosition);
viewPager.setAdapter(adapter);
viewPager.setCurrentItem(initialPosition);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset,
- int positionOffsetPixels) {}
+ int positionOffsetPixels) {
+ }
@Override
public void onPageSelected(int position) {
@@ -84,7 +93,8 @@ public class ViewMediaActivity extends BaseActivity implements ViewMediaFragment
}
@Override
- public void onPageScrollStateChanged(int state) {}
+ public void onPageScrollStateChanged(int state) {
+ }
});
// Setup the toolbar.
@@ -98,7 +108,7 @@ public class ViewMediaActivity extends BaseActivity implements ViewMediaFragment
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- finish();
+ supportFinishAfterTransition();
}
});
toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@@ -113,6 +123,13 @@ public class ViewMediaActivity extends BaseActivity implements ViewMediaFragment
return true;
}
});
+
+ View decorView = getWindow().getDecorView();
+ int uiOptions = View.SYSTEM_UI_FLAG_LOW_PROFILE;
+ decorView.setSystemUiVisibility(uiOptions);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ getWindow().setStatusBarColor(Color.BLACK);
+ }
}
@Override
@@ -132,12 +149,28 @@ public class ViewMediaActivity extends BaseActivity implements ViewMediaFragment
@Override
public void onDismiss() {
- finish();
+ supportFinishAfterTransition();
+ }
+
+ @Override
+ public void onPhotoTap() {
+ isToolbarVisible = !isToolbarVisible;
+ final int visibility = isToolbarVisible ? View.VISIBLE : View.INVISIBLE;
+ int alpha = isToolbarVisible ? 1 : 0;
+ toolbar.animate().alpha(alpha)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ toolbar.setVisibility(visibility);
+ animation.removeListener(this);
+ }
+ })
+ .start();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[],
- @NonNull int[] grantResults) {
+ @NonNull int[] grantResults) {
switch (requestCode) {
case PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE: {
if (grantResults.length > 0
@@ -158,7 +191,7 @@ public class ViewMediaActivity extends BaseActivity implements ViewMediaFragment
}
private void doErrorDialog(@StringRes int descriptionId, @StringRes int actionId,
- View.OnClickListener listener) {
+ View.OnClickListener listener) {
if (anyView != null) {
Snackbar bar = Snackbar.make(anyView, getString(descriptionId),
Snackbar.LENGTH_SHORT);
@@ -170,9 +203,9 @@ public class ViewMediaActivity extends BaseActivity implements ViewMediaFragment
private void downloadImage() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN &&
ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
- != PackageManager.PERMISSION_GRANTED) {
+ != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
- new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },
+ new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);
} else {
String url = imageUrls[viewPager.getCurrentItem()];
diff --git a/app/src/main/java/com/keylesspalace/tusky/ViewVideoActivity.java b/app/src/main/java/com/keylesspalace/tusky/ViewVideoActivity.java
index a6e06a52..7a7b33ed 100644
--- a/app/src/main/java/com/keylesspalace/tusky/ViewVideoActivity.java
+++ b/app/src/main/java/com/keylesspalace/tusky/ViewVideoActivity.java
@@ -15,17 +15,28 @@
package com.keylesspalace.tusky;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.graphics.Color;
import android.media.MediaPlayer;
+import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
+import android.view.MotionEvent;
import android.view.View;
import android.widget.MediaController;
import android.widget.ProgressBar;
import android.widget.VideoView;
public class ViewVideoActivity extends BaseActivity {
+
+ Handler handler = new Handler(Looper.getMainLooper());
+ Toolbar toolbar;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -34,7 +45,7 @@ public class ViewVideoActivity extends BaseActivity {
final ProgressBar progressBar = (ProgressBar) findViewById(R.id.video_progress);
VideoView videoView = (VideoView) findViewById(R.id.video_player);
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar bar = getSupportActionBar();
if (bar != null) {
@@ -55,9 +66,28 @@ public class ViewVideoActivity extends BaseActivity {
public void onPrepared(MediaPlayer mp) {
progressBar.setVisibility(View.GONE);
mp.setLooping(true);
+ hideToolbarAfterDelay();
}
});
videoView.start();
+
+ videoView.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ handler.removeCallbacksAndMessages(null);
+ toolbar.animate().cancel();
+ toolbar.setAlpha(1);
+ toolbar.setVisibility(View.VISIBLE);
+ hideToolbarAfterDelay();
+ }
+ return false;
+ }
+ });
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ getWindow().setStatusBarColor(Color.BLACK);
+ }
}
@Override
@@ -70,4 +100,22 @@ public class ViewVideoActivity extends BaseActivity {
}
return super.onOptionsItemSelected(item);
}
+
+ void hideToolbarAfterDelay() {
+ handler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ toolbar.animate().alpha(0).setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ View decorView = getWindow().getDecorView();
+ int uiOptions = View.SYSTEM_UI_FLAG_LOW_PROFILE;
+ decorView.setSystemUiVisibility(uiOptions);
+ toolbar.setVisibility(View.INVISIBLE);
+ animation.removeListener(this);
+ }
+ });
+ }
+ }, 3000);
+ }
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusViewHolder.java
index 30dbd3cf..f1de206e 100644
--- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusViewHolder.java
+++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusViewHolder.java
@@ -275,7 +275,7 @@ public class StatusViewHolder extends RecyclerView.ViewHolder {
previews[i].setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- listener.onViewMedia(urls, urlIndex, type);
+ listener.onViewMedia(urls, urlIndex, type, v);
}
});
}
@@ -359,7 +359,7 @@ public class StatusViewHolder extends RecyclerView.ViewHolder {
mediaLabel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- listener.onViewMedia(urls, 0, type);
+ listener.onViewMedia(urls, 0, type, null);
}
});
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java
index 3df0db63..b5fa5401 100644
--- a/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java
@@ -243,8 +243,9 @@ public class NotificationsFragment extends SFragment implements
}
@Override
- public void onViewMedia(String[] urls, int urlIndex, Status.MediaAttachment.Type type) {
- super.viewMedia(urls, urlIndex, type);
+ public void onViewMedia(String[] urls, int urlIndex, Status.MediaAttachment.Type type,
+ View view) {
+ super.viewMedia(urls, urlIndex, type, view);
}
@Override
diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java
index c76b4447..1a5a6612 100644
--- a/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java
@@ -19,7 +19,9 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.annotation.Nullable;
+import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.content.LocalBroadcastManager;
+import android.support.v4.view.ViewCompat;
import android.support.v7.widget.PopupMenu;
import android.support.v7.widget.RecyclerView;
import android.text.Spanned;
@@ -293,13 +295,23 @@ public abstract class SFragment extends BaseFragment implements AdapterItemRemov
popup.show();
}
- protected void viewMedia(String[] urls, int urlIndex, Status.MediaAttachment.Type type) {
+ protected void viewMedia(String[] urls, int urlIndex, Status.MediaAttachment.Type type,
+ @Nullable View view) {
switch (type) {
case IMAGE: {
Intent intent = new Intent(getContext(), ViewMediaActivity.class);
intent.putExtra("urls", urls);
intent.putExtra("urlIndex", urlIndex);
- startActivity(intent);
+ if (view != null) {
+ String url = urls[urlIndex];
+ ViewCompat.setTransitionName(view, url);
+ ActivityOptionsCompat options =
+ ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(),
+ view, url);
+ startActivity(intent, options.toBundle());
+ } else {
+ startActivity(intent);
+ }
break;
}
case GIFV:
diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java
index 0a6b7d2c..548851bd 100644
--- a/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java
@@ -342,8 +342,9 @@ public class TimelineFragment extends SFragment implements
}
@Override
- public void onViewMedia(String[] urls, int urlIndex, Status.MediaAttachment.Type type) {
- super.viewMedia(urls, urlIndex, type);
+ public void onViewMedia(String[] urls, int urlIndex, Status.MediaAttachment.Type type,
+ View view) {
+ super.viewMedia(urls, urlIndex, type, view);
}
@Override
diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewMediaFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewMediaFragment.java
index b0102df7..fa409757 100644
--- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewMediaFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewMediaFragment.java
@@ -17,6 +17,7 @@ package com.keylesspalace.tusky.fragment;
import android.content.Context;
import android.os.Bundle;
+import android.support.v4.view.ViewCompat;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -29,20 +30,30 @@ import com.github.chrisbanes.photoview.PhotoView;
import com.github.chrisbanes.photoview.PhotoViewAttacher;
import com.keylesspalace.tusky.R;
import com.squareup.picasso.Callback;
+import com.squareup.picasso.NetworkPolicy;
import com.squareup.picasso.Picasso;
public class ViewMediaFragment extends BaseFragment {
- public interface OnDismissListener {
+ public interface PhotoActionsListener {
void onDismiss();
+
+ void onPhotoTap();
}
private PhotoViewAttacher attacher;
- private OnDismissListener onDismissListener;
+ private PhotoActionsListener photoActionsListener;
+ View rootView;
+ PhotoView photoView;
- public static ViewMediaFragment newInstance(String url) {
+ private static final String ARG_URL = "url";
+ private static final String ARG_START_POSTPONED_TRANSITION = "startPostponedTransition";
+
+ public static ViewMediaFragment newInstance(String url, boolean shouldStartPostponedTransition) {
Bundle arguments = new Bundle();
ViewMediaFragment fragment = new ViewMediaFragment();
arguments.putString("url", url);
+ arguments.putBoolean(ARG_START_POSTPONED_TRANSITION, shouldStartPostponedTransition);
+
fragment.setArguments(arguments);
return fragment;
}
@@ -50,18 +61,17 @@ public class ViewMediaFragment extends BaseFragment {
@Override
public void onAttach(Context context) {
super.onAttach(context);
- onDismissListener = (OnDismissListener) context;
+ photoActionsListener = (PhotoActionsListener) context;
}
@Override
public View onCreateView(LayoutInflater inflater, final ViewGroup container,
- Bundle savedInstanceState) {
- final View rootView = inflater.inflate(R.layout.fragment_view_media, container, false);
+ Bundle savedInstanceState) {
+ rootView = inflater.inflate(R.layout.fragment_view_media, container, false);
+ photoView = (PhotoView) rootView.findViewById(R.id.view_media_image);
- PhotoView photoView = (PhotoView) rootView.findViewById(R.id.view_media_image);
-
- Bundle arguments = getArguments();
- String url = arguments.getString("url");
+ final Bundle arguments = getArguments();
+ final String url = arguments.getString("url");
attacher = new PhotoViewAttacher(photoView);
@@ -69,7 +79,14 @@ public class ViewMediaFragment extends BaseFragment {
attacher.setOnOutsidePhotoTapListener(new OnOutsidePhotoTapListener() {
@Override
public void onOutsidePhotoTap(ImageView imageView) {
- onDismissListener.onDismiss();
+ photoActionsListener.onDismiss();
+ }
+ });
+
+ attacher.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ photoActionsListener.onPhotoTap();
}
});
@@ -80,26 +97,82 @@ public class ViewMediaFragment extends BaseFragment {
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
if (Math.abs(velocityY) > Math.abs(velocityX)) {
- onDismissListener.onDismiss();
+ photoActionsListener.onDismiss();
return true;
}
return false;
}
});
- Picasso.with(getContext())
- .load(url)
- .into(photoView, new Callback() {
- @Override
- public void onSuccess() {
- rootView.findViewById(R.id.view_media_progress).setVisibility(View.GONE);
- attacher.update();
- }
+ ViewCompat.setTransitionName(photoView, url);
- @Override
- public void onError() {}
- });
+ // If we are the view to be shown initially...
+ if (arguments.getBoolean(ARG_START_POSTPONED_TRANSITION)) {
+ // Try to load image from disk.
+ Picasso.with(getContext())
+ .load(url)
+ .noFade()
+ .networkPolicy(NetworkPolicy.OFFLINE)
+ .into(photoView, new Callback() {
+ @Override
+ public void onSuccess() {
+ // if we loaded image from disk, we should check that view is attached.
+ if (ViewCompat.isAttachedToWindow(photoView)) {
+ finishLoadingSuccessfully();
+ } else {
+ // if view is not attached yet, wait for an attachment and
+ // start transition when it's finally ready.
+ photoView.addOnAttachStateChangeListener(
+ new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ finishLoadingSuccessfully();
+ photoView.removeOnAttachStateChangeListener(this);
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onError() {
+ // if there's no image in cache, load from network and start trnasition
+ // immediately.
+ getActivity().supportStartPostponedEnterTransition();
+ loadImageFromNetwork(url, photoView);
+ }
+ });
+ } else {
+ // if we're not initial page, don't bother.
+ loadImageFromNetwork(url, photoView);
+ }
return rootView;
}
+
+ private void loadImageFromNetwork(String url, ImageView photoView) {
+ Picasso.with(getContext())
+ .load(url)
+ .noPlaceholder()
+ .into(photoView, new Callback() {
+ @Override
+ public void onSuccess() {
+ finishLoadingSuccessfully();
+ }
+
+ @Override
+ public void onError() {
+ rootView.findViewById(R.id.view_media_progress).setVisibility(View.GONE);
+ }
+ });
+ }
+
+ private void finishLoadingSuccessfully() {
+ rootView.findViewById(R.id.view_media_progress).setVisibility(View.GONE);
+ attacher.update();
+ getActivity().supportStartPostponedEnterTransition();
+ }
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java
index 50069fae..8bc482ef 100644
--- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java
@@ -199,8 +199,9 @@ public class ViewThreadFragment extends SFragment implements
}
@Override
- public void onViewMedia(String[] urls, int urlIndex, Status.MediaAttachment.Type type) {
- super.viewMedia(urls, urlIndex, type);
+ public void onViewMedia(String[] urls, int urlIndex, Status.MediaAttachment.Type type,
+ View view) {
+ super.viewMedia(urls, urlIndex, type, view);
}
@Override
diff --git a/app/src/main/java/com/keylesspalace/tusky/interfaces/StatusActionListener.java b/app/src/main/java/com/keylesspalace/tusky/interfaces/StatusActionListener.java
index 6bf13f15..fc6a7dcb 100644
--- a/app/src/main/java/com/keylesspalace/tusky/interfaces/StatusActionListener.java
+++ b/app/src/main/java/com/keylesspalace/tusky/interfaces/StatusActionListener.java
@@ -25,7 +25,7 @@ public interface StatusActionListener extends LinkListener {
void onReblog(final boolean reblog, final int position);
void onFavourite(final boolean favourite, final int position);
void onMore(View view, final int position);
- void onViewMedia(String[] urls, int index, Status.MediaAttachment.Type type);
+ void onViewMedia(String[] urls, int index, Status.MediaAttachment.Type type, View view);
void onViewThread(int position);
void onOpenReblog(int position);
void onExpandedChange(boolean expanded, int position);
diff --git a/app/src/main/java/com/keylesspalace/tusky/pager/ImagePagerAdapter.java b/app/src/main/java/com/keylesspalace/tusky/pager/ImagePagerAdapter.java
index 561dbee4..9f9511cb 100644
--- a/app/src/main/java/com/keylesspalace/tusky/pager/ImagePagerAdapter.java
+++ b/app/src/main/java/com/keylesspalace/tusky/pager/ImagePagerAdapter.java
@@ -10,16 +10,20 @@ import java.util.Locale;
public class ImagePagerAdapter extends FragmentPagerAdapter {
private String[] urls;
+ private FragmentManager fragmentManager;
+ private int initialPosition;
- public ImagePagerAdapter(FragmentManager fragmentManager, String[] urls) {
+ public ImagePagerAdapter(FragmentManager fragmentManager, String[] urls, int initialPosition) {
super(fragmentManager);
this.urls = urls;
+ this.fragmentManager = fragmentManager;
+ this.initialPosition = initialPosition;
}
@Override
public Fragment getItem(int position) {
if (position >= 0 && position < urls.length) {
- return ViewMediaFragment.newInstance(urls[position]);
+ return ViewMediaFragment.newInstance(urls[position], position == initialPosition);
} else {
return null;
}
diff --git a/app/src/main/res/layout/activity_view_video.xml b/app/src/main/res/layout/activity_view_video.xml
index 0575d902..c20a559a 100644
--- a/app/src/main/res/layout/activity_view_video.xml
+++ b/app/src/main/res/layout/activity_view_video.xml
@@ -10,10 +10,9 @@
tools:context=".ViewVideoActivity">
+ android:layout_gravity="center" />