// GalleryActivity.java
// twixelated image gallery
// TwixelCam - Copyright © 2016 Brigham Toskin


package com.ionoclast.twixelcam.activity;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import com.ionoclast.twixelcam.CameraTwixelator;
import com.ionoclast.twixelcam.R;

import java.io.File;
import java.util.Arrays;
import java.util.Comparator;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


/**
 * @author btoskin &lt;brigham@ionoclast.com&gt; Ionoclast Laboratories, LLC.
 */
public class GalleryActivity extends Activity
{
	ViewPager mGalTwixelated;
	File[] mFiles;
	Bitmap[] mImages;

	ThreadPoolExecutor mExecutor;

	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.gallery);

		mGalTwixelated = (ViewPager)findViewById(R.id.lst_gallery);

		mFiles = listFiles();
		mImages = new Bitmap[mFiles.length];
		mExecutor = new ThreadPoolExecutor(1, 4, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(4));
		new LoadBitmapsTask(0, 4).executeOnExecutor(mExecutor);

		mGalTwixelated.setAdapter(new TwixelatedPageAdapter());
	}

	private File[] listFiles()
	{
		File[] tFiles = CameraTwixelator.TWIXEL_DIR.listFiles();
		Arrays.sort(tFiles, new Comparator<File>()
		{
			@Override
			public int compare(File pLhs, File pRhs)
			{
				// sort newest first
				return Long.valueOf(pRhs.lastModified()).compareTo(pLhs.lastModified());
			}
		});
		return tFiles;
	}

	private class LoadBitmapsTask extends AsyncTask<Void, Void, Void>
	{
		private int mStartIndex, mCount;

		public LoadBitmapsTask(int pStart, int pCount)
		{
			mStartIndex = pStart;
			mCount = pCount;
		}

		@Override
		protected Void doInBackground(Void... pParams)
		{
			for(int i = mStartIndex; i < mStartIndex + mCount; ++i)
			{
				synchronized(mImages)
				{
					if(mImages[i] == null)
					{
						String tFile = mFiles[i].toString();
						mImages[i] = BitmapFactory.decodeFile(tFile);
					}
				}
			}

			return null;
		}

		@Override
		protected void onPostExecute(Void pVoid)
		{
			// ???
		}
	}

	private class TwixelatedPageAdapter extends PagerAdapter
	{
		@Override
		public int getCount()
		{
			return mImages.length;
		}

		@Override
		public Object instantiateItem(ViewGroup pParent, int pPosition)
		{
			ImageView tImageView = new ImageView(GalleryActivity.this);
			tImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);

			synchronized(mImages)
			{
				if(mImages[pPosition] == null)
				{
					// load this one on demand, and queue up the next few on a thread
					mImages[pPosition] = BitmapFactory.decodeFile(mFiles[pPosition].toString());
					int tNumToLoad = Math.min(mFiles.length - pPosition - 1, 4);
					if(tNumToLoad > 0)
					{
						new LoadBitmapsTask(pPosition + 1, tNumToLoad).executeOnExecutor(mExecutor);
					}
				}
			}
			tImageView.setImageBitmap(mImages[pPosition]);
			pParent.requestLayout();

			pParent.addView(tImageView);
			return tImageView;
		}

		@Override
		public boolean isViewFromObject(View pView, Object pObject)
		{
			return pView == pObject;
		}

		@Override
		public void destroyItem(ViewGroup pParent, final int pPosition, final Object pObject)
		{
			// clean up bitmap
			synchronized(mImages)
			{
				final Bitmap tBmp = mImages[pPosition];
				if(tBmp != null)
				{
					mImages[pPosition] = null;
					mExecutor.execute(new Runnable()
					{
						@Override
						public void run()
						{
							tBmp.recycle();
						}
					});
				}
			}

			// clear the image view
			ImageView tImg = (ImageView)pObject;
			tImg.setImageDrawable(null);

		}
	}
}

@@ -241,37 +241,8 @@
 ry;%0A
-import android.os.AsyncTask;%0A
 impo
@@ -259,24 +259,24 @@
 .os.Bundle;%0A
+
 import andro
@@ -596,24 +596,48 @@
 Comparator;%0A
+import java.util.Queue;%0A
 import java.
@@ -1368,16 +1368,34 @@
 (4));%0A%09%09
+mExecutor.execute(
 new Load
@@ -1415,36 +1415,8 @@
 , 4)
-.executeOnExecutor(mExecutor
 );%0A%0A
@@ -1847,43 +1847,27 @@
 ask 
-extends AsyncTask%3CVoid, Void, Void%3E
+implements Runnable
 %0A%09%7B%0A
@@ -2024,52 +2024,23 @@
 %0A%09%09p
-rotected Void doInBackground(Void... pParams
+ublic void run(
 )%0A%09%09
@@ -2288,99 +2288,8 @@
 %09%09%09%7D
-%0A%0A%09%09%09return null;%0A%09%09%7D%0A%0A%09%09@Override%0A%09%09protected void onPostExecute(Void pVoid)%0A%09%09%7B%0A%09%09%09// ???
 %0A%09%09%7D
@@ -2354,16 +2354,60 @@
 pter%0A%09%7B%0A
+%09%09private Queue%3CImageView%3E mRecycledViews;%0A%0A
 %09%09@Overr
@@ -3002,16 +3002,34 @@
 %7B%0A%09%09%09%09%09%09
+mExecutor.execute(
 new Load
@@ -3070,36 +3070,8 @@
 oad)
-.executeOnExecutor(mExecutor
 );%0A%09

Convert AsyncTask to simple Runnable.