package com.rafali.flickruploader.ui;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Click;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.UiThread;
import org.androidannotations.annotations.ViewById;
import org.androidannotations.api.BackgroundExecutor;
import org.slf4j.LoggerFactory;
import uk.co.senab.bitmapcache.CacheableBitmapDrawable;
import uk.co.senab.bitmapcache.CacheableImageView;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.rafali.common.STR;
import com.rafali.common.ToolString;
import com.rafali.flickruploader.enums.STATUS;
import com.rafali.flickruploader.enums.VIEW_SIZE;
import com.rafali.flickruploader.model.Media;
import com.rafali.flickruploader.service.UploadService;
import com.rafali.flickruploader.service.UploadService.UploadProgressListener;
import com.rafali.flickruploader.tool.Notifications;
import com.rafali.flickruploader.tool.Utils;
import com.rafali.flickruploader.ui.activity.FlickrUploaderActivity;
import com.rafali.flickruploader.ui.widget.TabView;
import com.rafali.flickruploader2.R;
@EViewGroup(R.layout.drawer_content)
public class DrawerContentView extends RelativeLayout implements UploadProgressListener {
static final org.slf4j.Logger LOG = LoggerFactory.getLogger(DrawerContentView.class);
public DrawerContentView(Context context, AttributeSet attrs) {
super(context, attrs);
activity = (FlickrUploaderActivity) getContext();
}
FlickrUploaderActivity activity = null;
@ViewById(R.id.pause_btn)
TextView pauseBtn;
@ViewById(R.id.clear_btn)
TextView clearBtn;
@ViewById(R.id.container)
LinearLayout container;
PhotoAdapter queuedAdapter;
PhotoAdapter uploadedAdapter;
PhotoAdapter failedAdapter;
@Click(R.id.bottom_bar)
void onBottomBarClick() {
// do nothing
}
@UiThread
void renderButtons() {
if (queueTabView.getCurrentItem() == TAB_UPLOADED_INDEX) {
pauseBtn.setVisibility(View.GONE);
clearBtn.setVisibility(View.GONE);
} else if (queueTabView.getCurrentItem() == TAB_QUEUED_INDEX) {
clearBtn.setVisibility(View.VISIBLE);
pauseBtn.setVisibility(View.VISIBLE);
if (isUploadManuallyPaused()) {
pauseBtn.setText("Resume");
} else {
pauseBtn.setText("Pause");
}
} else if (queueTabView.getCurrentItem() == TAB_FAILED_INDEX) {
clearBtn.setVisibility(View.VISIBLE);
pauseBtn.setVisibility(View.VISIBLE);
pauseBtn.setText("Retry all");
}
}
boolean isUploadManuallyPaused() {
return System.currentTimeMillis() < Utils.getLongProperty(STR.manuallyPaused);
}
public void setCurrentTab(int tabIndex) {
queueTabView.setCurrentItem(tabIndex);
}
@Click(R.id.pause_btn)
void onPauseClick() {
if (queueTabView.getCurrentItem() == TAB_QUEUED_INDEX) {
if (isUploadManuallyPaused()) {
Utils.setLongProperty(STR.manuallyPaused, 0L);
renderButtons();
UploadService.wake();
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("Pause uploads").setItems(new String[] { "for 30 minutes", "for 2 hours", "for 12 hours", "indefinitely" }, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
long delay = 0;
if (which == 0) {
delay = 30 * 60 * 1000L;
} else if (which == 1) {
delay = 2 * 60 * 60 * 1000L;
} else if (which == 2) {
delay = 12 * 60 * 60 * 1000L;
} else if (which == 3) {
delay = Long.MAX_VALUE;
}
Utils.setLongProperty(STR.manuallyPaused, delay == Long.MAX_VALUE ? delay : System.currentTimeMillis() + delay);
}
});
AlertDialog dialog = builder.create();
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
renderButtons();
}
});
dialog.show();
}
} else if (queueTabView.getCurrentItem() == TAB_FAILED_INDEX) {
List<Media> medias = Utils.loadMedia(false);
Iterator<Media> it = medias.iterator();
while (it.hasNext()) {
Media media = it.next();
if (!media.isFailed()) {
it.remove();
}
}
if (!medias.isEmpty()) {
UploadService.enqueueRetry(medias);
}
}
}
public static final int TAB_UPLOADED_INDEX = 0;
public static final int TAB_QUEUED_INDEX = 1;
public static final int TAB_FAILED_INDEX = 2;
@Click(R.id.clear_btn)
void onClearClick() {
final int tab = queueTabView.getCurrentItem();
if (tab == TAB_QUEUED_INDEX && !UploadService.getCurrentlyQueued().isEmpty()) {
Utils.showConfirmCancel(activity, "Cancel uploads", "Do you confirm aborting the current upload and clear the upload queue?", new Utils.Callback<Boolean>() {
@Override
public void onResult(Boolean result) {
if (result) {
clearTab(tab);
}
}
});
} else {
clearTab(tab);
}
}
private void clearTab(int tab) {
Notifications.clear();
UploadService.clear(tab == TAB_QUEUED_INDEX ? STATUS.QUEUED : STATUS.FAILED, new Utils.Callback<Void>() {
@Override
public void onResult(Void result) {
updateLists();
}
});
}
@AfterViews
void afterViews() {
queueTabView = new QueueTabView(getContext());
container.addView(queueTabView);
renderButtons();
}
View createEmptyView(String text) {
TextView tv = (TextView) View.inflate(getContext(), R.layout.no_data, null);
tv.setText(text);
return tv;
}
public void updateLists() {
if (activity != null && !activity.destroyed && !activity.isPaused() && activity.getSlidingDrawer() != null && activity.getSlidingDrawer().isOpened()) {
BackgroundExecutor.execute(new Runnable() {
@Override
public void run() {
try {
List<Media> currentlyQueued = new ArrayList<Media>(UploadService.getCurrentlyQueued());
Collections.sort(currentlyQueued, Utils.MEDIA_COMPARATOR);
Collections.reverse(currentlyQueued);
notifyDataSetChanged(queuedAdapter, currentlyQueued);
List<Media> recentlyUploaded = new ArrayList<Media>(UploadService.getRecentlyUploaded());
Collections.sort(recentlyUploaded, Utils.MEDIA_COMPARATOR_UPLOAD);
notifyDataSetChanged(uploadedAdapter, recentlyUploaded);
List<Media> failed = new ArrayList<Media>(UploadService.getFailed());
Collections.sort(failed, Utils.MEDIA_COMPARATOR);
notifyDataSetChanged(failedAdapter, failed);
} catch (Throwable e) {
LOG.error(ToolString.stack2string(e));
}
}
});
}
}
@UiThread
void notifyDataSetChanged(PhotoAdapter photoAdapter, List<Media> medias) {
if (!medias.equals(photoAdapter.medias)) {
photoAdapter.medias.clear();
photoAdapter.medias.addAll(medias);
photoAdapter.notifyDataSetChanged();
}
}
class QueueTabView extends TabView {
public QueueTabView(Context context) {
super(context, null, 3, TAB_QUEUED_INDEX);
}
@Override
protected View createTabViewItem(int position) {
View view = View.inflate(getContext(), R.layout.drawer_list_view, null);
ListView listView = (ListView) view.findViewById(R.id.list_view);
TextView emptyView = (TextView) view.findViewById(R.id.empty_list_item);
listView.setEmptyView(emptyView);
if (position == TAB_UPLOADED_INDEX) {
uploadedAdapter = new PhotoAdapter(new ArrayList<Media>(), getTabViewItemTitle(position));
listView.setAdapter(uploadedAdapter);
emptyView.setText("No media recently uploaded");
} else if (position == TAB_QUEUED_INDEX) {
queuedAdapter = new PhotoAdapter(new ArrayList<Media>(), getTabViewItemTitle(position));
listView.setAdapter(queuedAdapter);
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (view.getTag() instanceof Media) {
final Media media = (Media) view.getTag();
Utils.showConfirmCancel(activity, "Stop uploading", "Abort uploading " + media.getPath() + " - " + ToolString.readableFileSize(media.getSize()) + " ?",
new Utils.Callback<Boolean>() {
@Override
public void onResult(Boolean result) {
if (result) {
UploadService.dequeue(Arrays.asList(media));
}
}
});
}
}
});
emptyView.setText("No media queued for upload");
} else if (position == TAB_FAILED_INDEX) {
failedAdapter = new PhotoAdapter(new ArrayList<Media>(), getTabViewItemTitle(position));
listView.setAdapter(failedAdapter);
emptyView.setText("No upload errors");
}
return view;
}
@Override
protected int getTabViewItemTitle(int position) {
if (position == TAB_UPLOADED_INDEX) {
return R.string.uploaded;
} else if (position == TAB_QUEUED_INDEX) {
return R.string.queued;
} else if (position == TAB_FAILED_INDEX) {
return R.string.failed;
}
return R.string.ellipsis;
}
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
renderView(position);
renderButtons();
}
private void renderView(int position) {
View view = gridViewsArray[position];
if (view != null) {
ListView listView = (ListView) view.findViewById(R.id.list_view);
for (int i = 0; i < listView.getChildCount(); i++) {
renderThumbView(listView.getChildAt(i));
}
}
}
}
class PhotoAdapter extends BaseAdapter {
private List<Media> medias;
private int titleRes;
public PhotoAdapter(List<Media> medias, int titleRes) {
this.medias = medias;
this.titleRes = titleRes;
}
@Override
public int getCount() {
return medias.size();
}
@Override
public Object getItem(int position) {
return medias.get(position);
}
@Override
public long getItemId(int arg0) {
return arg0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = View.inflate(getContext(), R.layout.upload_status_thumb, null);
convertView.setTag(R.id.image_view, convertView.findViewById(R.id.image_view));
convertView.setTag(R.id.title, convertView.findViewById(R.id.title));
convertView.setTag(R.id.list_view, titleRes);
}
final Media media = medias.get(position);
if (convertView.getTag() != media) {
convertView.setTag(media);
renderThumbView(convertView);
}
return convertView;
}
}
ExecutorService executorService = Executors.newSingleThreadExecutor();
private QueueTabView queueTabView;
private void renderThumbView(final View convertView) {
final Media media = (Media) convertView.getTag();
if (media != null) {
final CacheableImageView imageView = (CacheableImageView) convertView.getTag(R.id.image_view);
final TextView titleView = (TextView) convertView.getTag(R.id.title);
int titleRes = (Integer) convertView.getTag(R.id.list_view);
String text = media.getPath() + " - " + ToolString.readableFileSize(media.getSize());
imageView.setTag(media);
int nbError = media.getRetries();
if (titleRes == R.string.queued) {
long since = System.currentTimeMillis() - media.getTimestampCreated();
text += "\ncreated " + ToolString.formatDuration(since) + " ago";
if (nbError > 0) {
text += "\nretry #" + nbError;
if (ToolString.isNotBlank(media.getErrorMessage())) {
text += "\n" + media.getErrorMessage();
}
}
if (media.getTimestampRetry() < Long.MAX_VALUE && media.getTimestampRetry() > 0 && System.currentTimeMillis() < media.getTimestampRetry()) {
text += "\nuploading in " + ToolString.formatDuration(media.getTimestampRetry() - System.currentTimeMillis());
}
} else if (titleRes == R.string.failed) {
long since;
if (media.getTimestampRetry() > 0 && media.getTimestampRetry() < Long.MAX_VALUE) {
since = System.currentTimeMillis() - media.getTimestampRetry();
} else {
since = System.currentTimeMillis() - media.getTimestampQueued();
}
text += "\nfailed " + ToolString.formatDuration(since) + " ago";
if (media.getRetries() > 0) {
text += "\nafter " + media.getRetries() + " retr" + (media.getRetries() > 1 ? "ies" : "y");
}
} else if (titleRes == R.string.uploaded) {
long since = System.currentTimeMillis() - media.getTimestampUploaded();
if (since > 0) {
text += "\nuploaded " + ToolString.formatDuration(since) + " ago";
if (media.getTimestampUploadStarted() > 0 && media.getTimestampUploaded() > media.getTimestampUploadStarted()) {
text += " in " + ToolString.formatDuration(media.getTimestampUploaded() - media.getTimestampUploadStarted());
}
if (media.getRetries() > 0) {
text += "\nafter " + media.getRetries() + " retr" + (media.getRetries() > 1 ? "ies" : "y");
}
}
}
titleView.setText(text);
final CacheableBitmapDrawable wrapper = Utils.getCache().getFromMemoryCache(media.getPath() + "_" + VIEW_SIZE.small);
if (wrapper != null && !wrapper.getBitmap().isRecycled()) {
// The cache has it, so just display it
imageView.setImageDrawable(wrapper);
} else {
imageView.setImageDrawable(null);
executorService.submit(new Runnable() {
@Override
public void run() {
if (imageView.getTag() == media) {
final CacheableBitmapDrawable bitmapDrawable;
if (wrapper != null && !wrapper.getBitmap().isRecycled()) {
bitmapDrawable = wrapper;
} else {
Bitmap bitmap = Utils.getBitmap(media, VIEW_SIZE.small);
if (bitmap != null) {
bitmapDrawable = Utils.getCache().put(media.getPath() + "_" + VIEW_SIZE.small, bitmap);
} else {
bitmapDrawable = null;
}
}
((Activity) getContext()).runOnUiThread(new Runnable() {
@Override
public void run() {
if (imageView.getTag() == media) {
if (wrapper != bitmapDrawable) {
imageView.setImageDrawable(bitmapDrawable);
}
}
}
});
}
}
});
}
}
}
@Override
public void onProgress(Media media) {
}
@Override
public void onQueued(int nbQueued, int nbAlreadyUploaded, int nbAlreadyQueued) {
updateLists();
}
@Override
public void onDequeued(int nbDequeued) {
updateLists();
}
@Override
public void onFinished(int nbUploaded, int nbErrors) {
updateLists();
}
@Override
public void onProcessed(Media media) {
updateLists();
}
@UiThread
public void renderCurrentView() {
if (activity != null && !activity.isPaused()) {
queueTabView.renderView(queueTabView.getCurrentItem());
}
}
}