package com.seafile.seadroid2.cameraupload;
import android.content.Intent;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.google.common.collect.Lists;
import com.seafile.seadroid2.R;
import com.seafile.seadroid2.SeafConnection;
import com.seafile.seadroid2.SeafException;
import com.seafile.seadroid2.account.Account;
import com.seafile.seadroid2.account.AccountManager;
import com.seafile.seadroid2.avatar.Avatar;
import com.seafile.seadroid2.avatar.AvatarManager;
import com.seafile.seadroid2.data.DataManager;
import com.seafile.seadroid2.data.SeafDirent;
import com.seafile.seadroid2.data.SeafRepo;
import com.seafile.seadroid2.ui.NavContext;
import com.seafile.seadroid2.ui.adapter.DirentsAdapter;
import com.seafile.seadroid2.ui.dialog.PasswordDialog;
import com.seafile.seadroid2.ui.dialog.TaskDialog;
import com.seafile.seadroid2.util.ConcurrentAsyncTask;
import com.seafile.seadroid2.util.Utils;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.List;
/**
* Choose account and library for camera upload
*/
public class CloudLibrarySelectionFragment extends Fragment {
public static final String DEBUG_TAG = "CloudLibrarySelectionFragment";
public static final String PASSWORD_DIALOG_FRAGMENT_TAG = "passwordDialogFragmentTag";
public static final String ONLY_SHOW_WRITABLE_REPOS = "onlyShowWritableRepos";
public static final String SHOW_ENCRYPTED_REPOS = "showEncryptedRepos";
public static final String ENCRYPTED_REPO_ID = "encryptedRepoId";
public static final String DATA_REPO_ID = "repoID";
public static final String DATA_REPO_NAME = "repoNAME";
public static final String DATA_DIR = "dir";
public static final String DATA_ACCOUNT = "account";
private static final int STEP_CHOOSE_ACCOUNT = 1;
private static final int STEP_CHOOSE_REPO = 2;
private static final int STEP_CHOOSE_DIR = 3;
private int mStep = 1;
private CameraUploadConfigActivity mActivity;
private CloudLibraryAccountAdapter mAccountAdapter;
private CloudLibraryAdapter mReposAdapter;
private DirentsAdapter mDirentsAdapter;
private AccountManager mAccountManager;
private DataManager mDataManager;
private NavContext mNavContext;
private Account mAccount;
private LoadAccountsTask mLoadAccountsTask;
private LoadReposTask mLoadReposTask;
private LoadDirTask mLoadDirTask;
private AvatarManager avatarManager;
private RelativeLayout mUpLayout;
private TextView mCurrentFolderText;
private TextView mEmptyText, mErrorText;
private ImageView mRefreshBtn;
private View mProgressContainer, mListContainer;
private ListView mFoldersListView;
private Cursor mCursor;
private String mCurrentDir;
private boolean canChooseAccount;
private boolean onlyShowWritableRepos;
private String encryptedRepoId;
/** only show repo list for camera upload */
private boolean isOnlyChooseRepo;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mActivity = (CameraUploadConfigActivity) getActivity();
Intent intent = mActivity.getIntent();
avatarManager = new AvatarManager();
Account account = intent.getParcelableExtra("account");
if (account == null) {
canChooseAccount = true;
} else {
mAccount = account;
}
onlyShowWritableRepos = intent.getBooleanExtra(ONLY_SHOW_WRITABLE_REPOS, true);
encryptedRepoId = intent.getStringExtra(ENCRYPTED_REPO_ID);
isOnlyChooseRepo = true;
View rootView = getActivity().getLayoutInflater().inflate(R.layout.cuc_multi_selection_layout, null);
mFoldersListView = (ListView) rootView.findViewById(R.id.cuc_multi_selection_lv);
mFoldersListView.setFastScrollEnabled(true);
mUpLayout = (RelativeLayout) rootView.findViewById(R.id.cuc_multi_selection_up_layout);
mCurrentFolderText = (TextView) rootView.findViewById(R.id.cuc_multi_selection_current_directory_txt);
mEmptyText = (TextView) rootView.findViewById(R.id.cuc_multi_selection_empty_msg);
mErrorText = (TextView) rootView.findViewById(R.id.cuc_multi_selection_error_msg);
mRefreshBtn = (ImageView) rootView.findViewById(R.id.cuc_multi_selection_refresh_iv);
mProgressContainer = rootView.findViewById(R.id.cuc_multi_selection_progress_container);
mListContainer = rootView.findViewById(R.id.cuc_multi_selection_list_container);
mRefreshBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
refreshList(true);
}
});
mUpLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
stepBack();
} catch (Exception e) {
e.printStackTrace();
}
}
});
mFoldersListView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
onListItemClick(parent, position, id);
}
});
if (canChooseAccount) {
chooseAccount();
} else {
chooseRepo();
}
return rootView;
}
@Override
public void onResume() {
super.onResume();
loadAvatarUrls(48);
}
private void refreshList(final boolean forceRefresh) {
switch (mStep) {
case STEP_CHOOSE_ACCOUNT:
if (mLoadAccountsTask != null && mLoadAccountsTask.getStatus() != AsyncTask.Status.FINISHED) {
return;
} else {
chooseAccount(false);
break;
}
case STEP_CHOOSE_REPO:
if (mLoadReposTask != null && mLoadReposTask.getStatus() != AsyncTask.Status.FINISHED) {
return;
} else {
chooseRepo(forceRefresh);
break;
}
case STEP_CHOOSE_DIR:
if (mLoadDirTask != null && mLoadDirTask.getStatus() != AsyncTask.Status.FINISHED) {
return;
} else {
SeafRepo repo = getDataManager().getCachedRepoByID(getNavContext().getRepoID());
if (repo.encrypted && !getDataManager().getRepoPasswordSet(repo.id)) {
String password = getDataManager().getRepoPassword(repo.id);
showPasswordDialog(repo.name, repo.id,
new TaskDialog.TaskDialogListener() {
@Override
public void onTaskSuccess() {
chooseRepo(forceRefresh);
}
} , password);
}
chooseDir(forceRefresh);
break;
}
}
}
public void onListItemClick(final View v, final int position, final long id) {
NavContext nav = getNavContext();
SeafRepo repo = null;
if (mStep == STEP_CHOOSE_REPO) {
repo = getReposAdapter().getItem(position);
//mCurrentFolderText.setText(nav.getRepoName());
} else if (mStep == STEP_CHOOSE_DIR) {
repo = getDataManager().getCachedRepoByID(nav.getRepoID());
}
if (repo != null) {
if (repo.encrypted && !getDataManager().getRepoPasswordSet(repo.id)) {
String password = getDataManager().getRepoPassword(repo.id);
showPasswordDialog(repo.name, repo.id, new TaskDialog.TaskDialogListener() {
@Override
public void onTaskSuccess() {
onListItemClick(v, position, id);
}
}, password);
return;
}
}
switch (mStep) {
case STEP_CHOOSE_ACCOUNT:
setAccount(getAccountAdapter().getItem(position));
mCurrentDir = mAccount.getDisplayName();
setCurrentDirText(mCurrentDir);
chooseRepo();
break;
case STEP_CHOOSE_REPO:
if (!isOnlyChooseRepo) {
nav.setRepoName(repo.name);
nav.setRepoID(repo.id);
nav.setDir("/", repo.root);
chooseDir();
}
mCurrentDir = getString(R.string.settings_cuc_remote_lib_repo, repo.name);
setCurrentDirText(mCurrentDir);
SeafRepo seafRepo = getReposAdapter().getItem(position);
onRepoSelected(mAccount, seafRepo);
break;
case STEP_CHOOSE_DIR:
SeafDirent dirent = getDirentsAdapter().getItem(position);
mCurrentDir += "/" + dirent.name;
setCurrentDirText(mCurrentDir);
if (dirent.type == SeafDirent.DirentType.FILE) {
return;
}
nav.setDir(Utils.pathJoin(nav.getDirPath(), dirent.name), dirent.id);
refreshDir();
break;
}
}
private void onRepoSelected(Account account, SeafRepo seafRepo) {
mActivity.saveCameraUploadInfo(account, seafRepo);
getReposAdapter().setSelectedRepo(seafRepo);
getReposAdapter().notifyDataSetChanged();
}
private void stepBack() {
stepBack(false);
}
private void stepBack(boolean cancelIfFirstStep) {
switch (mStep) {
case STEP_CHOOSE_ACCOUNT:
if (cancelIfFirstStep) {
mActivity.finish();
}
mUpLayout.setVisibility(View.INVISIBLE);
break;
case STEP_CHOOSE_REPO:
if (canChooseAccount) {
mCurrentDir = getString(R.string.settings_cuc_remote_lib_account);
setCurrentDirText(mCurrentDir);
chooseAccount(false);
} else if (cancelIfFirstStep) {
mActivity.finish();
}
break;
case STEP_CHOOSE_DIR:
if (getNavContext().isRepoRoot()) {
mCurrentDir = getAccountManager().getCurrentAccount().getEmail();
setCurrentDirText(mCurrentDir);
chooseRepo();
} else {
String path = getNavContext().getDirPath();
mCurrentDir = getNavContext().getRepoName() + Utils.getParentPath(path);
setCurrentDirText(mCurrentDir);
getNavContext().setDir(Utils.getParentPath(path), null);
refreshDir();
}
break;
}
}
private void showPasswordDialog() {
NavContext nav = getNavContext();
String repoName = nav.getRepoName();
String repoID = nav.getRepoID();
showPasswordDialog(repoName, repoID, new TaskDialog.TaskDialogListener() {
@Override
public void onTaskSuccess() {
refreshDir();
}
}, null);
}
public void showPasswordDialog(String repoName, String repoID,
TaskDialog.TaskDialogListener listener, String password) {
PasswordDialog passwordDialog = new PasswordDialog();
passwordDialog.setRepo(repoName, repoID, mAccount);
if (password != null) {
passwordDialog.setPassword(password);
}
passwordDialog.setTaskDialogLisenter(listener);
passwordDialog.show(mActivity.getSupportFragmentManager(), PASSWORD_DIALOG_FRAGMENT_TAG);
}
private void chooseDir() {
chooseDir(false);
}
private void chooseDir(boolean forceRefresh) {
mStep = STEP_CHOOSE_DIR;
mUpLayout.setVisibility(View.VISIBLE);
mEmptyText.setText(R.string.dir_empty);
setListAdapter(getDirentsAdapter());
refreshDir(forceRefresh);
}
private void chooseAccount() {
chooseAccount(true);
}
/**
* List all accounts
*/
private void chooseAccount(boolean forwardIfOnlyOneAccount) {
mStep = STEP_CHOOSE_ACCOUNT;
mUpLayout.setVisibility(View.INVISIBLE);
mEmptyText.setText(R.string.no_account);
mCurrentDir = getString(R.string.settings_cuc_remote_lib_account);
setCurrentDirText(mCurrentDir);
mLoadAccountsTask = new LoadAccountsTask(getAccountManager(), forwardIfOnlyOneAccount);
ConcurrentAsyncTask.execute(mLoadAccountsTask);
setListAdapter(getAccountAdapter());
}
/**
* List all repos
*/
private void chooseRepo() {
chooseRepo(false);
}
private void chooseRepo(boolean forceRefresh) {
mStep = STEP_CHOOSE_REPO;
mUpLayout.setVisibility(View.VISIBLE);
mCurrentDir = mAccount.getDisplayName();
setCurrentDirText(mCurrentDir);
setListAdapter(getReposAdapter());
getNavContext().setRepoID(null);
if (!Utils.isNetworkOn() || !forceRefresh) {
List<SeafRepo> repos = getDataManager().getReposFromCache();
if (repos != null) {
updateAdapterWithRepos(repos);
return;
}
}
mLoadReposTask = new LoadReposTask(getDataManager());
ConcurrentAsyncTask.execute(mLoadReposTask);
}
private void refreshDir() {
refreshDir(false);
}
private void refreshDir(boolean forceRefresh) {
String repoID = getNavContext().getRepoID();
String dirPath = getNavContext().getDirPath();
if (!Utils.isNetworkOn() || !forceRefresh) {
List<SeafDirent> dirents = getDataManager().getCachedDirents(
getNavContext().getRepoID(), getNavContext().getDirPath());
if (dirents != null) {
updateAdapterWithDirents(dirents);
return;
}
}
mLoadDirTask = new LoadDirTask(repoID, dirPath, getDataManager());
ConcurrentAsyncTask.execute(mLoadDirTask);
}
private void updateAdapterWithDirents(List<SeafDirent> dirents) {
getDirentsAdapter().setDirents(dirents);
showListOrEmptyText(dirents.size());
}
private void updateAdapterWithRepos(List<SeafRepo> repos) {
// remove encrypted repos in order to "hide" them in selection list
List<SeafRepo> filteredRepos = Lists.newArrayList();
for (SeafRepo repo : repos) {
if (!repo.encrypted)
filteredRepos.add(repo);
}
getReposAdapter().setRepos(filteredRepos);
showListOrEmptyText(filteredRepos.size());
}
private CloudLibraryAccountAdapter getAccountAdapter() {
if (mAccountAdapter == null) {
mAccountAdapter = new CloudLibraryAccountAdapter(mActivity);
}
return mAccountAdapter;
}
private CloudLibraryAdapter getReposAdapter() {
if (mReposAdapter == null) {
mReposAdapter = new CloudLibraryAdapter(onlyShowWritableRepos, encryptedRepoId);
}
return mReposAdapter;
}
private DirentsAdapter getDirentsAdapter() {
if (mDirentsAdapter == null) {
mDirentsAdapter = new DirentsAdapter();
}
return mDirentsAdapter;
}
private void showListOrEmptyText(int listSize) {
if (listSize == 0) {
mFoldersListView.setVisibility(View.GONE);
mEmptyText.setVisibility(View.VISIBLE);
} else {
mFoldersListView.setVisibility(View.VISIBLE);
mEmptyText.setVisibility(View.GONE);
}
}
private void setListAdapter(BaseAdapter adapter) {
mFoldersListView.setAdapter(adapter);
}
private DataManager getDataManager() {
if (mDataManager == null) {
mDataManager = new DataManager(mAccount);
}
return mDataManager;
}
private void setAccount(Account account) {
mAccount = account;
mDataManager = new DataManager(account);
}
private AccountManager getAccountManager() {
if (mAccountManager == null) {
mAccountManager = new AccountManager(getActivity());
}
return mAccountManager;
}
private NavContext getNavContext() {
if (mNavContext == null) {
mNavContext = new NavContext();
}
return mNavContext;
}
/**
* Sets the current directory's text.
*/
private void setCurrentDirText(String text) {
mCurrentFolderText.setText(text);
}
@Override
public void onDestroy() {
super.onDestroy();
getActivity().finish();
}
@Override
public void onPause() {
super.onPause();
getActivity().finish();
}
@Override
public void onDestroyView() {
super.onDestroyView();
if (isRemoving()) {
mCursor.close();
mCursor = null;
}
}
private class LoadAccountsTask extends AsyncTask<Void, Void, Void> {
private List<Account> accounts;
private Exception err;
private AccountManager accountManager;
private boolean forwardIfOnlyOneAccount;
public LoadAccountsTask(AccountManager accountManager, boolean forwardIfOnlyOneAccount) {
this.accountManager = accountManager;
this.forwardIfOnlyOneAccount = forwardIfOnlyOneAccount;
}
@Override
protected void onPreExecute() {
showLoading(true);
}
@Override
protected Void doInBackground(Void... params) {
try {
accounts = accountManager.getAccountList();
} catch (Exception e) {
err = e;
}
return null;
}
@Override
protected void onPostExecute(Void v) {
showLoading(false);
if (err != null || accounts == null) {
setErrorMessage(R.string.load_accounts_fail);
if (err != null) {
Log.d(DEBUG_TAG, "failed to load accounts: " + err.getMessage());
}
return;
}
if (accounts.size() == 1 && forwardIfOnlyOneAccount) {
// Only 1 account. Go to next step.
setAccount(accounts.get(0));
chooseRepo();
return;
}
CloudLibraryAccountAdapter adapter = getAccountAdapter();
adapter.clear();
for (Account account: accounts) {
adapter.add(account);
}
adapter.notifyDataSetChanged();
showListOrEmptyText(accounts.size());
}
}
private class LoadReposTask extends AsyncTask<Void, Void, Void> {
private List<SeafRepo> repos;
private SeafException err;
private DataManager dataManager;
public LoadReposTask(DataManager dataManager) {
this.dataManager = dataManager;
}
@Override
protected Void doInBackground(Void... params) {
try {
repos = dataManager.getReposFromServer();
} catch (SeafException e) {
err = e;
}
return null;
}
@Override
protected void onPreExecute() {
showLoading(true);
}
@Override
protected void onPostExecute(Void v) {
if (mStep != STEP_CHOOSE_REPO) {
return;
}
showLoading(false);
if (err != null || repos == null) {
setErrorMessage(R.string.load_libraries_fail);
Log.d(DEBUG_TAG, "failed to load repos: " + (err != null ? err.getMessage() : " no error present"));
return;
}
updateAdapterWithRepos(repos);
}
}
private class LoadDirTask extends AsyncTask<Void, Void, Void> {
private String repoID, dirPath;
private SeafException err;
private DataManager dataManager;
private List<SeafDirent> dirents;
public LoadDirTask(String repoID, String dirPath, DataManager dataManager) {
this.repoID = repoID;
this.dirPath = dirPath;
this.dataManager = dataManager;
}
@Override
protected void onPreExecute() {
showLoading(true);
}
@Override
protected Void doInBackground(Void... params) {
try {
dirents = dataManager.getDirentsFromServer(repoID, dirPath);
} catch (SeafException e) {
err = e;
}
return null;
}
@Override
protected void onPostExecute(Void v) {
if (mStep != STEP_CHOOSE_DIR) {
return;
}
getDirentsAdapter().clearDirents();
showLoading(false);
if (err != null) {
int retCode = err.getCode();
if (retCode == SeafConnection.HTTP_STATUS_REPO_PASSWORD_REQUIRED) {
showPasswordDialog();
} else if (retCode == HttpURLConnection.HTTP_NOT_FOUND) {
final String message = String.format(getString(R.string.op_exception_folder_deleted), dirPath);
mActivity.showShortToast(mActivity, message);
} else {
Log.d(DEBUG_TAG, "failed to load dirents: " + err.getMessage());
err.printStackTrace();
setErrorMessage(R.string.load_dir_fail);
}
return;
}
if (dirents == null) {
Log.d(DEBUG_TAG, "failed to load dirents: no error present");
setErrorMessage(R.string.load_dir_fail);
return;
}
updateAdapterWithDirents(dirents);
}
}
/**
* asynchronously load avatars
*
* @param avatarSize set a avatar size in one of 24*24, 32*32, 48*48, 64*64, 72*72, 96*96
*/
public void loadAvatarUrls(int avatarSize) {
List<Avatar> avatars;
if (!Utils.isNetworkOn() || !avatarManager.isNeedToLoadNewAvatars()) {
// Toast.makeText(AccountsActivity.this, getString(R.string.network_down), Toast.LENGTH_SHORT).show();
// use cached avatars
avatars = avatarManager.getAvatarList();
if (avatars == null) {
return;
}
// set avatars url to adapter
mAccountAdapter.setAvatars((ArrayList<Avatar>) avatars);
// notify adapter data changed
mAccountAdapter.notifyDataSetChanged();
return;
}
LoadAvatarUrlsTask task = new LoadAvatarUrlsTask(avatarSize);
ConcurrentAsyncTask.execute(task);
}
private class LoadAvatarUrlsTask extends AsyncTask<Void, Void, List<Avatar>> {
private List<Avatar> avatars;
private int avatarSize;
private SeafConnection httpConnection;
public LoadAvatarUrlsTask(int avatarSize) {
this.avatarSize = avatarSize;
this.avatars = Lists.newArrayList();
}
@Override
protected List<Avatar> doInBackground(Void... params) {
// reuse cached avatars
avatars = avatarManager.getAvatarList();
// contains accounts who don`t have avatars yet
List<Account> acts = avatarManager.getAccountsWithoutAvatars();
// contains new avatars in order to persist them to database
List<Avatar> newAvatars = new ArrayList<Avatar>(acts.size());
// load avatars from server
for (Account account : acts) {
httpConnection = new SeafConnection(account);
String avatarRawData = null;
try {
avatarRawData = httpConnection.getAvatar(account.getEmail(), avatarSize);
} catch (SeafException e) {
e.printStackTrace();
return avatars;
}
Avatar avatar = avatarManager.parseAvatar(avatarRawData);
if (avatar == null)
continue;
avatar.setSignature(account.getSignature());
avatars.add(avatar);
newAvatars.add(avatar);
}
// save new added avatars to database
avatarManager.saveAvatarList(newAvatars);
return avatars;
}
@Override
protected void onPostExecute(List<Avatar> avatars) {
if (avatars == null) {
return;
}
// set avatars url to adapter
mAccountAdapter.setAvatars((ArrayList<Avatar>) avatars);
// notify adapter data changed
mAccountAdapter.notifyDataSetChanged();
}
}
private void setErrorMessage(int resID) {
//mContentArea.setVisibility(View.GONE);
mErrorText.setVisibility(View.VISIBLE);
mErrorText.setText(getString(resID));
}
private void clearError() {
mErrorText.setVisibility(View.GONE);
//mContentArea.setVisibility(View.VISIBLE);
}
private void showLoading(boolean loading) {
clearError();
if (loading) {
mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
mActivity, android.R.anim.fade_in));
mListContainer.startAnimation(AnimationUtils.loadAnimation(
mActivity, android.R.anim.fade_out));
mProgressContainer.setVisibility(View.VISIBLE);
mListContainer.setVisibility(View.INVISIBLE);
} else {
mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
mActivity, android.R.anim.fade_out));
mListContainer.startAnimation(AnimationUtils.loadAnimation(
mActivity, android.R.anim.fade_in));
mProgressContainer.setVisibility(View.GONE);
mListContainer.setVisibility(View.VISIBLE);
}
}
}