From 7e0c024a998e09c265c0bf7def766d0d88e8a35b Mon Sep 17 00:00:00 2001 From: Florens Douwes Date: Wed, 9 Apr 2014 19:53:43 +0200 Subject: [PATCH] Refactored ImageViewFragment The image, gif and webm things got their own view. Made videos playable and pausable through the actionbar. --- Chan/res/drawable-hdpi/ic_action_play.png | Bin 0 -> 437 bytes Chan/res/drawable-mdpi/ic_action_play.png | Bin 0 -> 339 bytes Chan/res/drawable-xhdpi/ic_action_play.png | Bin 0 -> 522 bytes Chan/res/drawable-xxhdpi/ic_action_play.png | Bin 0 -> 702 bytes Chan/res/menu/image_view.xml | 17 +- Chan/res/values/dimens.xml | 4 +- Chan/res/values/strings.xml | 1 + .../{CachingRequest.java => FileRequest.java} | 20 +- .../chan/ui/activity/ImageViewActivity.java | 153 ++++++------ .../chan/ui/adapter/ImageViewAdapter.java | 7 +- .../floens/chan/ui/adapter/PostAdapter.java | 2 +- .../chan/ui/fragment/ImageViewFragment.java | 233 ++++++------------ .../chan/ui/view/CustomNetworkImageView.java | 23 +- .../src/org/floens/chan/ui/view/LoadView.java | 72 +++--- .../chan/ui/view/ThumbnailImageView.java | 226 +++++++++++++++++ Chan/src/org/floens/chan/utils/Utils.java | 6 + 16 files changed, 479 insertions(+), 285 deletions(-) create mode 100644 Chan/res/drawable-hdpi/ic_action_play.png create mode 100644 Chan/res/drawable-mdpi/ic_action_play.png create mode 100644 Chan/res/drawable-xhdpi/ic_action_play.png create mode 100644 Chan/res/drawable-xxhdpi/ic_action_play.png rename Chan/src/org/floens/chan/core/net/{CachingRequest.java => FileRequest.java} (53%) create mode 100644 Chan/src/org/floens/chan/ui/view/ThumbnailImageView.java diff --git a/Chan/res/drawable-hdpi/ic_action_play.png b/Chan/res/drawable-hdpi/ic_action_play.png new file mode 100644 index 0000000000000000000000000000000000000000..9a2147eccf47037a214ad4588856e2af4f4af166 GIT binary patch literal 437 zcmV;m0ZRUfP){!L~L2Tc?gmY>W|{>{TYnE5-bgMmt=QmNWj zRaG-MVg`ue8@z=leX4Ev32!fmy!0H0; z08;k7g+6Hxp9Gc?;58qBIGgBY1h5*zCUO&a$_QW;Nm{MiirryHt-6%?Sh51x`B=$( zM2rCS`AA%?B|ZS^Ysu-~wZtcYRV*M2RscI6Yd{=-oe!6*giBpyzq+%$fyN4ZR!<=d zVa^6%HrPE1KQq386waEi;XVPb^3?BaGw^^nuD;;Hj{Dq1W5$?5nH2B*bF*!LTw<|c zI|m+8_?7J-DSWA*uZ{aXO;KR8q> fl}hzVPXPu1yws;8#Piok00000NkvXXu0mjfk6gL~ literal 0 HcmV?d00001 diff --git a/Chan/res/drawable-mdpi/ic_action_play.png b/Chan/res/drawable-mdpi/ic_action_play.png new file mode 100644 index 0000000000000000000000000000000000000000..b94fbe6e38881055417b18ebbefc00a6479b799d GIT binary patch literal 339 zcmV-Z0j&OsP)wy3N|ML!X z2=o9Q(Lig55Q+jHAl?LYMA|^60Z<5}0Ufa!h>Zs_5lRB_OrRq`SZW}1fiW~%im8!4 zD9MJAP=`bgWH|r|0Z>RRBR73es~}Q?rjH&{(+9OnLI+s3B{qG~swf7P8=z?M9mu)> zlx^#w+17Z_Rz@r|H2XOa&tha`+&mBtm<7bkftG)!cL*E+;y$3o&*&Wmw}7}8Xz^Kk zr-9GVcwRmbiEug8^3U|n1)yMVBqg4yigP)j!c zl}e>j{ZC!jl~c;c^mt|t5Ka&asQ{ebAoc*^2r-ig;GqiQ8z5}BwuTAxo)v;vNd@5K zD!^4j15rr?@H9yX=M8%f{$L`$dx34SrtA-9gfrF%Vks5C!ymSIZB`Ni_}8X`u%qG+ zCegX?5MLDh!6ZDpM1N>fj@5=h75ZcKTp|G99~$(BQX&A~AO7YCsQ{k-fNOUp5rFRx z$Czt3bpX-VZY}^`*Y2fM0RC7VWbNh%An4l789Z&-u=tCgB(^{cQA6@png_Tt|LEK2oB`Y+c0>D|BLIt~-b%a%&*<)UjKd0IeeM#! zN*jdknD|B7BxvzkYsV^gN&wDia<|mwS0w23I_9-T6+qDOl^cLMb)TmKFlbHX3?Rsw z$`Qb5<10k~!PitS0HUp_WaYlaPwB6+eE^i1N~Kb%R4SEC;$pb0l_222&5>$VaH?7dy=ow$S-Tpenpu~00000 z00000007{hY`5D(x?n5dUg{DNewZX$ zoa3iYu%jQ2y-SR*+)tHv7lk*=-D z;hAS;EQN@@ljF08h`p2ZFObIGZetDa0u+rk+{NBe1Ss=~v)DVD0L4x09aVtt2aPpm z2(Z1jHXD2QAVB$wy%AtLmNeFwBfyRvYuvODd-p2f`+hKuy?Yn%eLtAQ8VLBgAFBVF z4*|RPL$%l&0sHsE=&?5fen(^PGX&V~hf#jbhX7*^Xu+S@NZn1R(`N+KXDKyE^EW&J zby-VYOXJ@dL3MeTT1ey9?11`nK98CuOSnESQnzExc?9gA^SSY_Ttn;6`P}#w?*aAa z{8Ab}@FHe2=ZCScl?d29=Z9L?N(5Ah4-oJ>Lz(rP)(;v@z^Z)f-@F_+ih$R0tz3IM zxOV|2@c{xp88s$G!2WCHwWm2BML^Y<7y%8(2R#Wei-~&|U>+0qDxjH|xHkcta;;oO z9yhHDXfh`5L4e!%z`Fn!F>zD@ZerqS0#*}eF>w?DA>#uN0^G;Mo&|)6iCw}!qXPf{ k000000002MZaxGU0AWNaUcsk$JOBUy07*qoM6N<$g7ES&*Z=?k literal 0 HcmV?d00001 diff --git a/Chan/res/menu/image_view.xml b/Chan/res/menu/image_view.xml index 1994db71..9d30282d 100644 --- a/Chan/res/menu/image_view.xml +++ b/Chan/res/menu/image_view.xml @@ -1,9 +1,16 @@ + + + android:id="@+id/action_image_save" + android:orderInCategory="2" + android:showAsAction="never" + android:title="@string/image_save"/> - + \ No newline at end of file diff --git a/Chan/res/values/dimens.xml b/Chan/res/values/dimens.xml index b38361f2..b1b42513 100644 --- a/Chan/res/values/dimens.xml +++ b/Chan/res/values/dimens.xml @@ -5,12 +5,10 @@ 11dp 6dp - 48dp - 70dp 24dp 14dp 200dp - 8dp + 8dp diff --git a/Chan/res/values/strings.xml b/Chan/res/values/strings.xml index a593c58d..47689e10 100644 --- a/Chan/res/values/strings.xml +++ b/Chan/res/values/strings.xml @@ -29,6 +29,7 @@ Saving image failed Cannot make save directory Cannot write to storage + Start / stop playing Failed to show image Failed to open image diff --git a/Chan/src/org/floens/chan/core/net/CachingRequest.java b/Chan/src/org/floens/chan/core/net/FileRequest.java similarity index 53% rename from Chan/src/org/floens/chan/core/net/CachingRequest.java rename to Chan/src/org/floens/chan/core/net/FileRequest.java index a2700ce2..172329b5 100644 --- a/Chan/src/org/floens/chan/core/net/CachingRequest.java +++ b/Chan/src/org/floens/chan/core/net/FileRequest.java @@ -1,16 +1,21 @@ package org.floens.chan.core.net; +import java.io.File; + +import org.floens.chan.ChanApplication; + import com.android.volley.NetworkResponse; import com.android.volley.Request; import com.android.volley.Response; import com.android.volley.Response.ErrorListener; import com.android.volley.Response.Listener; +import com.android.volley.toolbox.DiskBasedCache; import com.android.volley.toolbox.HttpHeaderParser; -public class CachingRequest extends Request { - protected final Listener listener; +public class FileRequest extends Request { + protected final Listener listener; - public CachingRequest(String url, Listener listener, ErrorListener errorListener) { + public FileRequest(String url, Listener listener, ErrorListener errorListener) { super(Method.GET, url, errorListener); this.listener = listener; @@ -24,6 +29,13 @@ public class CachingRequest extends Request { @Override 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); + } } } diff --git a/Chan/src/org/floens/chan/ui/activity/ImageViewActivity.java b/Chan/src/org/floens/chan/ui/activity/ImageViewActivity.java index afcee46b..910e8ceb 100644 --- a/Chan/src/org/floens/chan/ui/activity/ImageViewActivity.java +++ b/Chan/src/org/floens/chan/ui/activity/ImageViewActivity.java @@ -6,6 +6,7 @@ import org.floens.chan.R; import org.floens.chan.core.model.Post; import org.floens.chan.ui.adapter.ImageViewAdapter; 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.Logger; @@ -18,56 +19,57 @@ import android.view.MenuItem; import android.view.Window; /** - * An fragment pager that contains images. Call setPosts first, - * and then start the activity with startActivity() + * An fragment pager that contains images. Call setPosts first, and then start + * the activity with startActivity() */ public class ImageViewActivity extends Activity implements ViewPager.OnPageChangeListener { private static final String TAG = "ImageViewActivity"; - + private static PostAdapter postAdapter; private static int selectedId = -1; - + + private ViewPager viewPager; private ImageViewAdapter adapter; private int currentPosition; - private boolean[] progressData; - + /** * 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) { postAdapter = adapter; selectedId = selected; } - + @Override protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); ActionBar actionBar = getActionBar(); actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_HOME_AS_UP); - + super.onCreate(savedInstanceState); - + if (postAdapter != null) { // Get the posts with images ArrayList imagePosts = new ArrayList(); for (Post post : postAdapter.getList()) { - if (post.hasImage){ + if (post.hasImage) { imagePosts.add(post); } } - + // Setup our pages and adapter 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.addToList(imagePosts); + adapter.setList(imagePosts); viewPager.setAdapter(adapter); viewPager.setOnPageChangeListener(this); - - progressData = new boolean[imagePosts.size()]; - + // Select the right image for (int i = 0; i < imagePosts.size(); i++) { if (imagePosts.get(i).no == selectedId) { @@ -77,18 +79,60 @@ public class ImageViewActivity extends Activity implements ViewPager.OnPageChang } } } else { - Logger.e(TAG, "Posts in imageview list was null"); + Logger.e(TAG, "Posts in ImageViewActivity was null"); finish(); } } - + + @Override + protected void onStop() { + super.onStop(); + + // Avoid things like out of sync, since this is an activity. + finish(); + } + @Override public void finish() { super.finish(); overridePendingTransition(R.anim.fade_in, R.anim.fade_out); 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 public boolean onOptionsItemSelected(MenuItem item) { 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) { Post post = adapter.getPost(currentPosition); ImageSaver.save(this, post.imageUrl, post.filename, post.ext); - + return true; } else { + ImageViewFragment fragment = getCurrentFragment(); + if (fragment != null) { + fragment.customOnOptionsItemSelected(item); + } + return super.onOptionsItemSelected(item); } } - + @Override public boolean onCreateOptionsMenu(Menu 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. - finish(); + return true; } @Override - public void onPageSelected(int position) { - currentPosition = position; - - setProgressBarIndeterminateVisibility(progressData[position]); - - Post post = adapter.getPost(position); - - if (post != null) { - String filename = post.filename + "." + post.ext; - String text = "(" + (position + 1) + "/" + progressData.length + ") " + filename; - - getActionBar().setTitle(text); + public boolean onPrepareOptionsMenu(Menu menu) { + ImageViewFragment fragment = getCurrentFragment(); + if (fragment != null) { + fragment.onPrepareOptionsMenu(currentPosition, adapter, menu); } - if (postAdapter != null) { - postAdapter.scrollToPost(post); - } - } - - @Override - public void onPageScrollStateChanged(int state) { + return super.onPrepareOptionsMenu(menu); } - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + private ImageViewFragment getCurrentFragment() { + Object o = adapter.instantiateItem(viewPager, currentPosition); + if (o instanceof ImageViewFragment) { + return (ImageViewFragment) o; + } else { + return null; + } } } - - - - - diff --git a/Chan/src/org/floens/chan/ui/adapter/ImageViewAdapter.java b/Chan/src/org/floens/chan/ui/adapter/ImageViewAdapter.java index aecbf9cb..b5142126 100644 --- a/Chan/src/org/floens/chan/ui/adapter/ImageViewAdapter.java +++ b/Chan/src/org/floens/chan/ui/adapter/ImageViewAdapter.java @@ -15,7 +15,6 @@ import android.view.View; public class ImageViewAdapter extends FragmentStatePagerAdapter { private final ImageViewActivity activity; - private int count = 0; private final ArrayList postList = new ArrayList(); public ImageViewAdapter(FragmentManager fragmentManager, ImageViewActivity activity) { @@ -25,7 +24,7 @@ public class ImageViewAdapter extends FragmentStatePagerAdapter { @Override public int getCount() { - return count; + return postList.size(); } @Override @@ -46,8 +45,8 @@ public class ImageViewAdapter extends FragmentStatePagerAdapter { view = null; } - public void addToList(ArrayList list){ - count += list.size(); + public void setList(ArrayList list){ + postList.clear(); postList.addAll(list); notifyDataSetChanged(); diff --git a/Chan/src/org/floens/chan/ui/adapter/PostAdapter.java b/Chan/src/org/floens/chan/ui/adapter/PostAdapter.java index b2a2f946..6bf0e8f4 100644 --- a/Chan/src/org/floens/chan/ui/adapter/PostAdapter.java +++ b/Chan/src/org/floens/chan/ui/adapter/PostAdapter.java @@ -92,7 +92,7 @@ public class PostAdapter extends BaseAdapter { view.init(threadManager, listView, this); int padding = context.getResources().getDimensionPixelSize(R.dimen.general_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.setGravity(Gravity.CENTER); return view; diff --git a/Chan/src/org/floens/chan/ui/fragment/ImageViewFragment.java b/Chan/src/org/floens/chan/ui/fragment/ImageViewFragment.java index 2beb52c8..2e091c63 100644 --- a/Chan/src/org/floens/chan/ui/fragment/ImageViewFragment.java +++ b/Chan/src/org/floens/chan/ui/fragment/ImageViewFragment.java @@ -1,70 +1,41 @@ 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.core.ChanPreferences; 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.view.GIFView; -import org.floens.chan.ui.view.NetworkPhotoView; -import org.floens.chan.utils.Logger; +import org.floens.chan.ui.adapter.ImageViewAdapter; +import org.floens.chan.ui.view.ThumbnailImageView; +import org.floens.chan.ui.view.ThumbnailImageView.ThumbnailImageViewCallback; -import uk.co.senab.photoview.PhotoViewAttacher.OnViewTapListener; import android.app.Fragment; import android.content.Context; -import android.media.MediaPlayer; -import android.media.MediaPlayer.OnPreparedListener; import android.os.Bundle; -import android.view.Gravity; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; -import android.view.View.OnClickListener; import android.view.ViewGroup; -import android.view.ViewGroup.LayoutParams; -import android.widget.RelativeLayout; -import android.widget.Toast; import android.widget.VideoView; -import com.android.volley.Response; -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"; - +public class ImageViewFragment extends Fragment implements ThumbnailImageViewCallback { private Context context; - private RelativeLayout wrapper; - private Post post; 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) { ImageViewFragment imageViewFragment = new ImageViewFragment(); imageViewFragment.post = post; imageViewFragment.activity = activity; - imageViewFragment.index = index; 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 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (post == null) { @@ -73,14 +44,13 @@ public class ImageViewFragment extends Fragment implements View.OnLongClickListe } else { context = inflater.getContext(); - wrapper = new RelativeLayout(context); - int padding = (int) context.getResources().getDimension(R.dimen.image_popup_padding); - wrapper.setPadding(padding, padding, padding, padding); - wrapper.setGravity(Gravity.CENTER); - - wrapper.setOnClickListener(this); + imageView = new ThumbnailImageView(context); + imageView.setCallback(this); + + int padding = (int) context.getResources().getDimension(R.dimen.image_view_padding); + imageView.setPadding(padding, padding, padding, padding); - return wrapper; + return imageView; } } @@ -92,135 +62,92 @@ public class ImageViewFragment extends Fragment implements View.OnLongClickListe // No restoring } else { 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")) { - loadGif(); + imageView.setGif(post.imageUrl); } else if (post.ext.equals("webm")) { - if (ChanPreferences.getVideosEnabled()) { - loadMovie(); - } else { - loadOtherImage(post.thumbnailUrl); - } + isVideo = true; + activity.invalidateActionBar(); + showProgressBar(false); } else { - loadOtherImage(post.imageUrl); + imageView.setBigImage(post.imageUrl); } } } - private void loadMovie() { - movieRequest = new CachingRequest(post.imageUrl, new Response.Listener() { - @Override - public void onResponse(Void empty) { - if (movieRequest != null) { - 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); + @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); } - private void handleMovieResponse(CachingRequest request) throws IOException { - DiskBasedCache cache = (DiskBasedCache) ChanApplication.getVolleyRequestQueue().getCache(); - 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); + public void onSelected(ImageViewAdapter adapter, int position) { + activity.setProgressBarIndeterminateVisibility(showProgressBar); - view.setOnPreparedListener(new OnPreparedListener() { - @Override - public void onPrepared(MediaPlayer mp) { - mp.setLooping(true); - view.start(); - - } - }); + String filename = post.filename + "." + post.ext; + activity.getActionBar().setTitle(filename); + + String text = (position + 1) + "/" + adapter.getCount(); + activity.getActionBar().setSubtitle(text); - view.setVideoPath(file.getAbsolutePath()); - } else { - Logger.e(TAG, "Cache file doesn't exist"); - Toast.makeText(context, R.string.image_preview_failed, Toast.LENGTH_LONG).show(); - } + activity.invalidateActionBar(); } - private void loadOtherImage(String url) { - NetworkPhotoView imageView = new NetworkPhotoView(context); - imageView.setImageViewFragment(this); - imageView.setFadeIn(100); - imageView.setImageUrl(url, ChanApplication.getImageLoader()); - imageView.setMaxScale(3f); - imageView.setOnLongClickListenerToAttacher(this); - imageView.setOnViewTapListenerToAttacher(this); - - wrapper.addView(imageView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); - - showProgressBar(true); + public void onPrepareOptionsMenu(int position, ImageViewAdapter adapter, Menu menu) { + MenuItem item = menu.findItem(R.id.action_image_play_state); + item.setVisible(isVideo); + item.setEnabled(isVideo); + + VideoView view = imageView.getVideoView(); + if (view != null) { + item.setIcon((videoSetIconToPause || view.isPlaying()) ? R.drawable.ic_action_pause : R.drawable.ic_action_play); + } + videoSetIconToPause = false; } - private void loadGif() { - ChanApplication.getVolleyRequestQueue().add(new GIFRequest(post.imageUrl, new Response.Listener() { - @Override - public void onResponse(GIFView view) { - wrapper.addView(view, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); - view.setOnLongClickListener(ImageViewFragment.this); - view.setOnClickListener(ImageViewFragment.this); - 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); + public void customOnOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.action_image_play_state) { + if (!videoVisible) { + videoVisible = true; + imageView.setVideo(post.imageUrl); + } else { + VideoView view = imageView.getVideoView(); + if (view != null) { + if (!view.isPlaying()) { + view.start(); + } else { + view.pause(); + } + } } - }, context)); - - showProgressBar(true); + + activity.invalidateActionBar(); + } } - @Override - /* - * TODO: figure out why adding an onLongClick listener removes the error: - * "ImageView no longer exists. You should not use this PhotoViewAttacher any more." - * ); PhotoViewAttacher line 300 - */ - public boolean onLongClick(View v) { - return false; + public void showProgressBar(boolean e) { + showProgressBar = e; + activity.callOnSelect(); } @Override - public void onViewTap(View view, float x, float y) { + public void onTap() { activity.finish(); } + + @Override + public void setProgress(boolean progress) { + showProgressBar(progress); + } @Override - public void onClick(View v) { - activity.finish(); + public void onVideoLoaded() { + videoSetIconToPause = true; + activity.invalidateActionBar(); } } diff --git a/Chan/src/org/floens/chan/ui/view/CustomNetworkImageView.java b/Chan/src/org/floens/chan/ui/view/CustomNetworkImageView.java index 9d606a2f..647c6cdd 100644 --- a/Chan/src/org/floens/chan/ui/view/CustomNetworkImageView.java +++ b/Chan/src/org/floens/chan/ui/view/CustomNetworkImageView.java @@ -28,7 +28,7 @@ import com.android.volley.toolbox.ImageLoader.ImageListener; /** * Custom version of NetworkImageView - * + * * Handles fetching an image from a URL as well as the life-cycle of the * associated request. */ @@ -74,7 +74,7 @@ public class CustomNetworkImageView extends ImageView { /** * How larger the inner bitmap is to the defined view size. - * + * * @param amount */ public void setMaxScale(float amount) { @@ -87,7 +87,7 @@ public class CustomNetworkImageView extends ImageView { /** * Animate the image fading in. - * + * * @param duration * 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) * or the default image specified by * {@link CustomNetworkImageView#setDefaultImageResId(int)} on the view. - * + * * NOTE: If applicable, * {@link CustomNetworkImageView#setDefaultImageResId(int)} and * {@link CustomNetworkImageView#setErrorImageResId(int)} should be called * prior to calling this function. - * + * * @param url * The URL that should be loaded into this ImageView. * @param imageLoader @@ -139,7 +139,7 @@ public class CustomNetworkImageView extends ImageView { /** * Loads the image for the view if it isn't already loaded. - * + * * @param isInLayoutPass * True if this was invoked from a layout pass, false otherwise. */ @@ -207,12 +207,9 @@ public class CustomNetworkImageView extends ImageView { @Override public void onResponse(final ImageContainer response, boolean isImmediate) { - // If this was an immediate response that was delivered inside - // of a layout - // pass do not set the image immediately as it will trigger a - // requestLayout - // inside of a layout. Instead, defer setting the image by - // posting back to + // If this was an immediate response that was delivered inside of a layout + // pass do not set the image immediately as it will trigger a requestLayout + // inside of a layout. Instead, defer setting the image by posting back to // the main thread. if (isImmediate && isInLayoutPass) { post(new Runnable() { @@ -235,7 +232,7 @@ public class CustomNetworkImageView extends ImageView { setImageResource(mDefaultImageId); } } - }, (int)(maxWidth * mMaxScale), (int)(maxHeight * mMaxScale)); + }, (int) (maxWidth * mMaxScale), (int) (maxHeight * mMaxScale)); // update the ImageContainer to be the new bitmap container. mImageContainer = newContainer; diff --git a/Chan/src/org/floens/chan/ui/view/LoadView.java b/Chan/src/org/floens/chan/ui/view/LoadView.java index 74e74392..95b7d36e 100644 --- a/Chan/src/org/floens/chan/ui/view/LoadView.java +++ b/Chan/src/org/floens/chan/ui/view/LoadView.java @@ -12,70 +12,76 @@ import android.widget.LinearLayout; 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 int fadeDuration = 100; - - private View currentView; - + public LoadView(Context context) { super(context); init(); } - + public LoadView(Context context, AttributeSet attrs) { super(context, attrs); init(); } - + public LoadView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); 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 view to null to show the progressbar. - * @param view the view or null for a progressbar. + * Set the content of this container. It will fade the old one out with the + * new one. Set view to null to show the progressbar. + * + * @param view + * the view or null for a progressbar. */ public void setView(View view) { + setView(view, true); + } + + public void setView(View view, boolean animation) { if (view == null) { LinearLayout layout = new LinearLayout(getContext()); layout.setGravity(Gravity.CENTER); - + ProgressBar pb = new ProgressBar(getContext()); layout.addView(pb); view = layout; } - - while (getChildCount() > 2) { + + while (getChildCount() > 1) { removeViewAt(0); } - + + View currentView = getChildAt(0); if (currentView != null) { - final View tempView = currentView; - currentView.animate().setDuration(fadeDuration).alpha(0).setListener(new SimpleAnimatorListener() { - @Override - public void onAnimationEnd(Animator animation) { - removeView(tempView); - } - }); + if (animation) { + final View tempView = currentView; + currentView.animate().setDuration(fadeDuration).alpha(0).setListener(new SimpleAnimatorListener() { + @Override + public void onAnimationEnd(Animator animation) { + removeView(tempView); + } + }); + } else { + removeView(currentView); + } } - + addView(view); - view.setAlpha(0f); - view.animate().setDuration(fadeDuration).alpha(1f); - - currentView = view; + + if (animation) { + view.setAlpha(0f); + view.animate().setDuration(fadeDuration).alpha(1f); + } } } - - - - - diff --git a/Chan/src/org/floens/chan/ui/view/ThumbnailImageView.java b/Chan/src/org/floens/chan/ui/view/ThumbnailImageView.java new file mode 100644 index 00000000..96f7f7d9 --- /dev/null +++ b/Chan/src/org/floens/chan/ui/view/ThumbnailImageView.java @@ -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() { + @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() { + @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(); + } + } + } +} diff --git a/Chan/src/org/floens/chan/utils/Utils.java b/Chan/src/org/floens/chan/utils/Utils.java index fe6936ed..c82d0d89 100644 --- a/Chan/src/org/floens/chan/utils/Utils.java +++ b/Chan/src/org/floens/chan/utils/Utils.java @@ -7,8 +7,14 @@ import android.os.Handler; import android.os.Looper; import android.util.TypedValue; import android.view.View; +import android.view.ViewGroup; 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) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics()); }