package com.seafile.seadroid2.ui.activity; import android.annotation.SuppressLint; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.support.v7.app.ActionBar; import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.animation.AnimationUtils; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.ListView; import android.widget.TextView; import com.seafile.seadroid2.R; import com.seafile.seadroid2.SeafConnection; import com.seafile.seadroid2.SeafException; import com.seafile.seadroid2.SettingsManager; import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.account.AccountManager; 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.AccountAdapter; import com.seafile.seadroid2.ui.adapter.DirentsAdapter; import com.seafile.seadroid2.ui.adapter.SeafAccountAdapter; import com.seafile.seadroid2.ui.adapter.SeafReposAdapter; import com.seafile.seadroid2.ui.dialog.PasswordDialog; import com.seafile.seadroid2.ui.dialog.TaskDialog; import com.seafile.seadroid2.ui.fragment.SettingsFragment; import com.seafile.seadroid2.util.ConcurrentAsyncTask; import com.seafile.seadroid2.util.Utils; import java.net.HttpURLConnection; import java.util.List; /** * Path chooser - Let the user choose a target path (account, repo, dir) */ public class SeafilePathChooserActivity extends BaseActivity implements Toolbar.OnMenuItemClickListener { private static final String DEBUG_TAG = "SeafilePathChooserActivity"; public static final String PASSWORD_DIALOG_FRAGMENT_TAG = "password_dialog_fragment_tag"; private NavContext mNavContext; private Account mAccount; private AccountManager mAccountManager; private DataManager mDataManager; private AccountAdapter mAccountAdapter; private SeafReposAdapter mReposAdapter; private DirentsAdapter mDirentsAdapter; private LoadDirTask mLoadDirTask; private LoadReposTask mLoadReposTask; private LoadAccountsTask mLoadAccountsTask; private boolean canChooseAccount; private boolean onlyShowWritableRepos; private String encryptedRepoId; private boolean isOnlyChooseRepo; private View mProgressContainer, mListContainer, mContentArea; private Button mOkButton, mCancelButton; private View mTransparentSpace; private TextView mEmptyText, mErrorText; private ListView mListView; 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; public static final String DATA_REPO_ID = "repoID"; public static final String DATA_REPO_NAME = "repoNAME"; public static final String DATA_DIRECTORY_PATH = "dirPath"; public static final String DATA_DIR = "dir"; public static final String DATA_ACCOUNT = "account"; 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"; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.seafile_path_chooser); Intent intent = getIntent(); Account 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); mOkButton = (Button) findViewById(R.id.ok); mCancelButton = (Button) findViewById(R.id.cancel); mTransparentSpace = findViewById(R.id.transparent_space); mListView = (ListView) findViewById(android.R.id.list); mEmptyText = (TextView) findViewById(android.R.id.empty); mErrorText = (TextView) findViewById(R.id.error_message); mListContainer = findViewById(R.id.listContainer); mProgressContainer = findViewById(R.id.progressContainer); mContentArea = findViewById(R.id.content); isOnlyChooseRepo = intent.getBooleanExtra(SettingsFragment.CAMERA_UPLOAD_BOTH_PAGES, false); if (isOnlyChooseRepo) { mOkButton.setVisibility(View.GONE); mTransparentSpace.setVisibility(View.GONE); } mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); mListView.setOnItemClickListener(new ListView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> l, View view, int position, long id) { onListItemClick(view, position, id); } }); mOkButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String repoName = mNavContext.getRepoName(); String repoID = mNavContext.getRepoID(); String dir = mNavContext.getDirPath(); Intent intent = new Intent(); intent.putExtra(DATA_REPO_NAME, repoName); intent.putExtra(DATA_REPO_ID, repoID); intent.putExtra(DATA_DIR, dir); intent.putExtra(DATA_ACCOUNT, mAccount); setResult(RESULT_OK, intent); finish(); } }); mCancelButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { setResult(RESULT_CANCELED); finish(); } }); if (canChooseAccount) { chooseAccount(); } else { chooseRepo(); } Toolbar toolbar = getActionBarToolbar(); toolbar.setOnMenuItemClickListener(this); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setTitle(R.string.app_name); } @Override protected void onStart() { super.onStart(); if (android.os.Build.VERSION.SDK_INT < 14 && SettingsManager.instance().isGestureLockRequired()) { Intent newIntent = new Intent(this, UnlockGesturePasswordActivity.class); startActivity(newIntent); } } @SuppressLint("LongLogTag") @Override protected void onDestroy() { Log.d(DEBUG_TAG, "onDestroy is called"); if (mLoadReposTask != null && mLoadReposTask.getStatus() != AsyncTask.Status.FINISHED) { mLoadReposTask.cancel(true); } if (mLoadDirTask != null && mLoadDirTask.getStatus() != AsyncTask.Status.FINISHED) { mLoadDirTask.cancel(true); } if (mLoadAccountsTask != null && mLoadAccountsTask.getStatus() != AsyncTask.Status.FINISHED) { mLoadAccountsTask.cancel(true); } super.onDestroy(); } 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); } else if (mStep == STEP_CHOOSE_DIR) { repo = getDataManager().getCachedRepoByID(nav.getRepoID()); } if (repo != null) { if (repo.encrypted && !mDataManager.getRepoPasswordSet(repo.id)) { String password = mDataManager.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)); chooseRepo(); break; case STEP_CHOOSE_REPO: if (!isOnlyChooseRepo) { nav.setRepoName(repo.name); nav.setRepoID(repo.id); nav.setDir("/", repo.root); chooseDir(); } else { Intent intent = new Intent(); intent.putExtra(DATA_REPO_NAME, repo.name); intent.putExtra(DATA_REPO_ID, repo.id); intent.putExtra(DATA_DIR, repo.root); intent.putExtra(DATA_ACCOUNT, mAccount); setResult(RESULT_OK, intent); finish(); } break; case STEP_CHOOSE_DIR: SeafDirent dirent = getDirentsAdapter().getItem(position); if (dirent.type == SeafDirent.DirentType.FILE) { return; } nav.setDir(Utils.pathJoin(nav.getDirPath(), dirent.name), dirent.id); refreshDir(); break; } } @Override public boolean onCreateOptionsMenu(Menu menu) { Toolbar toolbar = getActionBarToolbar(); toolbar.inflateMenu(R.menu.seafile_path_chooser_menu); toolbar.setOnMenuItemClickListener(this); return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { MenuItem menuRefresh = menu.findItem(R.id.refresh); menuRefresh.setVisible(true); menuRefresh.setEnabled(true); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == android.R.id.home) { stepBack(); return true; } return super.onOptionsItemSelected(item); } @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.refresh: refreshList(true); return true; } return super.onOptionsItemSelected(item); } @Override public void onBackPressed() { stepBack(true); } 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 && !mDataManager.getRepoPasswordSet(repo.id)) { String password = mDataManager.getRepoPassword(repo.id); showPasswordDialog(repo.name, repo.id, new TaskDialog.TaskDialogListener() { @Override public void onTaskSuccess() { chooseRepo(forceRefresh); } } , password); } chooseDir(forceRefresh); break; } } } private void stepBack() { stepBack(false); } private void stepBack(boolean cancelIfFirstStep) { switch (mStep) { case STEP_CHOOSE_ACCOUNT: if (cancelIfFirstStep) { finish(); } break; case STEP_CHOOSE_REPO: if (canChooseAccount) { chooseAccount(false); } else if (cancelIfFirstStep) { finish(); } break; case STEP_CHOOSE_DIR: if (getNavContext().isRepoRoot()) { chooseRepo(); } else { String path = getNavContext().getDirPath(); getNavContext().setDir(Utils.getParentPath(path), null); refreshDir(); } break; } } private void setListAdapter(BaseAdapter adapter) { mListView.setAdapter(adapter); } /** * List all accounts */ private void chooseAccount(boolean forwardIfOnlyOneAccount) { mStep = STEP_CHOOSE_ACCOUNT; mEmptyText.setText(R.string.no_account); mLoadAccountsTask = new LoadAccountsTask(getAccountManager(), forwardIfOnlyOneAccount); ConcurrentAsyncTask.execute(mLoadAccountsTask); setListAdapter(getAccountAdapter()); mOkButton.setVisibility(View.GONE); mTransparentSpace.setVisibility(View.GONE); // update action bar ActionBar bar = getSupportActionBar(); bar.setDisplayHomeAsUpEnabled(false); bar.setTitle(R.string.choose_an_account); } private void chooseAccount() { chooseAccount(true); } /** * List all repos */ private void chooseRepo() { chooseRepo(false); } private void chooseRepo(boolean forceRefresh) { mStep = STEP_CHOOSE_REPO; mEmptyText.setText(R.string.no_repo); setListAdapter(getReposAdapter()); mOkButton.setVisibility(View.GONE); mTransparentSpace.setVisibility(View.GONE); getNavContext().setRepoID(null); if (!Utils.isNetworkOn() || !forceRefresh) { List<SeafRepo> repos = getDataManager().getReposFromCache(); if (repos != null) { updateAdapterWithRepos(repos); // update action bar ActionBar bar = getSupportActionBar(); bar.setDisplayHomeAsUpEnabled(true); bar.setTitle(R.string.choose_a_library); return; } } showLoading(true); mLoadReposTask = new LoadReposTask(getDataManager()); ConcurrentAsyncTask.execute(mLoadReposTask); // update action bar ActionBar bar = getSupportActionBar(); bar.setDisplayHomeAsUpEnabled(true); bar.setTitle(R.string.choose_a_library); } private void chooseDir() { chooseDir(false); } private void chooseDir(boolean forceRefresh) { mStep = STEP_CHOOSE_DIR; mEmptyText.setText(R.string.dir_empty); // update action bar setListAdapter(getDirentsAdapter()); mOkButton.setVisibility(View.VISIBLE); mTransparentSpace.setVisibility(View.VISIBLE); refreshDir(forceRefresh); } private void refreshDir() { refreshDir(false); } private void updateAdapterWithDirents(List<SeafDirent> dirents) { DirentsAdapter adapter=getDirentsAdapter(); if (dirents.size() > 0) { adapter.clearDirents(); for (SeafDirent dirent : dirents) { adapter.add(dirent); } int sort_type = SettingsManager.instance().getSortFilesTypePref(); int sort_order = SettingsManager.instance().getSortFilesOrderPref(); adapter.sortFiles(sort_type, sort_order); adapter.notifyDataSetChanged(); } showListOrEmptyText(dirents.size()); } private void updateAdapterWithRepos(List<SeafRepo> repos) { SeafReposAdapter adapter=getReposAdapter(); if (repos.size() > 0) { adapter.clearRepos(); for (SeafRepo item: repos) { adapter.add(item); } int sort_type = SettingsManager.instance().getSortFilesTypePref(); int sort_order = SettingsManager.instance().getSortFilesOrderPref(); adapter.sortFiles(sort_type, sort_order); adapter.notifyDataSetChanged(); } showListOrEmptyText(repos.size()); } 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); // update action bar ActionBar bar = getSupportActionBar(); bar.setDisplayHomeAsUpEnabled(true); bar.setTitle(R.string.choose_a_folder); return; } } showLoading(true); mLoadDirTask = new LoadDirTask(repoID, dirPath, getDataManager()); ConcurrentAsyncTask.execute(mLoadDirTask); // update action bar ActionBar bar = getSupportActionBar(); bar.setDisplayHomeAsUpEnabled(true); bar.setTitle(R.string.choose_a_folder); } 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(getSupportFragmentManager(), PASSWORD_DIALOG_FRAGMENT_TAG); } private void showLoading(boolean loading) { clearError(); if (loading) { mProgressContainer.startAnimation(AnimationUtils.loadAnimation( this, android.R.anim.fade_in)); mListContainer.startAnimation(AnimationUtils.loadAnimation( this, android.R.anim.fade_out)); mProgressContainer.setVisibility(View.VISIBLE); mListContainer.setVisibility(View.INVISIBLE); } else { mProgressContainer.startAnimation(AnimationUtils.loadAnimation( this, android.R.anim.fade_out)); mListContainer.startAnimation(AnimationUtils.loadAnimation( this, android.R.anim.fade_in)); mProgressContainer.setVisibility(View.GONE); mListContainer.setVisibility(View.VISIBLE); } } 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 showListOrEmptyText(int listSize) { if (listSize == 0) { mListView.setVisibility(View.GONE); mEmptyText.setVisibility(View.VISIBLE); } else { mListView.setVisibility(View.VISIBLE); mEmptyText.setVisibility(View.GONE); } } private DataManager getDataManager() { if (mDataManager == null) { mDataManager = new DataManager(mAccount); } return mDataManager; } private AccountManager getAccountManager() { if (mAccountManager == null) { mAccountManager = new AccountManager(this); } return mAccountManager; } private NavContext getNavContext() { if (mNavContext == null) { mNavContext = new NavContext(); } return mNavContext; } private AccountAdapter getAccountAdapter() { if (mAccountAdapter == null) { mAccountAdapter = new SeafAccountAdapter(this); } return mAccountAdapter; } private SeafReposAdapter getReposAdapter() { if (mReposAdapter == null) { mReposAdapter = new SeafReposAdapter(onlyShowWritableRepos, encryptedRepoId); } return mReposAdapter; } private DirentsAdapter getDirentsAdapter() { if (mDirentsAdapter == null) { mDirentsAdapter = new DirentsAdapter(); } return mDirentsAdapter; } private void setAccount(Account account) { mAccount = account; mDataManager = new DataManager(account); } 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 doInBackground(Void... params) { try { accounts = accountManager.getSignedInAccountList(); } catch (Exception e) { err = e; } return null; } @SuppressLint("LongLogTag") @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 the next next step. setAccount(accounts.get(0)); chooseRepo(); return; } AccountAdapter 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; } @SuppressLint("LongLogTag") @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 doInBackground(Void... params) { try { dirents = dataManager.getDirentsFromServer(repoID, dirPath); } catch (SeafException e) { err = e; } return null; } @SuppressLint("LongLogTag") @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); showShortToast(SeafilePathChooserActivity.this, 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); } } }