/******************************************************************************
* Copyright (C) 2012, 2013, 2014, 2015, 2016
* Younghyung Cho. <yhcting77@gmail.com>
* All rights reserved.
*
* This file is part of NetMBuddy
*
* This program is licensed under the FreeBSD license
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of the FreeBSD Project.
*****************************************************************************/
package free.yhc.netmbuddy;
import android.content.Context;
import android.graphics.Bitmap;
import android.support.annotation.NonNull;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import java.net.MalformedURLException;
import java.net.URL;
import free.yhc.abaselib.AppEnv;
import free.yhc.baselib.Logger;
import free.yhc.baselib.async.TmTask;
import free.yhc.abaselib.util.AUtil;
import free.yhc.netmbuddy.core.TaskManager;
import free.yhc.netmbuddy.task.YTThumbnailTask;
import free.yhc.netmbuddy.utils.UxUtil;
public abstract class YTSearchAdapter<T> extends BaseAdapter {
private static final boolean DBG = Logger.DBG_DEFAULT;
private static final Logger P = Logger.create(YTSearchAdapter.class, Logger.LOGLV_DEFAULT);
// So, assign one of them as view tag's key value.
protected static final int VTAGKEY_VALID = R.id.content;
protected final Context mCxt;
// View holder for each item
protected View[] mItemViews;
protected T[] mItems;
private Bitmap[] mThumbnails;
private final YTThumbnailTask.EventListener<YTThumbnailTask, Bitmap> mThumbnailTaskEventListener
= new YTThumbnailTask.EventListener<YTThumbnailTask, Bitmap>() {
@Override
public void
onPostRun(@NonNull YTThumbnailTask task,
Bitmap result,
Exception ex) {
if (null == result)
// set to thumbthing else?
return;
// View is NOT reused here.
// So, I don't need to worry about issues comes from reusing view in the list.
int i = (Integer)task.getOpaque();
ImageView iv = (ImageView)mItemViews[i].findViewById(R.id.thumbnail);
mThumbnails[i] = result;
iv.setImageBitmap(result);
}
};
YTSearchAdapter(Context context,
int rowLayout,
T[] items) {
super();
mCxt = context;
mItems = items;
mItemViews = new View[mItems.length];
for (int i = 0; i < mItemViews.length; i++)
mItemViews[i] = AUtil.inflateLayout(rowLayout);
TaskManager tm = TaskManager.get();
mThumbnails = new Bitmap[mItems.length];
for (int i = 0; i < mItemViews.length; i++) {
// NOTE!
// IMPORTANT! : DO NOT put R.drawable.ic_unknown_image at layout!
// Because of 'memory optimization' for thumbnail bitmap,
// putting drawable at Layout may lead to "Exception : try to used recycled bitmap ...".
// See comments at UxUtil.setThumbnailImageView() for details.
// Initialize thumbnail to ic_unknown_image
mThumbnails[i] = null;
if (null == getThumnailUrl(mItems[i]))
continue;
UxUtil.setThumbnailImageView((ImageView)mItemViews[i].findViewById(R.id.thumbnail), null);
setViewInvalid(mItemViews[i]);
URL url;
try {
url = new URL(getThumnailUrl(mItems[i]));
} catch (MalformedURLException e) {
if (DBG) P.w("MalformedURL?: " + getThumnailUrl(mItems[i]));
continue;
}
YTThumbnailTask t = YTThumbnailTask.create(
url,
AUtil.getResources().getDimensionPixelSize(R.dimen.thumbnail_width),
AUtil.getResources().getDimensionPixelSize(R.dimen.thumbnail_height),
i);
t.addEventListener(AppEnv.getUiHandlerAdapter(),mThumbnailTaskEventListener);
if (!tm.addTask(t,
t,
this,
null)) {
if (DBG) P.w("Fail to add thumbnail download task");
}
}
}
protected abstract String
getThumnailUrl(T item);
protected abstract void
setItemView(int position, View v, T item);
protected int
pos2index(int pos) {
return pos + 1;
}
protected void
setViewInvalid(View v) {
v.setTag(VTAGKEY_VALID, false);
}
@SuppressWarnings("unused")
protected void
setViewInvalid(int pos) {
setViewInvalid(mItemViews[pos]);
}
protected void
setViewValid(View v) {
v.setTag(VTAGKEY_VALID, true);
}
protected boolean
isViewValid(int pos) {
return (Boolean)mItemViews[pos].getTag(VTAGKEY_VALID);
}
/**
* This should be called when adapter is no more used.
* Adapter caching each music icons.
* So, it occupies lots of memory.
* To free those memories before GC, calling cleanup might be useful.
*/
public void
cleanup() {
P.bug(AUtil.isUiThread());
for (int i = 0; i < mThumbnails.length; i++) {
if (null != mThumbnails[i]) {
mThumbnails[i].recycle();
mThumbnails[i] = null;
}
}
TaskManager tm = TaskManager.get();
for (TmTask t : tm.getTasks(this))
tm.cancelTask(t);
}
public T[]
getItems() {
return mItems;
}
public Bitmap
getItemThumbnail(int pos) {
if (0 <= pos
&& mThumbnails.length > pos)
return mThumbnails[pos];
return null;
}
@Override
public int
getCount() {
return mItems.length;
}
@Override
public Object
getItem(int position) {
return mItems[position];
}
@Override
public long
getItemId(int position) {
return pos2index(position);
}
@Override
public View
getView(int position, View convertView, ViewGroup parent) {
View v = mItemViews[position];
if (isViewValid(position))
return v;
T item = mItems[position];
setItemView(position, v, item);
return v;
}
}