Refactored ImageViewFragment

The image, gif and webm things got their own view.
Made videos playable and pausable through the actionbar.
captchafix
Florens Douwes 11 years ago
parent 4e28980b33
commit 7e0c024a99
  1. BIN
      Chan/res/drawable-hdpi/ic_action_play.png
  2. BIN
      Chan/res/drawable-mdpi/ic_action_play.png
  3. BIN
      Chan/res/drawable-xhdpi/ic_action_play.png
  4. BIN
      Chan/res/drawable-xxhdpi/ic_action_play.png
  5. 17
      Chan/res/menu/image_view.xml
  6. 4
      Chan/res/values/dimens.xml
  7. 1
      Chan/res/values/strings.xml
  8. 20
      Chan/src/org/floens/chan/core/net/FileRequest.java
  9. 153
      Chan/src/org/floens/chan/ui/activity/ImageViewActivity.java
  10. 7
      Chan/src/org/floens/chan/ui/adapter/ImageViewAdapter.java
  11. 2
      Chan/src/org/floens/chan/ui/adapter/PostAdapter.java
  12. 233
      Chan/src/org/floens/chan/ui/fragment/ImageViewFragment.java
  13. 23
      Chan/src/org/floens/chan/ui/view/CustomNetworkImageView.java
  14. 72
      Chan/src/org/floens/chan/ui/view/LoadView.java
  15. 226
      Chan/src/org/floens/chan/ui/view/ThumbnailImageView.java
  16. 6
      Chan/src/org/floens/chan/utils/Utils.java

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 702 B

@ -1,9 +1,16 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" > <menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/action_image_play_state"
android:icon="@drawable/ic_action_play"
android:orderInCategory="1"
android:showAsAction="always"
android:title="@string/image_play_state"/>
<item <item
android:id="@+id/action_image_save" android:id="@+id/action_image_save"
android:orderInCategory="1" android:orderInCategory="2"
android:showAsAction="never" android:showAsAction="never"
android:title="@string/image_save" /> android:title="@string/image_save"/>
</menu> </menu>

@ -5,12 +5,10 @@
<dimen name="post_comment_padding">11dp</dimen> <dimen name="post_comment_padding">11dp</dimen>
<dimen name="post_icon_padding">6dp</dimen> <dimen name="post_icon_padding">6dp</dimen>
<dimen name="dp48">48dp</dimen>
<dimen name="thumbnail_size">70dp</dimen> <dimen name="thumbnail_size">70dp</dimen>
<dimen name="post_icon_width">24dp</dimen> <dimen name="post_icon_width">24dp</dimen>
<dimen name="post_icon_height">14dp</dimen> <dimen name="post_icon_height">14dp</dimen>
<dimen name="post_max_height">200dp</dimen> <dimen name="post_max_height">200dp</dimen>
<dimen name="image_popup_padding">8dp</dimen> <dimen name="image_view_padding">8dp</dimen>
</resources> </resources>

@ -29,6 +29,7 @@
<string name="image_save_failed">Saving image failed</string> <string name="image_save_failed">Saving image failed</string>
<string name="image_save_directory_error">Cannot make save directory</string> <string name="image_save_directory_error">Cannot make save directory</string>
<string name="image_save_not_mounted">Cannot write to storage</string> <string name="image_save_not_mounted">Cannot write to storage</string>
<string name="image_play_state">Start / stop playing</string>
<string name="image_preview_failed">Failed to show image</string> <string name="image_preview_failed">Failed to show image</string>
<string name="image_open_failed">Failed to open image</string> <string name="image_open_failed">Failed to open image</string>

@ -1,16 +1,21 @@
package org.floens.chan.core.net; package org.floens.chan.core.net;
import java.io.File;
import org.floens.chan.ChanApplication;
import com.android.volley.NetworkResponse; import com.android.volley.NetworkResponse;
import com.android.volley.Request; import com.android.volley.Request;
import com.android.volley.Response; import com.android.volley.Response;
import com.android.volley.Response.ErrorListener; import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener; import com.android.volley.Response.Listener;
import com.android.volley.toolbox.DiskBasedCache;
import com.android.volley.toolbox.HttpHeaderParser; import com.android.volley.toolbox.HttpHeaderParser;
public class CachingRequest extends Request<Void> { public class FileRequest extends Request<Void> {
protected final Listener<Void> listener; protected final Listener<File> listener;
public CachingRequest(String url, Listener<Void> listener, ErrorListener errorListener) { public FileRequest(String url, Listener<File> listener, ErrorListener errorListener) {
super(Method.GET, url, errorListener); super(Method.GET, url, errorListener);
this.listener = listener; this.listener = listener;
@ -24,6 +29,13 @@ public class CachingRequest extends Request<Void> {
@Override @Override
protected void deliverResponse(Void response) { protected void deliverResponse(Void response) {
listener.onResponse(response); DiskBasedCache cache = (DiskBasedCache) ChanApplication.getVolleyRequestQueue().getCache();
File file = cache.getFileForKey(getCacheKey());
if (file.exists()) {
listener.onResponse(file);
} else {
listener.onResponse(null);
}
} }
} }

@ -6,6 +6,7 @@ import org.floens.chan.R;
import org.floens.chan.core.model.Post; import org.floens.chan.core.model.Post;
import org.floens.chan.ui.adapter.ImageViewAdapter; import org.floens.chan.ui.adapter.ImageViewAdapter;
import org.floens.chan.ui.adapter.PostAdapter; import org.floens.chan.ui.adapter.PostAdapter;
import org.floens.chan.ui.fragment.ImageViewFragment;
import org.floens.chan.utils.ImageSaver; import org.floens.chan.utils.ImageSaver;
import org.floens.chan.utils.Logger; import org.floens.chan.utils.Logger;
@ -18,56 +19,57 @@ import android.view.MenuItem;
import android.view.Window; import android.view.Window;
/** /**
* An fragment pager that contains images. Call setPosts first, * An fragment pager that contains images. Call setPosts first, and then start
* and then start the activity with startActivity() * the activity with startActivity()
*/ */
public class ImageViewActivity extends Activity implements ViewPager.OnPageChangeListener { public class ImageViewActivity extends Activity implements ViewPager.OnPageChangeListener {
private static final String TAG = "ImageViewActivity"; private static final String TAG = "ImageViewActivity";
private static PostAdapter postAdapter; private static PostAdapter postAdapter;
private static int selectedId = -1; private static int selectedId = -1;
private ViewPager viewPager;
private ImageViewAdapter adapter; private ImageViewAdapter adapter;
private int currentPosition; private int currentPosition;
private boolean[] progressData;
/** /**
* Set the posts to show * Set the posts to show
* @param other the posts to get image data from *
* @param selected the no that the user clicked on * @param other
* the posts to get image data from
* @param selected
* the no that the user clicked on
*/ */
public static void setAdapter(PostAdapter adapter, int selected) { public static void setAdapter(PostAdapter adapter, int selected) {
postAdapter = adapter; postAdapter = adapter;
selectedId = selected; selectedId = selected;
} }
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
ActionBar actionBar = getActionBar(); ActionBar actionBar = getActionBar();
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_HOME_AS_UP); actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_HOME_AS_UP);
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if (postAdapter != null) { if (postAdapter != null) {
// Get the posts with images // Get the posts with images
ArrayList<Post> imagePosts = new ArrayList<Post>(); ArrayList<Post> imagePosts = new ArrayList<Post>();
for (Post post : postAdapter.getList()) { for (Post post : postAdapter.getList()) {
if (post.hasImage){ if (post.hasImage) {
imagePosts.add(post); imagePosts.add(post);
} }
} }
// Setup our pages and adapter // Setup our pages and adapter
setContentView(R.layout.image_pager); setContentView(R.layout.image_pager);
ViewPager viewPager = (ViewPager) findViewById(R.id.image_pager); viewPager = (ViewPager) findViewById(R.id.image_pager);
adapter = new ImageViewAdapter(getFragmentManager(), this); adapter = new ImageViewAdapter(getFragmentManager(), this);
adapter.addToList(imagePosts); adapter.setList(imagePosts);
viewPager.setAdapter(adapter); viewPager.setAdapter(adapter);
viewPager.setOnPageChangeListener(this); viewPager.setOnPageChangeListener(this);
progressData = new boolean[imagePosts.size()];
// Select the right image // Select the right image
for (int i = 0; i < imagePosts.size(); i++) { for (int i = 0; i < imagePosts.size(); i++) {
if (imagePosts.get(i).no == selectedId) { if (imagePosts.get(i).no == selectedId) {
@ -77,18 +79,60 @@ public class ImageViewActivity extends Activity implements ViewPager.OnPageChang
} }
} }
} else { } else {
Logger.e(TAG, "Posts in imageview list was null"); Logger.e(TAG, "Posts in ImageViewActivity was null");
finish(); finish();
} }
} }
@Override
protected void onStop() {
super.onStop();
// Avoid things like out of sync, since this is an activity.
finish();
}
@Override @Override
public void finish() { public void finish() {
super.finish(); super.finish();
overridePendingTransition(R.anim.fade_in, R.anim.fade_out); overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
postAdapter = null; postAdapter = null;
} }
@Override
public void onPageScrollStateChanged(int state) {
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
currentPosition = position;
ImageViewFragment fragment = getCurrentFragment();
if (fragment != null) {
fragment.onSelected(adapter, position);
}
Post post = adapter.getPost(position);
if (postAdapter != null) {
postAdapter.scrollToPost(post);
}
}
public void invalidateActionBar() {
invalidateOptionsMenu();
}
public void callOnSelect() {
ImageViewFragment fragment = getCurrentFragment();
if (fragment != null) {
fragment.onSelected(adapter, currentPosition);
}
}
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) { if (item.getItemId() == android.R.id.home) {
@ -97,70 +141,41 @@ public class ImageViewActivity extends Activity implements ViewPager.OnPageChang
} else if (item.getItemId() == R.id.action_image_save) { } else if (item.getItemId() == R.id.action_image_save) {
Post post = adapter.getPost(currentPosition); Post post = adapter.getPost(currentPosition);
ImageSaver.save(this, post.imageUrl, post.filename, post.ext); ImageSaver.save(this, post.imageUrl, post.filename, post.ext);
return true; return true;
} else { } else {
ImageViewFragment fragment = getCurrentFragment();
if (fragment != null) {
fragment.customOnOptionsItemSelected(item);
}
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
} }
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.image_view, menu); getMenuInflater().inflate(R.menu.image_view, menu);
return true;
}
/**
* Show the progressbar in the actionbar at the specified position.
* @param e
* @param position
*/
public void showProgressBar(boolean e, int position) {
progressData[position] = e;
if (position == currentPosition) {
setProgressBarIndeterminateVisibility(e);
}
}
@Override
protected void onStop() {
super.onStop();
// Avoid things like out of sync, since this is an activity. return true;
finish();
} }
@Override @Override
public void onPageSelected(int position) { public boolean onPrepareOptionsMenu(Menu menu) {
currentPosition = position; ImageViewFragment fragment = getCurrentFragment();
if (fragment != null) {
setProgressBarIndeterminateVisibility(progressData[position]); fragment.onPrepareOptionsMenu(currentPosition, adapter, menu);
Post post = adapter.getPost(position);
if (post != null) {
String filename = post.filename + "." + post.ext;
String text = "(" + (position + 1) + "/" + progressData.length + ") " + filename;
getActionBar().setTitle(text);
} }
if (postAdapter != null) { return super.onPrepareOptionsMenu(menu);
postAdapter.scrollToPost(post);
}
}
@Override
public void onPageScrollStateChanged(int state) {
} }
@Override private ImageViewFragment getCurrentFragment() {
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { Object o = adapter.instantiateItem(viewPager, currentPosition);
if (o instanceof ImageViewFragment) {
return (ImageViewFragment) o;
} else {
return null;
}
} }
} }

@ -15,7 +15,6 @@ import android.view.View;
public class ImageViewAdapter extends FragmentStatePagerAdapter { public class ImageViewAdapter extends FragmentStatePagerAdapter {
private final ImageViewActivity activity; private final ImageViewActivity activity;
private int count = 0;
private final ArrayList<Post> postList = new ArrayList<Post>(); private final ArrayList<Post> postList = new ArrayList<Post>();
public ImageViewAdapter(FragmentManager fragmentManager, ImageViewActivity activity) { public ImageViewAdapter(FragmentManager fragmentManager, ImageViewActivity activity) {
@ -25,7 +24,7 @@ public class ImageViewAdapter extends FragmentStatePagerAdapter {
@Override @Override
public int getCount() { public int getCount() {
return count; return postList.size();
} }
@Override @Override
@ -46,8 +45,8 @@ public class ImageViewAdapter extends FragmentStatePagerAdapter {
view = null; view = null;
} }
public void addToList(ArrayList<Post> list){ public void setList(ArrayList<Post> list){
count += list.size(); postList.clear();
postList.addAll(list); postList.addAll(list);
notifyDataSetChanged(); notifyDataSetChanged();

@ -92,7 +92,7 @@ public class PostAdapter extends BaseAdapter {
view.init(threadManager, listView, this); view.init(threadManager, listView, this);
int padding = context.getResources().getDimensionPixelSize(R.dimen.general_padding); int padding = context.getResources().getDimensionPixelSize(R.dimen.general_padding);
view.setPadding(padding, padding, padding, padding); view.setPadding(padding, padding, padding, padding);
int height = context.getResources().getDimensionPixelSize(R.dimen.dp48); int height = Utils.dp(context, 48f);
view.setHeight(height); view.setHeight(height);
view.setGravity(Gravity.CENTER); view.setGravity(Gravity.CENTER);
return view; return view;

@ -1,70 +1,41 @@
package org.floens.chan.ui.fragment; package org.floens.chan.ui.fragment;
import java.io.File;
import java.io.IOException;
import org.floens.chan.ChanApplication;
import org.floens.chan.R; import org.floens.chan.R;
import org.floens.chan.core.ChanPreferences;
import org.floens.chan.core.model.Post; import org.floens.chan.core.model.Post;
import org.floens.chan.core.net.CachingRequest;
import org.floens.chan.core.net.GIFRequest;
import org.floens.chan.ui.activity.ImageViewActivity; import org.floens.chan.ui.activity.ImageViewActivity;
import org.floens.chan.ui.view.GIFView; import org.floens.chan.ui.adapter.ImageViewAdapter;
import org.floens.chan.ui.view.NetworkPhotoView; import org.floens.chan.ui.view.ThumbnailImageView;
import org.floens.chan.utils.Logger; import org.floens.chan.ui.view.ThumbnailImageView.ThumbnailImageViewCallback;
import uk.co.senab.photoview.PhotoViewAttacher.OnViewTapListener;
import android.app.Fragment; import android.app.Fragment;
import android.content.Context; import android.content.Context;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Bundle; import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.RelativeLayout;
import android.widget.Toast;
import android.widget.VideoView; import android.widget.VideoView;
import com.android.volley.Response; public class ImageViewFragment extends Fragment implements ThumbnailImageViewCallback {
import com.android.volley.VolleyError;
import com.android.volley.toolbox.DiskBasedCache;
public class ImageViewFragment extends Fragment implements View.OnLongClickListener, OnViewTapListener, OnClickListener {
private static final String TAG = "ImageViewFragment";
private Context context; private Context context;
private RelativeLayout wrapper;
private Post post;
private ImageViewActivity activity; private ImageViewActivity activity;
private int index;
private CachingRequest movieRequest;
private Post post;
private boolean showProgressBar = true;
private ThumbnailImageView imageView;
private boolean isVideo = false;
private boolean videoVisible = false;
private boolean videoSetIconToPause = false;
public static ImageViewFragment newInstance(Post post, ImageViewActivity activity, int index) { public static ImageViewFragment newInstance(Post post, ImageViewActivity activity, int index) {
ImageViewFragment imageViewFragment = new ImageViewFragment(); ImageViewFragment imageViewFragment = new ImageViewFragment();
imageViewFragment.post = post; imageViewFragment.post = post;
imageViewFragment.activity = activity; imageViewFragment.activity = activity;
imageViewFragment.index = index;
return imageViewFragment; return imageViewFragment;
} }
@Override
public void onSaveInstanceState(Bundle bundle) {
// https://code.google.com/p/android/issues/detail?id=19917
bundle.putString("bug_19917", "bug_19917");
super.onSaveInstanceState(bundle);
}
public void showProgressBar(boolean e) {
activity.showProgressBar(e, index);
}
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (post == null) { if (post == null) {
@ -73,14 +44,13 @@ public class ImageViewFragment extends Fragment implements View.OnLongClickListe
} else { } else {
context = inflater.getContext(); context = inflater.getContext();
wrapper = new RelativeLayout(context); imageView = new ThumbnailImageView(context);
int padding = (int) context.getResources().getDimension(R.dimen.image_popup_padding); imageView.setCallback(this);
wrapper.setPadding(padding, padding, padding, padding);
wrapper.setGravity(Gravity.CENTER); int padding = (int) context.getResources().getDimension(R.dimen.image_view_padding);
imageView.setPadding(padding, padding, padding, padding);
wrapper.setOnClickListener(this);
return wrapper; return imageView;
} }
} }
@ -92,135 +62,92 @@ public class ImageViewFragment extends Fragment implements View.OnLongClickListe
// No restoring // No restoring
} else { } else {
if (!post.hasImage) { if (!post.hasImage) {
throw new IllegalArgumentException("No post / post has no image"); throw new IllegalArgumentException("Post has no image");
} }
imageView.setThumbnail(post.thumbnailUrl);
if (post.ext.equals("gif")) { if (post.ext.equals("gif")) {
loadGif(); imageView.setGif(post.imageUrl);
} else if (post.ext.equals("webm")) { } else if (post.ext.equals("webm")) {
if (ChanPreferences.getVideosEnabled()) { isVideo = true;
loadMovie(); activity.invalidateActionBar();
} else { showProgressBar(false);
loadOtherImage(post.thumbnailUrl);
}
} else { } else {
loadOtherImage(post.imageUrl); imageView.setBigImage(post.imageUrl);
} }
} }
} }
private void loadMovie() { @Override
movieRequest = new CachingRequest(post.imageUrl, new Response.Listener<Void>() { public void onSaveInstanceState(Bundle bundle) {
@Override // https://code.google.com/p/android/issues/detail?id=19917
public void onResponse(Void empty) { bundle.putString("bug_19917", "bug_19917");
if (movieRequest != null) { super.onSaveInstanceState(bundle);
try {
handleMovieResponse(movieRequest);
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(context, R.string.image_preview_failed, Toast.LENGTH_LONG).show();
}
}
showProgressBar(false);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(context, R.string.image_preview_failed, Toast.LENGTH_LONG).show();
showProgressBar(false);
}
});
movieRequest.setShouldCache(true);
ChanApplication.getVolleyRequestQueue().add(movieRequest);
showProgressBar(true);
} }
private void handleMovieResponse(CachingRequest request) throws IOException { public void onSelected(ImageViewAdapter adapter, int position) {
DiskBasedCache cache = (DiskBasedCache) ChanApplication.getVolleyRequestQueue().getCache(); activity.setProgressBarIndeterminateVisibility(showProgressBar);
File file = cache.getFileForKey(movieRequest.getCacheKey());
if (file.exists()) {
Logger.test("Showing video from " + file.getAbsolutePath());
final VideoView view = new VideoView(context);
view.setZOrderOnTop(true);
view.setOnLongClickListener(this);
view.setOnClickListener(this);
wrapper.addView(view, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
view.setOnPreparedListener(new OnPreparedListener() { String filename = post.filename + "." + post.ext;
@Override activity.getActionBar().setTitle(filename);
public void onPrepared(MediaPlayer mp) {
mp.setLooping(true); String text = (position + 1) + "/" + adapter.getCount();
view.start(); activity.getActionBar().setSubtitle(text);
}
});
view.setVideoPath(file.getAbsolutePath()); activity.invalidateActionBar();
} else {
Logger.e(TAG, "Cache file doesn't exist");
Toast.makeText(context, R.string.image_preview_failed, Toast.LENGTH_LONG).show();
}
} }
private void loadOtherImage(String url) { public void onPrepareOptionsMenu(int position, ImageViewAdapter adapter, Menu menu) {
NetworkPhotoView imageView = new NetworkPhotoView(context); MenuItem item = menu.findItem(R.id.action_image_play_state);
imageView.setImageViewFragment(this); item.setVisible(isVideo);
imageView.setFadeIn(100); item.setEnabled(isVideo);
imageView.setImageUrl(url, ChanApplication.getImageLoader());
imageView.setMaxScale(3f); VideoView view = imageView.getVideoView();
imageView.setOnLongClickListenerToAttacher(this); if (view != null) {
imageView.setOnViewTapListenerToAttacher(this); item.setIcon((videoSetIconToPause || view.isPlaying()) ? R.drawable.ic_action_pause : R.drawable.ic_action_play);
}
wrapper.addView(imageView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); videoSetIconToPause = false;
showProgressBar(true);
} }
private void loadGif() { public void customOnOptionsItemSelected(MenuItem item) {
ChanApplication.getVolleyRequestQueue().add(new GIFRequest(post.imageUrl, new Response.Listener<GIFView>() { if (item.getItemId() == R.id.action_image_play_state) {
@Override if (!videoVisible) {
public void onResponse(GIFView view) { videoVisible = true;
wrapper.addView(view, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); imageView.setVideo(post.imageUrl);
view.setOnLongClickListener(ImageViewFragment.this); } else {
view.setOnClickListener(ImageViewFragment.this); VideoView view = imageView.getVideoView();
showProgressBar(false); if (view != null) {
} if (!view.isPlaying()) {
}, new Response.ErrorListener() { view.start();
@Override } else {
public void onErrorResponse(VolleyError error) { view.pause();
Toast.makeText(context, R.string.image_preview_failed, Toast.LENGTH_LONG).show(); }
showProgressBar(false); }
} }
}, context));
activity.invalidateActionBar();
showProgressBar(true); }
} }
@Override public void showProgressBar(boolean e) {
/* showProgressBar = e;
* TODO: figure out why adding an onLongClick listener removes the error: activity.callOnSelect();
* "ImageView no longer exists. You should not use this PhotoViewAttacher any more."
* ); PhotoViewAttacher line 300
*/
public boolean onLongClick(View v) {
return false;
} }
@Override @Override
public void onViewTap(View view, float x, float y) { public void onTap() {
activity.finish(); activity.finish();
} }
@Override
public void setProgress(boolean progress) {
showProgressBar(progress);
}
@Override @Override
public void onClick(View v) { public void onVideoLoaded() {
activity.finish(); videoSetIconToPause = true;
activity.invalidateActionBar();
} }
} }

@ -28,7 +28,7 @@ import com.android.volley.toolbox.ImageLoader.ImageListener;
/** /**
* Custom version of NetworkImageView * Custom version of NetworkImageView
* *
* Handles fetching an image from a URL as well as the life-cycle of the * Handles fetching an image from a URL as well as the life-cycle of the
* associated request. * associated request.
*/ */
@ -74,7 +74,7 @@ public class CustomNetworkImageView extends ImageView {
/** /**
* How larger the inner bitmap is to the defined view size. * How larger the inner bitmap is to the defined view size.
* *
* @param amount * @param amount
*/ */
public void setMaxScale(float amount) { public void setMaxScale(float amount) {
@ -87,7 +87,7 @@ public class CustomNetworkImageView extends ImageView {
/** /**
* Animate the image fading in. * Animate the image fading in.
* *
* @param duration * @param duration
* duration of the fade animation in milliseconds * duration of the fade animation in milliseconds
*/ */
@ -100,12 +100,12 @@ public class CustomNetworkImageView extends ImageView {
* calling this will immediately either set the cached image (if available) * calling this will immediately either set the cached image (if available)
* or the default image specified by * or the default image specified by
* {@link CustomNetworkImageView#setDefaultImageResId(int)} on the view. * {@link CustomNetworkImageView#setDefaultImageResId(int)} on the view.
* *
* NOTE: If applicable, * NOTE: If applicable,
* {@link CustomNetworkImageView#setDefaultImageResId(int)} and * {@link CustomNetworkImageView#setDefaultImageResId(int)} and
* {@link CustomNetworkImageView#setErrorImageResId(int)} should be called * {@link CustomNetworkImageView#setErrorImageResId(int)} should be called
* prior to calling this function. * prior to calling this function.
* *
* @param url * @param url
* The URL that should be loaded into this ImageView. * The URL that should be loaded into this ImageView.
* @param imageLoader * @param imageLoader
@ -139,7 +139,7 @@ public class CustomNetworkImageView extends ImageView {
/** /**
* Loads the image for the view if it isn't already loaded. * Loads the image for the view if it isn't already loaded.
* *
* @param isInLayoutPass * @param isInLayoutPass
* True if this was invoked from a layout pass, false otherwise. * True if this was invoked from a layout pass, false otherwise.
*/ */
@ -207,12 +207,9 @@ public class CustomNetworkImageView extends ImageView {
@Override @Override
public void onResponse(final ImageContainer response, boolean isImmediate) { public void onResponse(final ImageContainer response, boolean isImmediate) {
// If this was an immediate response that was delivered inside // If this was an immediate response that was delivered inside of a layout
// of a layout // pass do not set the image immediately as it will trigger a requestLayout
// pass do not set the image immediately as it will trigger a // inside of a layout. Instead, defer setting the image by posting back to
// requestLayout
// inside of a layout. Instead, defer setting the image by
// posting back to
// the main thread. // the main thread.
if (isImmediate && isInLayoutPass) { if (isImmediate && isInLayoutPass) {
post(new Runnable() { post(new Runnable() {
@ -235,7 +232,7 @@ public class CustomNetworkImageView extends ImageView {
setImageResource(mDefaultImageId); setImageResource(mDefaultImageId);
} }
} }
}, (int)(maxWidth * mMaxScale), (int)(maxHeight * mMaxScale)); }, (int) (maxWidth * mMaxScale), (int) (maxHeight * mMaxScale));
// update the ImageContainer to be the new bitmap container. // update the ImageContainer to be the new bitmap container.
mImageContainer = newContainer; mImageContainer = newContainer;

@ -12,70 +12,76 @@ import android.widget.LinearLayout;
import android.widget.ProgressBar; import android.widget.ProgressBar;
/** /**
* Container for a view with an ProgressBar. Toggles between the view and a ProgressBar. * Container for a view with an ProgressBar. Toggles between the view and a
* ProgressBar.
*/ */
public class LoadView extends FrameLayout { public class LoadView extends FrameLayout {
public int fadeDuration = 100; public int fadeDuration = 100;
private View currentView;
public LoadView(Context context) { public LoadView(Context context) {
super(context); super(context);
init(); init();
} }
public LoadView(Context context, AttributeSet attrs) { public LoadView(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
init(); init();
} }
public LoadView(Context context, AttributeSet attrs, int defStyleAttr) { public LoadView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr); super(context, attrs, defStyleAttr);
init(); init();
} }
private void init() { private void init() {
setView(null); setView(null, false);
} }
/** /**
* Set the content of this container. It will fade the old one out with the new one. * Set the content of this container. It will fade the old one out with the
* Set view to null to show the progressbar. * new one. Set view to null to show the progressbar.
* @param view the view or null for a progressbar. *
* @param view
* the view or null for a progressbar.
*/ */
public void setView(View view) { public void setView(View view) {
setView(view, true);
}
public void setView(View view, boolean animation) {
if (view == null) { if (view == null) {
LinearLayout layout = new LinearLayout(getContext()); LinearLayout layout = new LinearLayout(getContext());
layout.setGravity(Gravity.CENTER); layout.setGravity(Gravity.CENTER);
ProgressBar pb = new ProgressBar(getContext()); ProgressBar pb = new ProgressBar(getContext());
layout.addView(pb); layout.addView(pb);
view = layout; view = layout;
} }
while (getChildCount() > 2) { while (getChildCount() > 1) {
removeViewAt(0); removeViewAt(0);
} }
View currentView = getChildAt(0);
if (currentView != null) { if (currentView != null) {
final View tempView = currentView; if (animation) {
currentView.animate().setDuration(fadeDuration).alpha(0).setListener(new SimpleAnimatorListener() { final View tempView = currentView;
@Override currentView.animate().setDuration(fadeDuration).alpha(0).setListener(new SimpleAnimatorListener() {
public void onAnimationEnd(Animator animation) { @Override
removeView(tempView); public void onAnimationEnd(Animator animation) {
} removeView(tempView);
}); }
});
} else {
removeView(currentView);
}
} }
addView(view); addView(view);
view.setAlpha(0f);
view.animate().setDuration(fadeDuration).alpha(1f); if (animation) {
view.setAlpha(0f);
currentView = view; view.animate().setDuration(fadeDuration).alpha(1f);
}
} }
} }

@ -0,0 +1,226 @@
package org.floens.chan.ui.view;
import java.io.File;
import org.floens.chan.ChanApplication;
import org.floens.chan.R;
import org.floens.chan.core.net.FileRequest;
import org.floens.chan.core.net.GIFRequest;
import org.floens.chan.utils.Utils;
import uk.co.senab.photoview.PhotoViewAttacher;
import uk.co.senab.photoview.PhotoViewAttacher.OnViewTapListener;
import android.content.Context;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnPreparedListener;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
import android.widget.VideoView;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageLoader.ImageContainer;
import com.android.volley.toolbox.ImageLoader.ImageListener;
public class ThumbnailImageView extends LoadView implements OnViewTapListener, View.OnClickListener {
private ThumbnailImageViewCallback callback;
/**
* Max amount to scale the image inside the view
*/
private final float maxScale = 3f;
private boolean thumbnailNeeded = true;
private VideoView videoView;
public ThumbnailImageView(Context context) {
super(context);
init();
}
public ThumbnailImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ThumbnailImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
setOnClickListener(this);
}
public void setCallback(ThumbnailImageViewCallback callback) {
this.callback = callback;
}
public void setThumbnail(String thumbnailUrl) {
ChanApplication.getImageLoader().get(thumbnailUrl, new ImageListener() {
@Override
public void onErrorResponse(VolleyError error) {
onError();
}
@Override
public void onResponse(ImageContainer response, boolean isImmediate) {
if (response.getBitmap() != null && thumbnailNeeded) {
ImageView thumbnail = new ImageView(getContext());
thumbnail.setImageBitmap(response.getBitmap());
thumbnail.setLayoutParams(Utils.MATCH_PARAMS);
setView(thumbnail, false);
}
}
}, getWidth(), getHeight());
}
public void setBigImage(String imageUrl) {
callback.setProgress(true);
ChanApplication.getImageLoader().get(imageUrl, new ImageListener() {
@Override
public void onErrorResponse(VolleyError error) {
onError();
}
@Override
public void onResponse(ImageContainer response, boolean isImmediate) {
if (response.getBitmap() != null) {
CleanupImageView image = new CleanupImageView(getContext());
image.setImageBitmap(response.getBitmap());
PhotoViewAttacher attacher = new PhotoViewAttacher(image);
attacher.setOnViewTapListener(ThumbnailImageView.this);
attacher.setMaximumScale(maxScale);
image.setAttacher(attacher);
setView(image, !isImmediate);
callback.setProgress(false);
thumbnailNeeded = false;
}
}
}, (int) (getWidth() * maxScale), (int) (getHeight() * maxScale));
}
public void setGif(String gifUrl) {
callback.setProgress(true);
ChanApplication.getVolleyRequestQueue().add(new GIFRequest(gifUrl, new Response.Listener<GIFView>() {
@Override
public void onResponse(GIFView view) {
view.setLayoutParams(Utils.MATCH_PARAMS);
setView(view, false);
callback.setProgress(false);
thumbnailNeeded = false;
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
onError();
}
}, getContext()));
}
public void setVideo(String videoUrl) {
callback.setProgress(true);
ChanApplication.getVolleyRequestQueue().add(new FileRequest(videoUrl, new Response.Listener<File>() {
@Override
public void onResponse(File file) {
if (file != null) {
videoView = new VideoView(getContext());
videoView.setZOrderOnTop(true);
videoView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
videoView.setLayoutParams(Utils.MATCH_PARAMS);
LayoutParams par = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
par.gravity = Gravity.CENTER;
videoView.setLayoutParams(par);
videoView.setOnPreparedListener(new OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mp.setLooping(true);
callback.onVideoLoaded();
}
});
videoView.setVideoPath(file.getAbsolutePath());
videoView.start();
setView(videoView, false);
callback.setProgress(false);
thumbnailNeeded = false;
} else {
onError();
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
onError();
}
}));
}
public VideoView getVideoView() {
return videoView;
}
public void onError() {
Toast.makeText(getContext(), R.string.image_preview_failed, Toast.LENGTH_LONG).show();
callback.setProgress(false);
}
@Override
public void onViewTap(View view, float x, float y) {
callback.onTap();
}
@Override
public void onClick(View v) {
callback.onTap();
}
public static interface ThumbnailImageViewCallback {
public void onTap();
public void setProgress(boolean progress);
public void onVideoLoaded();
}
private static class CleanupImageView extends ImageView {
private PhotoViewAttacher attacher;
public CleanupImageView(Context context) {
super(context);
}
public CleanupImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CleanupImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setAttacher(PhotoViewAttacher attacher) {
this.attacher = attacher;
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (attacher != null) {
attacher.cleanup();
}
}
}
}

@ -7,8 +7,14 @@ import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
public class Utils { public class Utils {
public final static ViewGroup.LayoutParams MATCH_PARAMS = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
public final static ViewGroup.LayoutParams WRAP_PARAMS = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
public final static ViewGroup.LayoutParams MATCH_WRAP_PARAMS = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
public final static ViewGroup.LayoutParams WRAP_MATCH_PARAMS = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT);
public static int dp(Context context, float dp) { public static int dp(Context context, float dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics()); return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
} }

Loading…
Cancel
Save