package com.seafile.seadroid2.ui.activity; import android.Manifest; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.provider.MediaStore; import android.support.design.widget.Snackbar; import android.support.design.widget.TabLayout; import android.support.v4.app.ActivityCompat; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.app.FragmentTransaction; import android.support.v4.content.ContextCompat; import android.support.v4.content.LocalBroadcastManager; import android.support.v4.view.ViewPager; import android.support.v7.app.AlertDialog; import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.Window; import android.widget.FrameLayout; 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.SettingsManager; import com.seafile.seadroid2.account.Account; import com.seafile.seadroid2.account.AccountManager; import com.seafile.seadroid2.cameraupload.MediaObserverService; import com.seafile.seadroid2.data.DataManager; import com.seafile.seadroid2.data.DatabaseHelper; import com.seafile.seadroid2.data.SeafDirent; import com.seafile.seadroid2.data.SeafRepo; import com.seafile.seadroid2.data.SeafStarredFile; import com.seafile.seadroid2.data.ServerInfo; import com.seafile.seadroid2.data.StorageManager; import com.seafile.seadroid2.fileschooser.MultiFileChooserActivity; import com.seafile.seadroid2.monitor.FileMonitorService; import com.seafile.seadroid2.notification.DownloadNotificationProvider; import com.seafile.seadroid2.notification.UploadNotificationProvider; import com.seafile.seadroid2.transfer.DownloadTaskInfo; import com.seafile.seadroid2.transfer.DownloadTaskManager; import com.seafile.seadroid2.transfer.PendingUploadInfo; import com.seafile.seadroid2.transfer.TransferManager; import com.seafile.seadroid2.transfer.TransferService; import com.seafile.seadroid2.transfer.TransferService.TransferBinder; import com.seafile.seadroid2.transfer.UploadTaskInfo; import com.seafile.seadroid2.transfer.UploadTaskManager; import com.seafile.seadroid2.ui.CopyMoveContext; import com.seafile.seadroid2.ui.NavContext; import com.seafile.seadroid2.ui.WidgetUtils; import com.seafile.seadroid2.ui.adapter.SeafItemAdapter; import com.seafile.seadroid2.ui.dialog.AppChoiceDialog; import com.seafile.seadroid2.ui.dialog.AppChoiceDialog.CustomAction; import com.seafile.seadroid2.ui.dialog.CopyMoveDialog; import com.seafile.seadroid2.ui.dialog.DeleteFileDialog; import com.seafile.seadroid2.ui.dialog.DeleteRepoDialog; import com.seafile.seadroid2.ui.dialog.FetchFileDialog; import com.seafile.seadroid2.ui.dialog.NewDirDialog; import com.seafile.seadroid2.ui.dialog.NewFileDialog; import com.seafile.seadroid2.ui.dialog.NewRepoDialog; import com.seafile.seadroid2.ui.dialog.PasswordDialog; import com.seafile.seadroid2.ui.dialog.RenameFileDialog; import com.seafile.seadroid2.ui.dialog.RenameRepoDialog; import com.seafile.seadroid2.ui.dialog.SortFilesDialogFragment; import com.seafile.seadroid2.ui.dialog.SslConfirmDialog; import com.seafile.seadroid2.ui.dialog.TaskDialog; import com.seafile.seadroid2.ui.dialog.UploadChoiceDialog; import com.seafile.seadroid2.ui.fragment.ActivitiesFragment; import com.seafile.seadroid2.ui.fragment.ReposFragment; import com.seafile.seadroid2.ui.fragment.StarredFragment; import com.seafile.seadroid2.util.ConcurrentAsyncTask; import com.seafile.seadroid2.util.Utils; import com.seafile.seadroid2.util.UtilsJellyBean; import com.viewpagerindicator.IconPagerAdapter; import org.apache.commons.io.IOUtils; import org.json.JSONException; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; public class BrowserActivity extends BaseActivity implements ReposFragment.OnFileSelectedListener, StarredFragment.OnStarredFileSelectedListener, FragmentManager.OnBackStackChangedListener, Toolbar.OnMenuItemClickListener, SortFilesDialogFragment.SortItemClickListener { private static final String DEBUG_TAG = "BrowserActivity"; public static final String ACTIONBAR_PARENT_PATH = "/"; public static final String OPEN_FILE_DIALOG_FRAGMENT_TAG = "openfile_fragment"; public static final String PASSWORD_DIALOG_FRAGMENT_TAG = "password_fragment"; public static final String CHOOSE_APP_DIALOG_FRAGMENT_TAG = "choose_app_fragment"; public static final String CHARE_LINK_PASSWORD_FRAGMENT_TAG = "share_link_password_fragment"; public static final String PICK_FILE_DIALOG_FRAGMENT_TAG = "pick_file_fragment"; public static final int REQUEST_PERMISSIONS_WRITE_EXTERNAL_STORAGE = 1; public static final String TAG_NEW_REPO_DIALOG_FRAGMENT = "NewRepoDialogFragment"; public static final String TAG_DELETE_REPO_DIALOG_FRAGMENT = "DeleteRepoDialogFragment"; public static final String TAG_DELETE_FILE_DIALOG_FRAGMENT = "DeleteFileDialogFragment"; public static final String TAG_DELETE_FILES_DIALOG_FRAGMENT = "DeleteFilesDialogFragment"; public static final String TAG_RENAME_REPO_DIALOG_FRAGMENT = "RenameRepoDialogFragment"; public static final String TAG_RENAME_FILE_DIALOG_FRAGMENT = "RenameFileDialogFragment"; public static final String TAG_COPY_MOVE_DIALOG_FRAGMENT = "CopyMoveDialogFragment"; public static final String TAG_SORT_FILES_DIALOG_FRAGMENT = "SortFilesDialogFragment"; public static final int INDEX_LIBRARY_TAB = 0; public static final int INDEX_STARRED_TAB = 1; public static final int INDEX_ACTIVITIES_TAB = 2; private static final int[] ICONS = new int[] { R.drawable.tab_library, R.drawable.tab_starred, R.drawable.tab_activity }; private FetchFileDialog fetchFileDialog = null; private SeafileTabsAdapter adapter; private View mLayout; private FrameLayout container; private TabLayout mTabLayout; private ViewPager pager; private NavContext navContext = new NavContext(); private CopyMoveContext copyMoveContext; private Menu overFlowMenu; private MenuItem menuSearch; private DataManager dataManager = null; private TransferService txService = null; private TransferReceiver mTransferReceiver; private AccountManager accountManager; private int currentPosition = 0; private Intent copyMoveIntent; private Account account; public DataManager getDataManager() { return dataManager; } public void addUpdateTask(String repoID, String repoName, String targetDir, String localFilePath) { if (txService != null) { txService.addTaskToUploadQue(account, repoID, repoName, targetDir, localFilePath, true, true); } else { PendingUploadInfo info = new PendingUploadInfo(repoID, repoName, targetDir, localFilePath, true, true); pendingUploads.add(info); } } public void addUpdateBlocksTask(String repoID, String repoName, String targetDir, String localFilePath, int version) { if (txService != null) { txService.addTaskToUploadQue(account, repoID, repoName, targetDir, localFilePath, true, true, version); } else { PendingUploadInfo info = new PendingUploadInfo(repoID, repoName, targetDir, localFilePath, true, true, version); pendingUploads.add(info); } } private int addUploadTask(String repoID, String repoName, String targetDir, String localFilePath) { if (txService != null) { return txService.addTaskToUploadQue(account, repoID, repoName, targetDir, localFilePath, false, true); } else { PendingUploadInfo info = new PendingUploadInfo(repoID, repoName, targetDir, localFilePath, false, true); pendingUploads.add(info); return 0; } } private int addUploadBlocksTask(String repoID, String repoName, String targetDir, String localFilePath, int version) { if (txService != null) { return txService.addTaskToUploadQue(account, repoID, repoName, targetDir, localFilePath, false, true, version); } else { PendingUploadInfo info = new PendingUploadInfo(repoID, repoName, targetDir, localFilePath, false, true, version); pendingUploads.add(info); return 0; } } private ArrayList<PendingUploadInfo> pendingUploads = Lists.newArrayList(); public TransferService getTransferService() { return txService; } public Account getAccount() { return account; } public NavContext getNavContext() { return navContext; } @Override protected void onCreate(Bundle savedInstanceState) { supportRequestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); super.onCreate(savedInstanceState); accountManager = new AccountManager(this); // restart service should it have been stopped for some reason Intent mediaObserver = new Intent(this, MediaObserverService.class); startService(mediaObserver); if (!isTaskRoot()) { final Intent intent = getIntent(); final String intentAction = getIntent().getAction(); if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null && intentAction.equals(Intent.ACTION_MAIN)) { finish(); return; } } setContentView(R.layout.tabs_main); mLayout = findViewById(R.id.main_layout); container = (FrameLayout) findViewById(R.id.bottom_sheet_container); setSupportActionBar(getActionBarToolbar()); // enable ActionBar app icon to behave as action back getSupportActionBar().setDisplayHomeAsUpEnabled(true); findViewById(R.id.view_toolbar_bottom_line).setVisibility(View.GONE); // Get the message from the intent Intent intent = getIntent(); account = accountManager.getCurrentAccount(); if (account == null || !account.hasValidToken()) { finishAndStartAccountsActivity(); return; } // Log.d(DEBUG_TAG, "browser activity onCreate " + account.server + " " + account.email); dataManager = new DataManager(account); getSupportFragmentManager().addOnBackStackChangedListener(this); unsetRefreshing(); disableUpButton(); pager = (ViewPager) findViewById(R.id.pager); adapter = new SeafileTabsAdapter(getSupportFragmentManager()); pager.setAdapter(adapter); mTabLayout = (TabLayout) findViewById(R.id.sliding_tabs); mTabLayout.setupWithViewPager(pager); mTabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { @Override public void onTabSelected(TabLayout.Tab tab) { currentPosition = tab.getPosition(); supportInvalidateOptionsMenu(); pager.setCurrentItem(tab.getPosition(), true); if (currentPosition != INDEX_LIBRARY_TAB) { disableUpButton(); } else if (navContext.inRepo()) { enableUpButton(); } setUpButtonTitleOnSlideTabs(tab.getPosition()); } @Override public void onTabUnselected(TabLayout.Tab tab) { } @Override public void onTabReselected(TabLayout.Tab tab) { } }); pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { currentPosition = position; supportInvalidateOptionsMenu(); pager.setCurrentItem(position, true); if (currentPosition != INDEX_LIBRARY_TAB) { disableUpButton(); } else if (navContext.inRepo()) { enableUpButton(); } setUpButtonTitleOnSlideTabs(position); } @Override public void onPageScrollStateChanged(int state) { } }); pager.setOffscreenPageLimit(3); if (savedInstanceState != null) { Log.d(DEBUG_TAG, "savedInstanceState is not null"); fetchFileDialog = (FetchFileDialog) getSupportFragmentManager().findFragmentByTag(OPEN_FILE_DIALOG_FRAGMENT_TAG); AppChoiceDialog appChoiceDialog = (AppChoiceDialog) getSupportFragmentManager().findFragmentByTag(CHOOSE_APP_DIALOG_FRAGMENT_TAG); if (appChoiceDialog != null) { FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.detach(appChoiceDialog); ft.commit(); } SslConfirmDialog sslConfirmDlg = (SslConfirmDialog) getSupportFragmentManager().findFragmentByTag(SslConfirmDialog.FRAGMENT_TAG); if (sslConfirmDlg != null) { Log.d(DEBUG_TAG, "sslConfirmDlg is not null"); FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.detach(sslConfirmDlg); ft.commit(); } else { Log.d(DEBUG_TAG, "sslConfirmDlg is null"); } String repoID = savedInstanceState.getString("repoID"); String repoName = savedInstanceState.getString("repoName"); String path = savedInstanceState.getString("path"); String dirID = savedInstanceState.getString("dirID"); String permission = savedInstanceState.getString("permission"); if (repoID != null) { navContext.setRepoID(repoID); navContext.setRepoName(repoName); navContext.setDir(path, dirID); navContext.setDirPermission(permission); } } String repoID = intent.getStringExtra("repoID"); String repoName = intent.getStringExtra("repoName"); String path = intent.getStringExtra("path"); String dirID = intent.getStringExtra("dirID"); String permission = intent.getStringExtra("permission"); if (repoID != null) { navContext.setRepoID(repoID); navContext.setRepoName(repoName); navContext.setDir(path, dirID); navContext.setDirPermission(permission); } Intent txIntent = new Intent(this, TransferService.class); startService(txIntent); Log.d(DEBUG_TAG, "start TransferService"); // bind transfer service Intent bIntent = new Intent(this, TransferService.class); bindService(bIntent, mConnection, Context.BIND_AUTO_CREATE); Log.d(DEBUG_TAG, "try bind TransferService"); Intent monitorIntent = new Intent(this, FileMonitorService.class); startService(monitorIntent); requestServerInfo(); requestReadExternalStoragePermission(); } public FrameLayout getContainer() { return container; } private void finishAndStartAccountsActivity() { Intent newIntent = new Intent(this, AccountsActivity.class); newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); newIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); finish(); startActivity(newIntent); } private void requestServerInfo() { if(!checkServerProEdition()) { // hide Activity tab adapter.hideActivityTab(); adapter.notifyDataSetChanged(); mTabLayout.setupWithViewPager(pager); } if (!checkSearchEnabled()) { // hide search menu if (menuSearch != null) menuSearch.setVisible(false); } if (!Utils.isNetworkOn()) return; ConcurrentAsyncTask.execute(new RequestServerInfoTask()); } public void completeRemoteWipe() { ConcurrentAsyncTask.execute(new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... objects) { // clear local caches StorageManager storageManager = StorageManager.getInstance(); storageManager.clearCache(); // clear cached data from database DatabaseHelper dbHelper = DatabaseHelper.getDatabaseHelper(); dbHelper.delCaches(); try { // response to server when finished cleaning caches getDataManager().completeRemoteWipe(); } catch (SeafException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void o) { // sign out current account logoutWhenTokenExpired(); } }); } /** * Token expired, clear current authorized info and redirect user to login page */ public void logoutWhenTokenExpired() { AccountManager accountMgr = new AccountManager(this); // sign out current account Account account = accountMgr.getCurrentAccount(); accountMgr.signOutAccount(account); // then redirect to AccountsActivity Intent intent = new Intent(this, AccountsActivity.class); startActivity(intent); // finish current Activity finish(); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: if (navContext.inRepo() && currentPosition == INDEX_LIBRARY_TAB) { onBackPressed(); } return true; } return super.onOptionsItemSelected(item); } @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.sort: showSortFilesDialog(); return true; case R.id.search: Intent searchIntent = new Intent(this, SearchActivity.class); startActivity(searchIntent); return true; case R.id.create_repo: showNewRepoDialog(); return true; case R.id.add: addFile(); return true; case R.id.transfer_tasks: Intent newIntent = new Intent(this, TransferActivity.class); newIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(newIntent); return true; case R.id.accounts: newIntent = new Intent(this, AccountsActivity.class); newIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(newIntent); return true; case R.id.edit: // start action mode for selecting multiple files/folders if (!Utils.isNetworkOn()) { showShortToast(this, R.string.network_down); return true; } if (currentPosition == INDEX_LIBRARY_TAB) { if (navContext.inRepo()) { SeafRepo repo = dataManager.getCachedRepoByID(navContext.getRepoID()); if (repo.encrypted && !dataManager.getRepoPasswordSet(repo.id)) { String password = dataManager.getRepoPassword(repo.id); showPasswordDialog(repo.name, repo.id, new TaskDialog.TaskDialogListener() { @Override public void onTaskSuccess() { getReposFragment().startContextualActionMode(); } } , password); return true; } } getReposFragment().startContextualActionMode(); } return true; case R.id.settings: Intent settingsIntent = new Intent(BrowserActivity.this,SettingsActivity.class); settingsIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(settingsIntent); return true; } return super.onOptionsItemSelected(item); } /** * If the user is running Android 6.0 (API level 23) or later, the user has to grant your app its permissions while they are running the app * * Requests the WRITE_EXTERNAL_STORAGE permission. * If the permission has been denied previously, a SnackBar will prompt the user to grant the * permission, otherwise it is requested directly. * */ private void requestReadExternalStoragePermission() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { // Should we show an explanation? if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { Snackbar.make(mLayout, R.string.permission_read_exteral_storage_rationale, Snackbar.LENGTH_INDEFINITE) .setAction(R.string.settings, new View.OnClickListener() { @Override public void onClick(View view) { ActivityCompat.requestPermissions(BrowserActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_PERMISSIONS_WRITE_EXTERNAL_STORAGE); } }) .show(); } else { // No explanation needed, we can request the permission. // WRITE_EXTERNAL_STORAGE permission has not been granted yet. Request it directly. ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_PERMISSIONS_WRITE_EXTERNAL_STORAGE); } } } /** * Callback received when a permissions request has been completed. */ @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { // Log.i(DEBUG_TAG, "Received response for permission request."); switch (requestCode) { case REQUEST_PERMISSIONS_WRITE_EXTERNAL_STORAGE: { // Check if the only required permission has been granted // If request is cancelled, the result arrays are empty. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // permission was granted } else { // permission denied } } } } class RequestServerInfoTask extends AsyncTask<Void, Void, ServerInfo> { private SeafException err; @Override protected ServerInfo doInBackground(Void... params) { try { return dataManager.getServerInfo(); } catch (SeafException e) { err = e; } catch (JSONException e) { Log.e(DEBUG_TAG, "JSONException " + e.getMessage()); } return null; } @Override protected void onPostExecute(ServerInfo serverInfo) { // Check to see whether this activity is in the process of finishing // to avoid IllegalStateException when AsyncTasks continue to run after the activity has been destroyed // http://stackoverflow.com/a/35729068/3962551 if (isFinishing()) return; if (serverInfo == null) { if (err != null) showShortToast(BrowserActivity.this, err.getMessage()); return; } if (serverInfo.isProEdition()) { // show Activity tab adapter.unHideActivityTab(); adapter.notifyDataSetChanged(); mTabLayout.setupWithViewPager(pager); } if (serverInfo.isSearchEnabled()) { // show search menu if (menuSearch != null) menuSearch.setVisible(true); } accountManager.setServerInfo(account, serverInfo); } } /** * check if server is pro edition * * @return * true, if server is pro edition * false, otherwise. */ private boolean checkServerProEdition() { if (account == null) return false; ServerInfo serverInfo = accountManager.getServerInfo(account); return serverInfo.isProEdition(); } /** * check if server supports searching feature * * @return * true, if search enabled * false, otherwise. */ private boolean checkSearchEnabled() { if (account == null) return false; ServerInfo serverInfo = accountManager.getServerInfo(account); return serverInfo.isSearchEnabled(); } class SeafileTabsAdapter extends FragmentPagerAdapter implements IconPagerAdapter { public SeafileTabsAdapter(FragmentManager fm) { super(fm); } private ReposFragment reposFragment = null; private ActivitiesFragment activitieFragment = null; private StarredFragment starredFragment = null; private boolean isHideActivityTab; public void hideActivityTab() { this.isHideActivityTab = true; } public void unHideActivityTab() { this.isHideActivityTab = false; } @Override public Fragment getItem(int position) { switch (position) { case 0: if (reposFragment == null) { reposFragment = new ReposFragment(); } return reposFragment; case 1: if (starredFragment == null) { starredFragment = new StarredFragment(); } return starredFragment; case 2: if (activitieFragment == null) { activitieFragment = new ActivitiesFragment(); } return activitieFragment; default: return new Fragment(); } } @Override public CharSequence getPageTitle(int position) { switch (position) { case 0: return getString(R.string.tabs_library).toUpperCase(); case 1: return getString(R.string.tabs_starred).toUpperCase(); case 2: return getString(R.string.tabs_activity).toUpperCase(); default: return null; } } @Override public int getIconResId(int index) { return ICONS[index]; } @Override public int getCount() { if (!isHideActivityTab) return ICONS.length; else return 2; } } public int getCurrentPosition() { return currentPosition; } public void setCurrentPosition(int currentPosition) { this.currentPosition = currentPosition; pager.setCurrentItem(currentPosition); mTabLayout.setScrollPosition(currentPosition, 0, true); setUpButtonTitleOnSlideTabs(currentPosition); refreshViewOnSlideTabs(currentPosition); } public Fragment getFragment(int index) { return getSupportFragmentManager().findFragmentByTag(makeFragmentName(index)); } private String makeFragmentName(int index) { return "android:switcher:" + R.id.pager + ":" + index; } public ReposFragment getReposFragment() { return (ReposFragment) getFragment(0); } public StarredFragment getStarredFragment() { return (StarredFragment)getFragment(1); } public ActivitiesFragment getActivitiesFragment() { return (ActivitiesFragment)getFragment(2); } ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { TransferBinder binder = (TransferBinder) service; txService = binder.getService(); Log.d(DEBUG_TAG, "bind TransferService"); for (PendingUploadInfo info : pendingUploads) { if (info.enckVersion != -1) { txService.addTaskToUploadQue(account, info.repoID, info.repoName, info.targetDir, info.localFilePath, info.isUpdate, info.isCopyToLocal, info.enckVersion); } else { txService.addTaskToUploadQue(account, info.repoID, info.repoName, info.targetDir, info.localFilePath, info.isUpdate, info.isCopyToLocal); } } pendingUploads.clear(); } @Override public void onServiceDisconnected(ComponentName arg0) { txService = null; } }; @Override public void onStart() { Log.d(DEBUG_TAG, "onStart"); super.onStart(); if (android.os.Build.VERSION.SDK_INT < 14 && SettingsManager.instance().isGestureLockRequired()) { Intent intent = new Intent(this, UnlockGesturePasswordActivity.class); startActivity(intent); } if (mTransferReceiver == null) { mTransferReceiver = new TransferReceiver(); } IntentFilter filter = new IntentFilter(TransferManager.BROADCAST_ACTION); LocalBroadcastManager.getInstance(this).registerReceiver(mTransferReceiver, filter); } @Override protected void onPause() { Log.d(DEBUG_TAG, "onPause"); super.onPause(); } @Override public void onRestart() { Log.d(DEBUG_TAG, "onRestart"); super.onRestart(); if (accountManager.getCurrentAccount() == null || !accountManager.getCurrentAccount().equals(this.account) || !accountManager.getCurrentAccount().getToken().equals(this.account.getToken())) { finishAndStartAccountsActivity(); } } @Override protected void onNewIntent(Intent intent) { Log.d(DEBUG_TAG, "onNewIntent"); // if the user started the Seadroid app from the Launcher, keep the old Activity final String intentAction = intent.getAction(); if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null && intentAction.equals(Intent.ACTION_MAIN)) { return; } Account selectedAccount = accountManager.getCurrentAccount(); Log.d(DEBUG_TAG,"Current account: "+selectedAccount); if (selectedAccount == null || !account.equals(selectedAccount) || !account.getToken().equals(selectedAccount.getToken())) { Log.d(DEBUG_TAG,"Account switched, restarting activity."); finish(); startActivity(intent); } } @Override protected void onStop() { Log.d(DEBUG_TAG, "onStop"); super.onStop(); if (mTransferReceiver != null) { LocalBroadcastManager.getInstance(this).unregisterReceiver(mTransferReceiver); } } @Override protected void onDestroy() { Log.d(DEBUG_TAG, "onDestroy is called"); if (txService != null) { unbindService(mConnection); txService = null; } super.onDestroy(); } @Override public void onSaveInstanceState(Bundle outState) { Log.d(DEBUG_TAG, "onSaveInstanceState"); super.onSaveInstanceState(outState); //outState.putInt("tab", getActionBarToolbar().getSelectedNavigationIndex()); if (navContext.getRepoID() != null) { outState.putString("repoID", navContext.getRepoID()); outState.putString("repoName", navContext.getRepoName()); outState.putString("path", navContext.getDirPath()); outState.putString("dirID", navContext.getDirID()); outState.putString("permission", navContext.getDirPermission()); } } @Override public boolean onCreateOptionsMenu(Menu menu) { overFlowMenu = menu; Toolbar toolbar = getActionBarToolbar(); toolbar.inflateMenu(R.menu.browser_menu); toolbar.setOnMenuItemClickListener(this); return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { menuSearch = menu.findItem(R.id.search); MenuItem menuSort = menu.findItem(R.id.sort); MenuItem menuAdd = menu.findItem(R.id.add); MenuItem menuCreateRepo = menu.findItem(R.id.create_repo); MenuItem menuEdit = menu.findItem(R.id.edit); // Libraries Tab if (currentPosition == 0) { if (navContext.inRepo()) { menuCreateRepo.setVisible(false); menuAdd.setVisible(true); menuEdit.setVisible(true); if (hasRepoWritePermission()) { menuAdd.setEnabled(true); menuEdit.setEnabled(true); } else { menuAdd.setEnabled(false); menuEdit.setEnabled(false); } } else { menuCreateRepo.setVisible(true); menuAdd.setVisible(false); menuEdit.setVisible(false); } menuSort.setVisible(true); } else { menuSort.setVisible(false); menuCreateRepo.setVisible(false); menuAdd.setVisible(false); menuEdit.setVisible(false); } // Global menus, e.g. Accounts, TransferTasks, Settings, are visible by default. // So nothing need to be done here. // Though search menu is also a global menu, its state was maintained dynamically at runtime. if (!checkServerProEdition()) menuSearch.setVisible(false); return true; } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); } @Override protected void onPostResume() { super.onPostResume(); // We can't show the CopyMoveDialog in onActivityResult, this is a // workaround found in // http://stackoverflow.com/questions/16265733/failure-delivering-result-onactivityforresult/18345899#18345899 if (copyMoveIntent != null) { String dstRepoId, dstDir; dstRepoId = copyMoveIntent.getStringExtra(SeafilePathChooserActivity.DATA_REPO_ID); dstDir = copyMoveIntent.getStringExtra(SeafilePathChooserActivity.DATA_DIR); copyMoveContext.setDest(dstRepoId, dstDir); doCopyMove(); copyMoveIntent = null; } } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); } private void showSortFilesDialog() { SortFilesDialogFragment dialog = new SortFilesDialogFragment(); dialog.show(getSupportFragmentManager(), TAG_SORT_FILES_DIALOG_FRAGMENT); } @Override public void onSortFileItemClick(DialogFragment dialog, int position) { switch (position) { case 0: // sort by name, ascending sortFiles(SeafItemAdapter.SORT_BY_NAME, SeafItemAdapter.SORT_ORDER_ASCENDING); break; case 1: // sort by name, descending sortFiles(SeafItemAdapter.SORT_BY_NAME, SeafItemAdapter.SORT_ORDER_DESCENDING); break; case 2: // sort by last modified time, ascending sortFiles(SeafItemAdapter.SORT_BY_LAST_MODIFIED_TIME, SeafItemAdapter.SORT_ORDER_ASCENDING); break; case 3: // sort by last modified time, descending sortFiles(SeafItemAdapter.SORT_BY_LAST_MODIFIED_TIME, SeafItemAdapter.SORT_ORDER_DESCENDING); break; default: return; } } /** * Sort files by type and order * * @param type */ private void sortFiles(final int type, final int order) { if (currentPosition == INDEX_LIBRARY_TAB) { if (navContext.inRepo()) { SeafRepo repo = dataManager.getCachedRepoByID(navContext.getRepoID()); if (repo.encrypted && !dataManager.getRepoPasswordSet(repo.id)) { String password = dataManager.getRepoPassword(repo.id); showPasswordDialog(repo.name, repo.id, new TaskDialog.TaskDialogListener() { @Override public void onTaskSuccess() { getReposFragment().sortFiles(type, order); } }, password); } } getReposFragment().sortFiles(type, order); } } /** * create a new repo */ private void showNewRepoDialog() { final NewRepoDialog dialog = new NewRepoDialog(); dialog.init(account); dialog.setTaskDialogLisenter(new TaskDialog.TaskDialogListener() { @Override public void onTaskSuccess(){ showShortToast( BrowserActivity.this, String.format(getResources().getString(R.string.create_new_repo_success), dialog.getRepoName()) ); ReposFragment reposFragment = getReposFragment(); if (currentPosition == INDEX_LIBRARY_TAB && reposFragment != null) { reposFragment.refreshView(true, true); } } }); dialog.show(getSupportFragmentManager(), TAG_NEW_REPO_DIALOG_FRAGMENT); } /** * add new file/files */ private void addFile() { final AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.add_file)); builder.setItems(R.array.add_file_options_array, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (which == 0) // create file showNewFileDialog(); else if (which == 1) // create folder showNewDirDialog(); else if (which == 2) // upload file pickFile(); else if (which == 3) // take a photo CameraTakePhoto(); } }).show(); } private void showNewDirDialog() { if (!hasRepoWritePermission()) { showShortToast(this, R.string.library_read_only); return; } final NewDirDialog dialog = new NewDirDialog(); dialog.init(navContext.getRepoID(), navContext.getDirPath(), account); dialog.setTaskDialogLisenter(new TaskDialog.TaskDialogListener() { @Override public void onTaskSuccess() { final String message = String.format(getString(R.string.create_new_folder_success), dialog.getNewDirName()); showShortToast(BrowserActivity.this, message); ReposFragment reposFragment = getReposFragment(); if (currentPosition == INDEX_LIBRARY_TAB && reposFragment != null) { reposFragment.refreshView(); } } }); dialog.show(getSupportFragmentManager(), "NewDirDialogFragment"); } private void showNewFileDialog() { if (!hasRepoWritePermission()) { showShortToast(this, R.string.library_read_only); return; } final NewFileDialog dialog = new NewFileDialog(); dialog.init(navContext.getRepoID(), navContext.getDirPath(), account); dialog.setTaskDialogLisenter(new TaskDialog.TaskDialogListener() { @Override public void onTaskSuccess() { final String message = String.format(getString(R.string.create_new_file_success), dialog.getNewFileName()); showShortToast(BrowserActivity.this, message); ReposFragment reposFragment = getReposFragment(); if (currentPosition == INDEX_LIBRARY_TAB && reposFragment != null) { reposFragment.refreshView(); } } }); dialog.show(getSupportFragmentManager(), "NewFileDialogFragment"); } public void setRefreshing() { setSupportProgressBarIndeterminateVisibility(Boolean.TRUE); } public void unsetRefreshing() { setSupportProgressBarIndeterminateVisibility(Boolean.FALSE); } private File takeCameraPhotoTempFile; private void CameraTakePhoto() { Intent imageCaptureIntent = new Intent("android.media.action.IMAGE_CAPTURE"); try { File ImgDir = DataManager.createTempDir(); String fileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".jpg"; takeCameraPhotoTempFile = new File(ImgDir, fileName); Uri photo = Uri.fromFile(takeCameraPhotoTempFile); imageCaptureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photo); startActivityForResult(imageCaptureIntent, TAKE_PHOTO_REQUEST); } catch (IOException e) { showShortToast(BrowserActivity.this, R.string.unknow_error); } } public void enableUpButton() { getSupportActionBar().setDisplayHomeAsUpEnabled(true); setSupportActionBar(getActionBarToolbar()); //getActionBarToolbar().setLogo(getResources().getDrawable(R.color.transparent)); } public void disableUpButton() { getSupportActionBar().setDisplayHomeAsUpEnabled(false); //getActionBarToolbar().setEnabled(false); //getActionBarToolbar().setLogo(R.drawable.icon); } public void setUpButtonTitle(String title){ getActionBarToolbar().setTitle(title); } /** * update up button title when sliding among tabs * * @param position */ private void setUpButtonTitleOnSlideTabs(int position) { if (navContext == null) return; if (position == INDEX_LIBRARY_TAB) { if (navContext.inRepo()) { if (navContext.getDirPath().equals(BrowserActivity.ACTIONBAR_PARENT_PATH)) { setUpButtonTitle(navContext.getRepoName()); } else { setUpButtonTitle(navContext.getDirPath().substring( navContext.getDirPath().lastIndexOf(BrowserActivity.ACTIONBAR_PARENT_PATH) + 1)); } } else setUpButtonTitle(getString(R.string.tabs_library).toUpperCase()); } else { setUpButtonTitle(currentPosition == 1 ? getString(R.string.tabs_starred).toUpperCase() : getString(R.string.tabs_activity).toUpperCase()); } } /** * refresh view when sliding among tabs * * @param position */ private void refreshViewOnSlideTabs(int position) { if (navContext == null) return; if (position == INDEX_LIBRARY_TAB) { if (navContext.inRepo()) { getReposFragment().refreshView(); } } } /*********** Start other activity ***************/ public static final int PICK_FILES_REQUEST = 1; public static final int PICK_PHOTOS_VIDEOS_REQUEST = 2; public static final int PICK_FILE_REQUEST = 3; public static final int TAKE_PHOTO_REQUEST = 4; public static final int CHOOSE_COPY_MOVE_DEST_REQUEST = 5; public static final int DOWNLOAD_FILE_REQUEST = 6; public boolean hasRepoWritePermission() { if (navContext == null) { return false; } if (navContext.getDirPermission() == null || navContext.getDirPermission().indexOf('w') == -1) { return false; } return true; } void pickFile() { if (!hasRepoWritePermission()) { showShortToast(this, R.string.library_read_only); return; } // Starting with kitkat (or earlier?), the document picker has integrated image and local file support if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { UploadChoiceDialog dialog = new UploadChoiceDialog(); dialog.show(getSupportFragmentManager(), PICK_FILE_DIALOG_FRAGMENT_TAG); } else { Intent target = Utils.createGetContentIntent(); Intent intent = Intent.createChooser(target, getString(R.string.choose_file)); startActivityForResult(intent, BrowserActivity.PICK_FILE_REQUEST); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case PICK_FILES_REQUEST: if (resultCode == RESULT_OK) { String[] paths = data.getStringArrayExtra(MultiFileChooserActivity.MULTI_FILES_PATHS); if (paths == null) return; showShortToast(this, getString(R.string.added_to_upload_tasks)); List<SeafDirent> list = dataManager.getCachedDirents(navContext.getRepoID(), navContext.getDirPath()); if (list == null) return; for (String path : paths) { boolean duplicate = false; for (SeafDirent dirent : list) { if (dirent.name.equals(Utils.fileNameFromPath(path))) { duplicate = true; break; } } if (!duplicate) { showShortToast(BrowserActivity.this, getString(R.string.added_to_upload_tasks)); final SeafRepo repo = dataManager.getCachedRepoByID(navContext.getRepoID()); if (repo != null && repo.canLocalDecrypt()) { addUploadBlocksTask(navContext.getRepoID(), navContext.getRepoName(), navContext.getDirPath(), path, repo.encVersion); } else { addUploadTask(navContext.getRepoID(), navContext.getRepoName(), navContext.getDirPath(), path); } } else { showFileExistDialog(path); } } } break; case PICK_PHOTOS_VIDEOS_REQUEST: if (resultCode == RESULT_OK) { ArrayList<String> paths = data.getStringArrayListExtra("photos"); if (paths == null) return; showShortToast(this, getString(R.string.added_to_upload_tasks)); List<SeafDirent> list = dataManager.getCachedDirents(navContext.getRepoID(), navContext.getDirPath()); if (list == null) return; for (String path : paths) { boolean duplicate = false; for (SeafDirent dirent : list) { if (dirent.name.equals(Utils.fileNameFromPath(path))) { duplicate = true; break; } } if (!duplicate) { showShortToast(BrowserActivity.this, getString(R.string.added_to_upload_tasks)); final SeafRepo repo = dataManager.getCachedRepoByID(navContext.getRepoID()); if (repo != null && repo.canLocalDecrypt()) { addUploadBlocksTask(navContext.getRepoID(), navContext.getRepoName(), navContext.getDirPath(), path,repo.encVersion); }else { addUploadTask(navContext.getRepoID(), navContext.getRepoName(), navContext.getDirPath(), path); } } else { showFileExistDialog(path); } } } break; case PICK_FILE_REQUEST: if (resultCode == RESULT_OK) { if (!Utils.isNetworkOn()) { showShortToast(this, R.string.network_down); return; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { List<Uri> uriList = UtilsJellyBean.extractUriListFromIntent(data); if (uriList.size() > 0) { ConcurrentAsyncTask.execute(new SAFLoadRemoteFileTask(), uriList.toArray(new Uri[]{})); } else { showShortToast(BrowserActivity.this, R.string.saf_upload_path_not_available); } } else { Uri uri = data.getData(); if (uri != null) { ConcurrentAsyncTask.execute(new SAFLoadRemoteFileTask(), uri); } else { showShortToast(BrowserActivity.this, R.string.saf_upload_path_not_available); } } } break; case CHOOSE_COPY_MOVE_DEST_REQUEST: if (resultCode == RESULT_OK) { if (!Utils.isNetworkOn()) { showShortToast(this, R.string.network_down); return; } copyMoveIntent = data; } break; case TAKE_PHOTO_REQUEST: if (resultCode == RESULT_OK) { showShortToast(this, getString(R.string.take_photo_successfully)); if (!Utils.isNetworkOn()) { showShortToast(this, R.string.network_down); return; } if(takeCameraPhotoTempFile == null) { showShortToast(this, getString(R.string.saf_upload_path_not_available)); Log.i(DEBUG_TAG, "Pick file request did not return a path"); return; } showShortToast(this, getString(R.string.added_to_upload_tasks)); final SeafRepo repo = dataManager.getCachedRepoByID(navContext.getRepoID()); if (repo != null && repo.canLocalDecrypt()) { addUploadBlocksTask(navContext.getRepoID(), navContext.getRepoName(), navContext.getDirPath(), takeCameraPhotoTempFile.getAbsolutePath(), repo.encVersion); } else { addUploadTask(navContext.getRepoID(), navContext.getRepoName(), navContext.getDirPath(), takeCameraPhotoTempFile.getAbsolutePath()); } } break; case DOWNLOAD_FILE_REQUEST: if (resultCode == RESULT_OK) { File file = new File(data.getStringExtra("path")); WidgetUtils.showFile(BrowserActivity.this, file); } default: break; } } class SAFLoadRemoteFileTask extends AsyncTask<Uri, Void, File[]> { @Override protected File[] doInBackground(Uri... uriList) { if (uriList == null) return null; List<File> fileList = new ArrayList<File>(); for (Uri uri: uriList) { // Log.d(DEBUG_TAG, "Uploading file from uri: " + uri); InputStream in = null; OutputStream out = null; try { File tempDir = DataManager.createTempDir(); File tempFile = new File(tempDir, Utils.getFilenamefromUri(BrowserActivity.this, uri)); if (!tempFile.createNewFile()) { throw new RuntimeException("could not create temporary file"); } in = getContentResolver().openInputStream(uri); out = new FileOutputStream(tempFile); IOUtils.copy(in, out); fileList.add(tempFile); } catch (IOException e) { Log.d(DEBUG_TAG, "Could not open requested document", e); } catch (RuntimeException e) { Log.d(DEBUG_TAG, "Could not open requested document", e); } finally { IOUtils.closeQuietly(in); IOUtils.closeQuietly(out); } } return fileList.toArray(new File[]{}); } @Override protected void onPostExecute(File... fileList) { if (fileList == null) return; List<SeafDirent> list = dataManager.getCachedDirents(navContext.getRepoID(), navContext.getDirPath()); for (final File file: fileList) { if (file == null) { showShortToast(BrowserActivity.this, R.string.saf_upload_path_not_available); } else { if (list == null) { Log.e(DEBUG_TAG, "Seadroid dirent cache is empty in uploadFile. Should not happen, aborting."); return; } boolean duplicate = false; for (SeafDirent dirent : list) { if (dirent.name.equals(file.getName())) { duplicate = true; break; } } if (!duplicate) { final SeafRepo repo = dataManager.getCachedRepoByID(navContext.getRepoID()); showShortToast(BrowserActivity.this, getString(R.string.added_to_upload_tasks)); if (repo != null && repo.canLocalDecrypt()) { addUploadBlocksTask(repo.id, repo.name, navContext.getDirPath(), file.getAbsolutePath(), repo.encVersion); } else { addUploadTask(navContext.getRepoID(), navContext.getRepoName(), navContext.getDirPath(), file.getAbsolutePath()); } } else { showFileExistDialog(file); } } } if (txService == null) return; if (!txService.hasUploadNotifProvider()) { UploadNotificationProvider provider = new UploadNotificationProvider( txService.getUploadTaskManager(), txService); txService.saveUploadNotifProvider(provider); } } } private void showFileExistDialog(final String filePath){ showFileExistDialog(new File(filePath)); } private void showFileExistDialog(final File file) { final SeafRepo repo = dataManager.getCachedRepoByID(navContext.getRepoID()); AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.upload_file_exist)); builder.setMessage(String.format(getString(R.string.upload_duplicate_found), file.getName())); builder.setPositiveButton(getString(R.string.upload_replace), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { showShortToast(BrowserActivity.this, getString(R.string.added_to_upload_tasks)); if (repo != null && repo.canLocalDecrypt()) { addUpdateBlocksTask(repo.id, repo.name, navContext.getDirPath(), file.getAbsolutePath(), repo.encVersion); } else { addUpdateTask(navContext.getRepoID(), navContext.getRepoName(), navContext.getDirPath(), file.getAbsolutePath()); } } }); builder.setNeutralButton(getString(R.string.cancel), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); builder.setNegativeButton(getString(R.string.upload_keep_both), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (repo != null && repo.canLocalDecrypt()) { addUploadBlocksTask(repo.id, repo.name, navContext.getDirPath(), file.getAbsolutePath(), repo.encVersion); } else { addUploadTask(navContext.getRepoID(), navContext.getRepoName(), navContext.getDirPath(), file.getAbsolutePath()); } } }); builder.show(); } public void onItemSelected() { // update contextual action bar (CAB) title getReposFragment().updateContextualActionBar(); } /*************** Navigation *************/ @Override public void onFileSelected(SeafDirent dirent) { final String fileName= dirent.name; final long fileSize = dirent.size; final String repoName = navContext.getRepoName(); final String repoID = navContext.getRepoID(); final String dirPath = navContext.getDirPath(); final String filePath = Utils.pathJoin(navContext.getDirPath(), fileName); final SeafRepo repo = dataManager.getCachedRepoByID(repoID); // Encrypted repo doesn\`t support gallery, // because pic thumbnail under encrypted repo was not supported at the server side if (Utils.isViewableImage(fileName) && repo != null && !repo.encrypted) { WidgetUtils.startGalleryActivity(this, repoName, repoID, dirPath, fileName, account); return; } final File localFile = dataManager.getLocalCachedFile(repoName, repoID, filePath, dirent.id); if (localFile != null) { WidgetUtils.showFile(this, localFile); return; } startFileActivity(repoName, repoID, filePath, fileSize); } /** * Download a file * * @param dir * @param fileName */ public void downloadFile(String dir, String fileName) { String filePath = Utils.pathJoin(dir, fileName); txService.addDownloadTask(account, navContext.getRepoName(), navContext.getRepoID(), filePath); if (!txService.hasDownloadNotifProvider()) { DownloadNotificationProvider provider = new DownloadNotificationProvider(txService.getDownloadTaskManager(), txService); txService.saveDownloadNotifProvider(provider); } SeafItemAdapter adapter = getReposFragment().getAdapter(); List<DownloadTaskInfo> infos = txService.getDownloadTaskInfosByPath(navContext.getRepoID(), dir); // update downloading progress adapter.setDownloadTaskList(infos); } /** * Download all files (folders) under a given folder * * @param dirPath * @param fileName name of the download folder * @param recurse */ public void downloadDir(String dirPath, String fileName, boolean recurse) { if (!Utils.isNetworkOn()) { showShortToast(this, R.string.network_down); return; } ConcurrentAsyncTask.execute(new DownloadDirTask(), navContext.getRepoName(), navContext.getRepoID(), dirPath, String.valueOf(recurse), fileName); } private class DownloadDirTask extends AsyncTask<String, Void, List<SeafDirent> > { private String repoName; private String repoID; private String fileName; private String dirPath; private int fileCount; private boolean recurse; private ArrayList<String> dirPaths = Lists.newArrayList(); private SeafException err = null; @Override protected List<SeafDirent> doInBackground(String... params) { if (params.length != 5) { Log.d(DEBUG_TAG, "Wrong params to LoadDirTask"); return null; } repoName = params[0]; repoID = params[1]; dirPath = params[2]; recurse = Boolean.valueOf(params[3]); fileName = params[4]; ArrayList<SeafDirent> dirents = Lists.newArrayList(); dirPaths.add(Utils.pathJoin(dirPath, fileName)); // don`t use for each loop here for (int i = 0; i < dirPaths.size(); i++) { List<SeafDirent> currentDirents; try { currentDirents = dataManager.getDirentsFromServer(repoID, dirPaths.get(i)); } catch (SeafException e) { err = e; e.printStackTrace(); return null; } if (currentDirents == null) continue; for (SeafDirent seafDirent : currentDirents) { if (seafDirent.isDir()) { if (recurse) { dirPaths.add(Utils.pathJoin(dirPaths.get(i), seafDirent.name)); } } else { File localCachedFile = dataManager.getLocalCachedFile(repoName, repoID, Utils.pathJoin(dirPaths.get(i), seafDirent.name), seafDirent.id); if (localCachedFile != null) { continue; } // txService maybe null if layout orientation has changed // e.g. landscape and portrait switch if (txService == null) return null; txService.addTaskToDownloadQue(account, repoName, repoID, Utils.pathJoin(dirPaths.get(i), seafDirent.name)); fileCount++; } } } return dirents; } @Override protected void onPostExecute(List<SeafDirent> dirents) { if (dirents == null) { if (err != null) { showShortToast(BrowserActivity.this, R.string.transfer_list_network_error); } return; } if (fileCount == 0) showShortToast(BrowserActivity.this, R.string.transfer_download_no_task); else { showShortToast(BrowserActivity.this, getResources().getQuantityString(R.plurals.transfer_download_started, fileCount, fileCount)); if (!txService.hasDownloadNotifProvider()) { DownloadNotificationProvider provider = new DownloadNotificationProvider(txService.getDownloadTaskManager(), txService); txService.saveDownloadNotifProvider(provider); } } // set download tasks info to adapter in order to update download progress in UI thread getReposFragment().getAdapter().setDownloadTaskList(txService.getDownloadTaskInfosByPath(repoID, dirPath)); } } private void startFileActivity(String repoName, String repoID, String filePath, long fileSize) { int taskID = txService.addDownloadTask(account, repoName, repoID, filePath, fileSize); Intent intent = new Intent(this, FileActivity.class); intent.putExtra("repoName", repoName); intent.putExtra("repoID", repoID); intent.putExtra("filePath", filePath); intent.putExtra("account", account); intent.putExtra("taskID", taskID); startActivityForResult(intent, DOWNLOAD_FILE_REQUEST); } @Override public void onStarredFileSelected(final SeafStarredFile starredFile) { final long fileSize = starredFile.getSize(); final String repoID = starredFile.getRepoID(); final SeafRepo repo = dataManager.getCachedRepoByID(repoID); if (repo == null) return; if (repo.encrypted && !dataManager.getRepoPasswordSet(repo.id)) { String password = dataManager.getRepoPassword(repo.id); showPasswordDialog(repo.name, repo.id, new TaskDialog.TaskDialogListener() { @Override public void onTaskSuccess() { onStarredFileSelected(starredFile); } }, password); return; } final String repoName = repo.getName(); final String filePath = starredFile.getPath(); final String dirPath = Utils.getParentPath(filePath); // Encrypted repo doesn\`t support gallery, // because pic thumbnail under encrypted repo was not supported at the server side if (Utils.isViewableImage(starredFile.getTitle()) && !repo.encrypted) { WidgetUtils.startGalleryActivity(this, repoName, repoID, dirPath, starredFile.getTitle(), account); return; } final File localFile = dataManager.getLocalCachedFile(repoName, repoID, filePath, null); if (localFile != null) { WidgetUtils.showFile(this, localFile); return; } startFileActivity(repoName, repoID, filePath, fileSize); } @Override public void onBackPressed() { if (getSupportFragmentManager().getBackStackEntryCount() != 0) { getSupportFragmentManager().popBackStack(); return; } if (currentPosition == INDEX_LIBRARY_TAB) { if (navContext.inRepo()) { if (navContext.isRepoRoot()) { navContext.setRepoID(null); getActionBarToolbar().setTitle(R.string.app_name); } else { String parentPath = Utils.getParentPath(navContext .getDirPath()); navContext.setDir(parentPath, null); if (parentPath.equals(ACTIONBAR_PARENT_PATH)) { getActionBarToolbar().setTitle(navContext.getRepoName()); }else { getActionBarToolbar().setTitle(parentPath.substring(parentPath.lastIndexOf(ACTIONBAR_PARENT_PATH) + 1)); } } getReposFragment().refreshView(true); } else super.onBackPressed(); } else if (currentPosition == INDEX_ACTIVITIES_TAB) { if (getActivitiesFragment().isBottomSheetShown()) { getActivitiesFragment().hideBottomSheet(); } else super.onBackPressed(); } else super.onBackPressed(); } @Override public void onBackStackChanged() { } /************ Files ************/ /** * Export a file. * 1. first ask the user to choose an app * 2. then download the latest version of the file * 3. start the choosen app * * @param fileName The name of the file to share in the current navcontext */ public void exportFile(String fileName, long fileSize) { String repoName = navContext.getRepoName(); String repoID = navContext.getRepoID(); String dirPath = navContext.getDirPath(); String fullPath = Utils.pathJoin(dirPath, fileName); chooseExportApp(repoName, repoID, fullPath, fileSize); } private void chooseExportApp(final String repoName, final String repoID, final String path, final long fileSize) { final File file = dataManager.getLocalRepoFile(repoName, repoID, path); Uri uri = Uri.fromFile(file); final Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); sendIntent.setType(Utils.getFileMimeType(file)); sendIntent.putExtra(Intent.EXTRA_STREAM, uri); // Get a list of apps List<ResolveInfo> infos = Utils.getAppsByIntent(sendIntent); if (infos.isEmpty()) { showShortToast(this, R.string.no_app_available); return; } AppChoiceDialog dialog = new AppChoiceDialog(); dialog.init(getString(R.string.export_file), infos, new AppChoiceDialog.OnItemSelectedListener() { @Override public void onCustomActionSelected(CustomAction action) { } @Override public void onAppSelected(ResolveInfo appInfo) { String className = appInfo.activityInfo.name; String packageName = appInfo.activityInfo.packageName; sendIntent.setClassName(packageName, className); if (!Utils.isNetworkOn() && file.exists()) { startActivity(sendIntent); return; } fetchFileAndExport(appInfo, sendIntent, repoName, repoID, path, fileSize); } }); dialog.show(getSupportFragmentManager(), CHOOSE_APP_DIALOG_FRAGMENT_TAG); } private void fetchFileAndExport(final ResolveInfo appInfo, final Intent intent, final String repoName, final String repoID, final String path, final long fileSize) { fetchFileDialog = new FetchFileDialog(); fetchFileDialog.init(repoName, repoID, path, fileSize, new FetchFileDialog.FetchFileListener() { @Override public void onSuccess() { startActivity(intent); } @Override public void onDismiss() { fetchFileDialog = null; } @Override public void onFailure(SeafException err) { } }); fetchFileDialog.show(getSupportFragmentManager(), OPEN_FILE_DIALOG_FRAGMENT_TAG); } public void renameRepo(String repoID, String repoName) { final RenameRepoDialog dialog = new RenameRepoDialog(); dialog.init(repoID, repoName, account); dialog.setTaskDialogLisenter(new TaskDialog.TaskDialogListener() { @Override public void onTaskSuccess() { showShortToast(BrowserActivity.this, R.string.rename_successful); ReposFragment reposFragment = getReposFragment(); if (currentPosition == INDEX_LIBRARY_TAB && reposFragment != null) { reposFragment.refreshView(true, true); } } }); dialog.show(getSupportFragmentManager(), TAG_RENAME_REPO_DIALOG_FRAGMENT); } public void deleteRepo(String repoID) { final DeleteRepoDialog dialog = new DeleteRepoDialog(); dialog.init(repoID, account); dialog.setTaskDialogLisenter(new TaskDialog.TaskDialogListener() { @Override public void onTaskSuccess() { showShortToast(BrowserActivity.this, R.string.delete_successful); ReposFragment reposFragment = getReposFragment(); if (currentPosition == INDEX_LIBRARY_TAB && reposFragment != null) { reposFragment.refreshView(true, true); } } }); dialog.show(getSupportFragmentManager(), TAG_DELETE_REPO_DIALOG_FRAGMENT); } /** * Share a file. Generating a file share link and send the link to someone * through some app. * @param repoID * @param path */ public void shareFile(final String repoID, final String path, boolean isEncrypt) { if (isEncrypt) { WidgetUtils.inputSharePassword(this, repoID, path, false, account); } else { WidgetUtils.chooseShareApp(this, repoID, path, false, account, null, null); } } public void shareDir(String repoID, String path, boolean isEncrypt) { if (isEncrypt) { WidgetUtils.inputSharePassword(this, repoID, path, true, account); } else { WidgetUtils.chooseShareApp(this, repoID, path, true, account, null, null); } } public void renameFile(String repoID, String repoName, String path) { doRename(repoID, repoName, path, false); } public void renameDir(String repoID, String repoName, String path) { doRename(repoID, repoName, path, true); } private void doRename(String repoID, String repoName, String path, boolean isdir) { final RenameFileDialog dialog = new RenameFileDialog(); dialog.init(repoID, path, isdir, account); dialog.setTaskDialogLisenter(new TaskDialog.TaskDialogListener() { @Override public void onTaskSuccess() { showShortToast(BrowserActivity.this, R.string.rename_successful); ReposFragment reposFragment = getReposFragment(); if (currentPosition == INDEX_LIBRARY_TAB && reposFragment != null) { reposFragment.refreshView(); } } }); dialog.show(getSupportFragmentManager(), TAG_RENAME_FILE_DIALOG_FRAGMENT); } public void deleteFile(String repoID, String repoName, String path) { doDelete(repoID, repoName, path, false); } public void deleteDir(String repoID, String repoName, String path) { doDelete(repoID, repoName, path, true); } private void doDelete(String repoID, String repoName, String path, boolean isdir) { final DeleteFileDialog dialog = new DeleteFileDialog(); dialog.init(repoID, path, isdir, account); dialog.setTaskDialogLisenter(new TaskDialog.TaskDialogListener() { @Override public void onTaskSuccess() { showShortToast(BrowserActivity.this, R.string.delete_successful); ReposFragment reposFragment = getReposFragment(); if (currentPosition == INDEX_LIBRARY_TAB && reposFragment != null) { reposFragment.refreshView(); } } }); dialog.show(getSupportFragmentManager(), TAG_DELETE_FILE_DIALOG_FRAGMENT); } public void copyFile(String srcRepoId, String srcRepoName, String srcDir, String srcFn, boolean isdir) { chooseCopyMoveDest(srcRepoId, srcRepoName, srcDir, srcFn, isdir, CopyMoveContext.OP.COPY); } public void moveFile(String srcRepoId, String srcRepoName, String srcDir, String srcFn, boolean isdir) { chooseCopyMoveDest(srcRepoId, srcRepoName, srcDir, srcFn, isdir, CopyMoveContext.OP.MOVE); } public void starFile(String srcRepoId, String srcDir, String srcFn) { getStarredFragment().doStarFile(srcRepoId, srcDir, srcFn); } private void chooseCopyMoveDest(String repoID, String repoName, String path, String filename, boolean isdir, CopyMoveContext.OP op) { copyMoveContext = new CopyMoveContext(repoID, repoName, path, filename, isdir, op); Intent intent = new Intent(this, SeafilePathChooserActivity.class); intent.putExtra(SeafilePathChooserActivity.DATA_ACCOUNT, account); SeafRepo repo = dataManager.getCachedRepoByID(repoID); if (repo.encrypted) { intent.putExtra(SeafilePathChooserActivity.ENCRYPTED_REPO_ID, repoID); } startActivityForResult(intent, CHOOSE_COPY_MOVE_DEST_REQUEST); return; } private void doCopyMove() { if (!copyMoveContext.checkCopyMoveToSubfolder()) { showShortToast(this, copyMoveContext.isCopy() ? R.string.cannot_copy_folder_to_subfolder : R.string.cannot_move_folder_to_subfolder); return; } final CopyMoveDialog dialog = new CopyMoveDialog(); dialog.init(account, copyMoveContext); dialog.setCancelable(false); dialog.setTaskDialogLisenter(new TaskDialog.TaskDialogListener() { @Override public void onTaskSuccess() { showShortToast(BrowserActivity.this, copyMoveContext.isCopy() ? R.string.copied_successfully : R.string.moved_successfully); if (copyMoveContext.batch) { List<SeafDirent> cachedDirents = getDataManager().getCachedDirents(getNavContext().getRepoID(), getNavContext().getDirPath()); // refresh view if (getReposFragment().getAdapter() != null) { getReposFragment().getAdapter().setItems(cachedDirents); getReposFragment().getAdapter().notifyDataSetChanged(); } if (cachedDirents.size() == 0) getReposFragment().getEmptyView().setVisibility(View.VISIBLE); return; } if (copyMoveContext.isMove()) { ReposFragment reposFragment = getReposFragment(); if (currentPosition == INDEX_LIBRARY_TAB && reposFragment != null) { reposFragment.refreshView(); } } } }); dialog.show(getSupportFragmentManager(), TAG_COPY_MOVE_DIALOG_FRAGMENT); } private void onFileDownloadFailed(int taskID) { if (txService == null) { return; } DownloadTaskInfo info = txService.getDownloadTaskInfo(taskID); if (info == null) return; final SeafException err = info.err; final String repoName = info.repoName; final String repoID = info.repoID; final String path = info.pathInRepo; if (err != null && err.getCode() == SeafConnection.HTTP_STATUS_REPO_PASSWORD_REQUIRED) { if (currentPosition == INDEX_LIBRARY_TAB && repoID.equals(navContext.getRepoID()) && Utils.getParentPath(path).equals(navContext.getDirPath())) { showPasswordDialog(repoName, repoID, new TaskDialog.TaskDialogListener() { @Override public void onTaskSuccess() { txService.addDownloadTask(account, repoName, repoID, path); } }); return; } } showShortToast(this, getString(R.string.download_failed)); } private void onFileUploaded(int taskID) { if (txService == null) { return; } UploadTaskInfo info = txService.getUploadTaskInfo(taskID); if (info == null) { return; } String repoID = info.repoID; String dir = info.parentDir; if (currentPosition == INDEX_LIBRARY_TAB && repoID.equals(navContext.getRepoID()) && dir.equals(navContext.getDirPath())) { getReposFragment().refreshView(true, true); String verb = getString(info.isUpdate ? R.string.updated : R.string.uploaded); showShortToast(this, verb + " " + Utils.fileNameFromPath(info.localFilePath)); } } private int intShowErrorTime; private void onFileUploadFailed(int taskID) { if (++ intShowErrorTime <= 1) showShortToast(this, getString(R.string.upload_failed)); } public PasswordDialog showPasswordDialog(String repoName, String repoID, TaskDialog.TaskDialogListener listener) { return showPasswordDialog(repoName, repoID, listener, null); } public PasswordDialog showPasswordDialog(String repoName, String repoID, TaskDialog.TaskDialogListener listener, String password) { PasswordDialog passwordDialog = new PasswordDialog(); passwordDialog.setRepo(repoName, repoID, account); if (password != null) { passwordDialog.setPassword(password); } passwordDialog.setTaskDialogLisenter(listener); passwordDialog.show(getSupportFragmentManager(), PASSWORD_DIALOG_FRAGMENT_TAG); return passwordDialog; } /************ Multiple Files ************/ /** * Delete multiple fiels * * @param repoID * @param path * @param dirents */ public void deleteFiles(final String repoID, String path, List<SeafDirent> dirents) { final DeleteFileDialog dialog = new DeleteFileDialog(); dialog.init(repoID, path, dirents, account); dialog.setCancelable(false); dialog.setTaskDialogLisenter(new TaskDialog.TaskDialogListener() { @Override public void onTaskSuccess() { showShortToast(BrowserActivity.this, R.string.delete_successful); if (getDataManager() != null) { List<SeafDirent> cachedDirents = getDataManager().getCachedDirents(repoID, getNavContext().getDirPath()); getReposFragment().getAdapter().setItems(cachedDirents); getReposFragment().getAdapter().notifyDataSetChanged(); // update contextual action bar (CAB) title getReposFragment().updateContextualActionBar(); if (cachedDirents.size() == 0) getReposFragment().getEmptyView().setVisibility(View.VISIBLE); } } }); dialog.show(getSupportFragmentManager(), TAG_DELETE_FILES_DIALOG_FRAGMENT); } /** * Copy multiple files * * @param srcRepoId * @param srcRepoName * @param srcDir * @param dirents */ public void copyFiles(String srcRepoId, String srcRepoName, String srcDir, List<SeafDirent> dirents) { chooseCopyMoveDestForMultiFiles(srcRepoId, srcRepoName, srcDir, dirents, CopyMoveContext.OP.COPY); } /** * Move multiple files * * @param srcRepoId * @param srcRepoName * @param srcDir * @param dirents */ public void moveFiles(String srcRepoId, String srcRepoName, String srcDir, List<SeafDirent> dirents) { chooseCopyMoveDestForMultiFiles(srcRepoId, srcRepoName, srcDir, dirents, CopyMoveContext.OP.MOVE); } /** * Choose copy/move destination for multiple files * * @param repoID * @param repoName * @param dirPath * @param dirents * @param op */ private void chooseCopyMoveDestForMultiFiles(String repoID, String repoName, String dirPath, List<SeafDirent> dirents, CopyMoveContext.OP op) { copyMoveContext = new CopyMoveContext(repoID, repoName, dirPath, dirents, op); Intent intent = new Intent(this, SeafilePathChooserActivity.class); intent.putExtra(SeafilePathChooserActivity.DATA_ACCOUNT, account); SeafRepo repo = getDataManager().getCachedRepoByID(repoID); if (repo.encrypted) { intent.putExtra(SeafilePathChooserActivity.ENCRYPTED_REPO_ID, repoID); } startActivityForResult(intent, BrowserActivity.CHOOSE_COPY_MOVE_DEST_REQUEST); } /** * Add selected files (folders) to downloading queue, * folders with subfolder will be downloaded recursively. * * @param repoID * @param repoName * @param dirPath * @param dirents */ public void downloadFiles(String repoID, String repoName, String dirPath, List<SeafDirent> dirents) { if (!Utils.isNetworkOn()) { showShortToast(this, R.string.network_down); return; } DownloadFilesTask task = new DownloadFilesTask(repoID, repoName, dirPath, dirents); ConcurrentAsyncTask.execute(task); } /** * Task for asynchronously downloading selected files (folders), * files wont be added to downloading queue if they have already been cached locally. */ class DownloadFilesTask extends AsyncTask<Void, Void, Void> { private String repoID, repoName, dirPath; private List<SeafDirent> dirents; private SeafException err; private int fileCount; public DownloadFilesTask(String repoID, String repoName, String dirPath, List<SeafDirent> dirents) { this.repoID = repoID; this.repoName = repoName; this.dirPath = dirPath; this.dirents = dirents; } @Override protected void onPreExecute() { getReposFragment().showLoading(true); } @Override protected Void doInBackground(Void... params) { ArrayList<String> dirPaths = Lists.newArrayList(dirPath); for (int i = 0; i < dirPaths.size(); i++) { if (i > 0) { try { dirents = getDataManager().getDirentsFromServer(repoID, dirPaths.get(i)); } catch (SeafException e) { err = e; Log.e(DEBUG_TAG, e.getMessage() + e.getCode()); } } if (dirents == null) continue; for (SeafDirent seafDirent : dirents) { if (seafDirent.isDir()) { // download files recursively dirPaths.add(Utils.pathJoin(dirPaths.get(i), seafDirent.name)); } else { File localCachedFile = getDataManager().getLocalCachedFile(repoName, repoID, Utils.pathJoin(dirPaths.get(i), seafDirent.name), seafDirent.id); if (localCachedFile != null) { continue; } // txService maybe null if layout orientation has changed // e.g. landscape and portrait switch if (txService == null) return null; txService.addTaskToDownloadQue(account, repoName, repoID, Utils.pathJoin(dirPaths.get(i), seafDirent.name)); fileCount++; } } } return null; } @Override protected void onPostExecute(Void aVoid) { // update ui getReposFragment().showLoading(false); if (err != null) { showShortToast(BrowserActivity.this, R.string.transfer_list_network_error); return; } if (fileCount == 0) showShortToast(BrowserActivity.this, R.string.transfer_download_no_task); else { showShortToast(BrowserActivity.this, getResources().getQuantityString(R.plurals.transfer_download_started, fileCount, fileCount)); if (!txService.hasDownloadNotifProvider()) { DownloadNotificationProvider provider = new DownloadNotificationProvider(txService.getDownloadTaskManager(), txService); txService.saveDownloadNotifProvider(provider); } } } } @Override public boolean onKeyUp(int keycode, KeyEvent e) { switch(keycode) { case KeyEvent.KEYCODE_MENU: if (overFlowMenu !=null) { overFlowMenu.performIdentifierAction(R.id.menu_overflow, 0); } } return super.onKeyUp(keycode, e); } // for receive broadcast from TransferService private class TransferReceiver extends BroadcastReceiver { private TransferReceiver() {} public void onReceive(Context context, Intent intent) { String type = intent.getStringExtra("type"); if (type.equals(DownloadTaskManager.BROADCAST_FILE_DOWNLOAD_FAILED)) { int taskID = intent.getIntExtra("taskID", 0); onFileDownloadFailed(taskID); } else if (type.equals(UploadTaskManager.BROADCAST_FILE_UPLOAD_SUCCESS)) { int taskID = intent.getIntExtra("taskID", 0); onFileUploaded(taskID); } else if (type.equals(UploadTaskManager.BROADCAST_FILE_UPLOAD_FAILED)) { int taskID = intent.getIntExtra("taskID", 0); onFileUploadFailed(taskID); } } } // TransferReceiver public void showRepoBottomSheet(SeafRepo repo) { getReposFragment().showRepoBottomSheet(repo); } public void showFileBottomSheet(String title, final SeafDirent dirent) { getReposFragment().showFileBottomSheet(title, dirent); } public void showDirBottomSheet(String title, final SeafDirent dirent) { getReposFragment().showDirBottomSheet(title, dirent); } }