package com.seafile.seadroid2.ui.activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.google.common.collect.Lists;
import com.seafile.seadroid2.ui.HackyViewPager;
import com.seafile.seadroid2.util.ConcurrentAsyncTask;
import com.seafile.seadroid2.R;
import com.seafile.seadroid2.SeafException;
import com.seafile.seadroid2.SettingsManager;
import com.seafile.seadroid2.account.Account;
import com.seafile.seadroid2.data.DataManager;
import com.seafile.seadroid2.data.SeafDirent;
import com.seafile.seadroid2.data.SeafPhoto;
import com.seafile.seadroid2.ui.WidgetUtils;
import com.seafile.seadroid2.ui.ZoomOutPageTransformer;
import com.seafile.seadroid2.ui.adapter.GalleryAdapter;
import com.seafile.seadroid2.ui.adapter.SeafItemAdapter;
import com.seafile.seadroid2.ui.dialog.DeleteFileDialog;
import com.seafile.seadroid2.ui.dialog.TaskDialog;
import com.seafile.seadroid2.util.Utils;
import java.util.Collections;
import java.util.List;
/**
* A gallery of images with sliding, zooming, multi-touch and single touch support
* Local cached images will be shown directly, while cloud images will be asynchronously downloaded first
*/
public class GalleryActivity extends BaseActivity {
public static final String DEBUG_TAG = "GalleryActivity";
private HackyViewPager mViewPager;
private LinearLayout mPageIndexContainer;
private TextView mPageIndexTextView;
private TextView mPageCountTextView;
private TextView mPageNameTextView;
private ImageView mDeleteBtn;
private ImageView mStarBtn;
private ImageView mShareBtn;
private LinearLayout mToolbar;
private DataManager dataMgr;
private Account mAccount;
private String repoName;
private String repoID;
private String dirPath;
private String fileName;
private String STATE_FILE_NAME;
private int mPageIndex;
private GalleryAdapter mGalleryAdapter;
private List<SeafPhoto> mPhotos = Lists.newArrayList();
/** flag to mark if the tool bar was shown */
private boolean showToolBar = true;
private View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.gallery_delete_photo:
deleteFile(repoID, Utils.pathJoin(dirPath, fileName));
break;
case R.id.gallery_star_photo:
starFile(repoID, dirPath, fileName);
break;
case R.id.gallery_share_photo:
shareFile(repoID, false, Utils.pathJoin(dirPath, fileName));
break;
}
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.gallery_activity_layout);
mDeleteBtn = (ImageView) findViewById(R.id.gallery_delete_photo);
mStarBtn = (ImageView) findViewById(R.id.gallery_star_photo);
mShareBtn = (ImageView) findViewById(R.id.gallery_share_photo);
mToolbar = (LinearLayout) findViewById(R.id.gallery_tool_bar);
mDeleteBtn.setOnClickListener(onClickListener);
mStarBtn.setOnClickListener(onClickListener);
mShareBtn.setOnClickListener(onClickListener);
mViewPager = (HackyViewPager) findViewById(R.id.gallery_pager);
mViewPager.setPageTransformer(true, new ZoomOutPageTransformer());
mViewPager.setOffscreenPageLimit(1);
mViewPager.setOnPageChangeListener(new HackyViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// page index starting from 1 instead of 0 in user interface, so plus one here
mPageIndexTextView.setText(String.valueOf(position + 1));
mPageIndex = position;
// fixed IndexOutOfBoundsException when accessing list
if (mPageIndex == mPhotos.size()) return;
fileName = mPhotos.get(mPageIndex).getName();
mPageNameTextView.setText(fileName);
}
@Override
public void onPageSelected(int position) {}
@Override
public void onPageScrollStateChanged(int state) {}
});
mPageIndexContainer = (LinearLayout) findViewById(R.id.page_index_container);
mPageIndexTextView = (TextView) findViewById(R.id.gallery_page_index);
mPageCountTextView = (TextView) findViewById(R.id.gallery_page_count);
mPageNameTextView = (TextView) findViewById(R.id.gallery_page_name);
repoName = getIntent().getStringExtra("repoName");
repoID = getIntent().getStringExtra("repoId");
dirPath = getIntent().getStringExtra("path");
mAccount = getIntent().getParcelableExtra("account");
fileName = getIntent().getStringExtra("fileName");
dataMgr = new DataManager(mAccount);
displayPhotosInGallery(repoName, repoID, dirPath);
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the current image file name
savedInstanceState.putString(STATE_FILE_NAME, fileName);
// Log.d(DEBUG_TAG, "onSaveInstanceState " + fileName);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
// Always call the superclass so it can restore the view hierarchy
super.onRestoreInstanceState(savedInstanceState);
// Restore state members from saved instance
fileName = savedInstanceState.getString(STATE_FILE_NAME);
// Log.d(DEBUG_TAG, "onRestoreInstanceState " + fileName);
navToSelectedPage();
}
/**
* Load thumbnail urls in order to display them in the gallery.
* Prior to use caches to calculate those urls.
* If caches are not available, load them asynchronously.
*
* NOTE: When user browsing files in "LIBRARY" tab, he has to navigate into a repo in order to open gallery.
* Method which get called is {@link com.seafile.seadroid2.ui.fragment.ReposFragment#navToReposView(boolean)} or {@link com.seafile.seadroid2.ui.fragment.ReposFragment#navToDirectory(boolean)},
* so seafDirents were already cached and it will always use them to calculate thumbnail urls for displaying photos in gallery.
* But for browsing "STARRED" tab, caches of starred files may or may not cached, that is where the asynchronous loading code segment comes into use.
* @param repoID
* @param dirPath
*/
private void displayPhotosInGallery(String repoName, String repoID, String dirPath) {
// calculate thumbnail urls by cached dirents
List<SeafDirent> seafDirents = dataMgr.getCachedDirents(repoID, dirPath);
if (seafDirents != null) {
// sort files by type and order
seafDirents = sortFiles(seafDirents,
SettingsManager.instance().getSortFilesTypePref(),
SettingsManager.instance().getSortFilesOrderPref());
for (SeafDirent seafDirent : seafDirents) {
if (!seafDirent.isDir()
&& Utils.isViewableImage(seafDirent.name)) {
mPhotos.add(new SeafPhoto(repoName, repoID, dirPath, seafDirent));
}
}
mGalleryAdapter = new GalleryAdapter(GalleryActivity.this,
mAccount,
mPhotos,
dataMgr);
mViewPager.setAdapter(mGalleryAdapter);
navToSelectedPage();
} else {
if (!Utils.isNetworkOn()) {
showShortToast(this, R.string.network_down);
// data is not available
finish();
}
// load photos asynchronously
LoadPhotosTask task = new LoadPhotosTask(repoName, repoID, dirPath);
ConcurrentAsyncTask.execute(task);
}
}
/**
* Load photos asynchronously, use {@link SeafPhoto} to manage state of each photo instance
*/
private class LoadPhotosTask extends AsyncTask<String, Void, List<SeafPhoto>> {
private String repoName, repoID, dirPath;
private SeafException err = null;
public LoadPhotosTask(String repoName, String repoID, String dirPath) {
this.repoName = repoName;
this.repoID = repoID;
this.dirPath = dirPath;
}
@Override
protected List<SeafPhoto> doInBackground(String... params) {
List<SeafPhoto> photos = Lists.newArrayList();
List<SeafDirent> seafDirents;
try {
seafDirents = dataMgr.getDirentsFromServer(repoID, dirPath);
} catch (SeafException e) {
err = e;
return null;
}
if (seafDirents == null)
return null;
// sort photos according to global sort settings
seafDirents = sortFiles(seafDirents,
SettingsManager.instance().getSortFilesTypePref(),
SettingsManager.instance().getSortFilesOrderPref());
for (SeafDirent seafDirent : seafDirents) {
if (!seafDirent.isDir()
&& Utils.isViewableImage(seafDirent.name)) {
photos.add(new SeafPhoto(repoName, repoID, dirPath, seafDirent));
}
}
return photos;
}
@Override
protected void onPostExecute(List<SeafPhoto> photos) {
if (photos.isEmpty()
|| fileName == null) {
if (err != null) {
showShortToast(GalleryActivity.this, R.string.gallery_load_photos_error);
Log.e(DEBUG_TAG, "error message " + err.getMessage() + " error code " + err.getCode());
}
return;
}
mPhotos = photos;
mGalleryAdapter = new GalleryAdapter(GalleryActivity.this, mAccount, photos, dataMgr);
mViewPager.setAdapter(mGalleryAdapter);
navToSelectedPage();
}
}
/**
* Sorts the given list by type and order.
* Sorting type is one of {@link SeafItemAdapter#SORT_BY_NAME} or {@link SeafItemAdapter#SORT_BY_LAST_MODIFIED_TIME}.
* Sorting order is one of {@link SeafItemAdapter#SORT_ORDER_ASCENDING} or {@link SeafItemAdapter#SORT_ORDER_DESCENDING}.
*
* @param dirents
* @param type
* @param order
* @return sorted file list
*/
public List<SeafDirent> sortFiles(List<SeafDirent> dirents, int type, int order) {
// sort SeafDirents
if (type == SeafItemAdapter.SORT_BY_NAME) {
// sort by name, in ascending order
Collections.sort(dirents, new SeafDirent.DirentNameComparator());
if (order == SeafItemAdapter.SORT_ORDER_DESCENDING) {
Collections.reverse(dirents);
}
} else if (type == SeafItemAdapter.SORT_BY_LAST_MODIFIED_TIME) {
// sort by last modified time, in ascending order
Collections.sort(dirents, new SeafDirent.DirentLastMTimeComparator());
if (order == SeafItemAdapter.SORT_ORDER_DESCENDING) {
Collections.reverse(dirents);
}
}
return dirents;
}
/**
* Dynamically navigate to the starting page index selected by user
* by default the starting page index is 0
*
*/
private void navToSelectedPage() {
int size = mPhotos.size();
for (int i = 0; i < size; i++) {
if (mPhotos.get(i).getName().equals(fileName)) {
mViewPager.setCurrentItem(i);
mPageIndexTextView.setText(String.valueOf(i + 1));
mPageIndex = i;
mPageNameTextView.setText(fileName);
break;
}
}
mPageCountTextView.setText(String.valueOf(size));
}
/**
* This method will get called when tapping at the center of a photo,
* tool bar will auto hide when open the gallery,
* and will show or hide alternatively when tapping.
*/
public void hideOrShowToolBar() {
if (showToolBar) {
mToolbar.setVisibility(View.VISIBLE);
mPageIndexContainer.setVisibility(View.VISIBLE);
mPageNameTextView.setVisibility(View.VISIBLE);
} else {
mToolbar.setVisibility(View.GONE);
mPageIndexContainer.setVisibility(View.GONE);
mPageNameTextView.setVisibility(View.GONE);
}
showToolBar = !showToolBar;
}
private void deleteFile(String repoID, String path) {
final DeleteFileDialog dialog = new DeleteFileDialog();
dialog.init(repoID, path, false, mAccount);
dialog.setTaskDialogLisenter(new TaskDialog.TaskDialogListener() {
@Override
public void onTaskSuccess() {
showShortToast(GalleryActivity.this, R.string.delete_successful);
removePageAndRefreshView();
}
});
dialog.show(getSupportFragmentManager(), "DialogFragment");
}
private void starFile(String repoId, String dir, String fileName) {
if (!Utils.isNetworkOn()) {
showShortToast(this, R.string.network_down);
return;
}
String p = Utils.pathJoin(dir, fileName);
ConcurrentAsyncTask.execute(new StarFileTask(repoId, p));
}
private void shareFile(String repoID, boolean isEncrypt, String path) {
if (isEncrypt) {
WidgetUtils.inputSharePassword(this, repoID, path, false, mAccount);
} else {
WidgetUtils.chooseShareApp(this, repoID, path, false, mAccount, null, null);
}
}
class StarFileTask extends AsyncTask<Void, Void, Void> {
private String repoId;
private String path;
private SeafException err;
public StarFileTask(String repoId, String path) {
this.repoId = repoId;
this.path = path;
}
@Override
protected Void doInBackground(Void... params) {
if (dataMgr == null)
return null;
try {
dataMgr.star(repoId, path);
} catch (SeafException e) {
err = e;
}
return null;
}
@Override
protected void onPostExecute(Void v) {
if (err != null) {
showShortToast(GalleryActivity.this, R.string.star_file_failed);
return;
}
showShortToast(GalleryActivity.this, R.string.star_file_succeed);
}
}
/**
* slide to next page if there are pages on the right side of the current one,
* slide to previous page if not,
* quit the gallery if both cases were not met
*/
private void removePageAndRefreshView() {
mPhotos.remove(mPageIndex);
mGalleryAdapter.setItems(mPhotos);
mGalleryAdapter.notifyDataSetChanged();
int size = mPhotos.size();
mPageCountTextView.setText(String.valueOf(size));
if (size == 0) {
finish();
return;
}
mPageIndex = mPageIndex > size - 1 ? size -1 : mPageIndex;
// page index starting from 1 instead of 0 in user interface, so plus one here
mPageIndexTextView.setText(String.valueOf(mPageIndex + 1));
// update file name in gallery view
mPageNameTextView.setText(mPhotos.get(mPageIndex).getName());
}
}