FileDisplayActivity.java 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897
  1. /* ownCloud Android client application
  2. * Copyright (C) 2011 Bartek Przybylski
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. *
  17. */
  18. package com.owncloud.android.ui.activity;
  19. import java.io.File;
  20. import java.util.ArrayList;
  21. import android.accounts.Account;
  22. import android.accounts.AccountManager;
  23. import android.app.AlertDialog;
  24. import android.app.ProgressDialog;
  25. import android.app.AlertDialog.Builder;
  26. import android.app.Dialog;
  27. import android.content.BroadcastReceiver;
  28. import android.content.ContentResolver;
  29. import android.content.Context;
  30. import android.content.DialogInterface;
  31. import android.content.DialogInterface.OnClickListener;
  32. import android.content.Intent;
  33. import android.content.IntentFilter;
  34. import android.content.SharedPreferences;
  35. import android.content.pm.PackageInfo;
  36. import android.content.pm.PackageManager.NameNotFoundException;
  37. import android.content.res.Resources.NotFoundException;
  38. import android.database.Cursor;
  39. import android.net.Uri;
  40. import android.os.Bundle;
  41. import android.os.Handler;
  42. import android.preference.PreferenceManager;
  43. import android.provider.MediaStore;
  44. import android.support.v4.app.FragmentTransaction;
  45. import android.util.Log;
  46. import android.view.View;
  47. import android.view.ViewGroup;
  48. import android.widget.ArrayAdapter;
  49. import android.widget.EditText;
  50. import android.widget.TextView;
  51. import android.widget.Toast;
  52. import com.actionbarsherlock.app.ActionBar;
  53. import com.actionbarsherlock.app.ActionBar.OnNavigationListener;
  54. import com.actionbarsherlock.app.SherlockFragmentActivity;
  55. import com.actionbarsherlock.view.Menu;
  56. import com.actionbarsherlock.view.MenuInflater;
  57. import com.actionbarsherlock.view.MenuItem;
  58. import com.actionbarsherlock.view.Window;
  59. import com.owncloud.android.AccountUtils;
  60. import com.owncloud.android.CrashHandler;
  61. import com.owncloud.android.authenticator.AccountAuthenticator;
  62. import com.owncloud.android.datamodel.DataStorageManager;
  63. import com.owncloud.android.datamodel.FileDataStorageManager;
  64. import com.owncloud.android.datamodel.OCFile;
  65. import com.owncloud.android.files.services.FileDownloader;
  66. import com.owncloud.android.files.services.FileUploader;
  67. import com.owncloud.android.syncadapter.FileSyncService;
  68. import com.owncloud.android.ui.fragment.FileDetailFragment;
  69. import com.owncloud.android.ui.fragment.FileListFragment;
  70. import com.owncloud.android.R;
  71. import eu.alefzero.webdav.WebdavClient;
  72. /**
  73. * Displays, what files the user has available in his ownCloud.
  74. *
  75. * @author Bartek Przybylski
  76. *
  77. */
  78. public class FileDisplayActivity extends SherlockFragmentActivity implements
  79. FileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNavigationListener, OnClickListener, android.view.View.OnClickListener {
  80. private ArrayAdapter<String> mDirectories;
  81. private OCFile mCurrentDir;
  82. private String[] mDirs = null;
  83. private DataStorageManager mStorageManager;
  84. private SyncBroadcastReceiver mSyncBroadcastReceiver;
  85. private UploadFinishReceiver mUploadFinishReceiver;
  86. private DownloadFinishReceiver mDownloadFinishReceiver;
  87. private View mLayoutView = null;
  88. private FileListFragment mFileList;
  89. private boolean mDualPane;
  90. private boolean mForcedLoginToCreateFirstAccount = false;
  91. private static final String KEY_DIR_ARRAY = "DIR_ARRAY";
  92. private static final String KEY_CURRENT_DIR = "DIR";
  93. private static final int DIALOG_SETUP_ACCOUNT = 0;
  94. private static final int DIALOG_CREATE_DIR = 1;
  95. private static final int DIALOG_ABOUT_APP = 2;
  96. public static final int DIALOG_SHORT_WAIT = 3;
  97. private static final int ACTION_SELECT_FILE = 1;
  98. private static final String TAG = "FileDisplayActivity";
  99. @Override
  100. public void onCreate(Bundle savedInstanceState) {
  101. Log.i(getClass().toString(), "onCreate() start");
  102. super.onCreate(savedInstanceState);
  103. requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
  104. Thread.setDefaultUncaughtExceptionHandler(new CrashHandler(getApplicationContext()));
  105. if(savedInstanceState != null) {
  106. mDirs = savedInstanceState.getStringArray(KEY_DIR_ARRAY);
  107. mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
  108. mDirectories.add(OCFile.PATH_SEPARATOR);
  109. if (mDirs != null)
  110. for (String s : mDirs)
  111. mDirectories.insert(s, 0);
  112. mCurrentDir = savedInstanceState.getParcelable(FileDetailFragment.EXTRA_FILE);
  113. }
  114. mLayoutView = getLayoutInflater().inflate(R.layout.files, null); // always inflate this at onCreate() ; just once!
  115. if (AccountUtils.accountsAreSetup(this)) {
  116. initDelayedTilAccountAvailabe();
  117. // PIN CODE request ; best location is to decide, let's try this first
  118. //if (savedInstanceState == null) {
  119. if (getIntent().getAction() != null && getIntent().getAction().equals(Intent.ACTION_MAIN) && savedInstanceState == null) {
  120. requestPinCode();
  121. }
  122. } else {
  123. setContentView(R.layout.no_account_available);
  124. getSupportActionBar().setNavigationMode(ActionBar.DISPLAY_SHOW_TITLE);
  125. findViewById(R.id.setup_account).setOnClickListener(this);
  126. setSupportProgressBarIndeterminateVisibility(false);
  127. Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT);
  128. intent.putExtra(android.provider.Settings.EXTRA_AUTHORITIES, new String[] { AccountAuthenticator.AUTH_TOKEN_TYPE });
  129. startActivity(intent); // although the code is here, the activity won't be created until this.onStart() and this.onResume() are finished;
  130. mForcedLoginToCreateFirstAccount = true;
  131. }
  132. Log.i(getClass().toString(), "onCreate() end");
  133. }
  134. @Override
  135. public boolean onCreateOptionsMenu(Menu menu) {
  136. MenuInflater inflater = getSherlock().getMenuInflater();
  137. inflater.inflate(R.menu.menu, menu);
  138. return true;
  139. }
  140. @Override
  141. public boolean onOptionsItemSelected(MenuItem item) {
  142. boolean retval = true;
  143. switch (item.getItemId()) {
  144. case R.id.createDirectoryItem: {
  145. showDialog(DIALOG_CREATE_DIR);
  146. break;
  147. }
  148. case R.id.startSync: {
  149. ContentResolver.cancelSync(null, "org.owncloud"); // cancel the current synchronizations of any ownCloud account
  150. Bundle bundle = new Bundle();
  151. bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
  152. ContentResolver.requestSync(
  153. AccountUtils.getCurrentOwnCloudAccount(this),
  154. "org.owncloud", bundle);
  155. break;
  156. }
  157. case R.id.action_upload: {
  158. Intent action = new Intent(Intent.ACTION_GET_CONTENT);
  159. action = action.setType("*/*")
  160. .addCategory(Intent.CATEGORY_OPENABLE);
  161. startActivityForResult(
  162. Intent.createChooser(action, getString(R.string.upload_chooser_title)),
  163. ACTION_SELECT_FILE);
  164. break;
  165. }
  166. case R.id.action_settings: {
  167. Intent settingsIntent = new Intent(this, Preferences.class);
  168. startActivity(settingsIntent);
  169. break;
  170. }
  171. case R.id.about_app : {
  172. showDialog(DIALOG_ABOUT_APP);
  173. break;
  174. }
  175. case android.R.id.home: {
  176. if(mCurrentDir != null && mCurrentDir.getParentId() != 0){
  177. onBackPressed();
  178. }
  179. break;
  180. }
  181. default:
  182. retval = false;
  183. }
  184. return retval;
  185. }
  186. @Override
  187. public boolean onNavigationItemSelected(int itemPosition, long itemId) {
  188. int i = itemPosition;
  189. while (i-- != 0) {
  190. onBackPressed();
  191. }
  192. return true;
  193. }
  194. /**
  195. * Called, when the user selected something for uploading
  196. */
  197. public void onActivityResult(int requestCode, int resultCode, Intent data) {
  198. if (requestCode == ACTION_SELECT_FILE) {
  199. if (resultCode == RESULT_OK) {
  200. String filepath = null;
  201. try {
  202. Uri selectedImageUri = data.getData();
  203. String filemanagerstring = selectedImageUri.getPath();
  204. String selectedImagePath = getPath(selectedImageUri);
  205. if (selectedImagePath != null)
  206. filepath = selectedImagePath;
  207. else
  208. filepath = filemanagerstring;
  209. } catch (Exception e) {
  210. Log.e("FileDisplay", "Unexpected exception when trying to read the result of Intent.ACTION_GET_CONTENT", e);
  211. e.printStackTrace();
  212. } finally {
  213. if (filepath == null) {
  214. Log.e("FileDisplay", "Couldnt resolve path to file");
  215. Toast t = Toast.makeText(this, getString(R.string.filedisplay_unexpected_bad_get_content), Toast.LENGTH_LONG);
  216. t.show();
  217. return;
  218. }
  219. }
  220. Intent i = new Intent(this, FileUploader.class);
  221. i.putExtra(FileUploader.KEY_ACCOUNT,
  222. AccountUtils.getCurrentOwnCloudAccount(this));
  223. String remotepath = new String();
  224. for (int j = mDirectories.getCount() - 2; j >= 0; --j) {
  225. remotepath += OCFile.PATH_SEPARATOR + mDirectories.getItem(j);
  226. }
  227. if (!remotepath.endsWith(OCFile.PATH_SEPARATOR))
  228. remotepath += OCFile.PATH_SEPARATOR;
  229. remotepath += new File(filepath).getName();
  230. i.putExtra(FileUploader.KEY_LOCAL_FILE, filepath);
  231. i.putExtra(FileUploader.KEY_REMOTE_FILE, remotepath);
  232. i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
  233. startService(i);
  234. }
  235. }/* dvelasco: WIP - not working as expected ... yet :)
  236. else if (requestCode == ACTION_CREATE_FIRST_ACCOUNT) {
  237. if (resultCode != RESULT_OK) {
  238. finish(); // the user cancelled the AuthenticatorActivity
  239. }
  240. }*/
  241. }
  242. @Override
  243. public void onBackPressed() {
  244. if (mDirectories == null || mDirectories.getCount() <= 1) {
  245. finish();
  246. return;
  247. }
  248. popDirname();
  249. mFileList.onNavigateUp();
  250. mCurrentDir = mFileList.getCurrentFile();
  251. if (mDualPane) {
  252. // Resets the FileDetailsFragment on Tablets so that it always displays
  253. FileDetailFragment fileDetails = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
  254. if (fileDetails != null && !fileDetails.isEmpty()) {
  255. FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
  256. transaction.remove(fileDetails);
  257. transaction.add(R.id.file_details_container, new FileDetailFragment(null, null));
  258. transaction.commit();
  259. }
  260. }
  261. if(mCurrentDir.getParentId() == 0){
  262. ActionBar actionBar = getSupportActionBar();
  263. actionBar.setDisplayHomeAsUpEnabled(false);
  264. }
  265. }
  266. @Override
  267. protected void onSaveInstanceState(Bundle outState) {
  268. // responsability of restore is prefered in onCreate() before than in onRestoreInstanceState when there are Fragments involved
  269. Log.i(getClass().toString(), "onSaveInstanceState() start");
  270. super.onSaveInstanceState(outState);
  271. if(mDirectories != null && mDirectories.getCount() != 0){
  272. mDirs = new String[mDirectories.getCount()-1];
  273. for (int j = mDirectories.getCount() - 2, i = 0; j >= 0; --j, ++i) {
  274. mDirs[i] = mDirectories.getItem(j);
  275. }
  276. }
  277. outState.putStringArray(KEY_DIR_ARRAY, mDirs);
  278. outState.putParcelable(FileDetailFragment.EXTRA_FILE, mCurrentDir);
  279. Log.i(getClass().toString(), "onSaveInstanceState() end");
  280. }
  281. @Override
  282. protected void onResume() {
  283. Log.i(getClass().toString(), "onResume() start");
  284. super.onResume();
  285. if (AccountUtils.accountsAreSetup(this)) {
  286. // at least an account exist: normal operation
  287. // set the layout only if it couldn't be set in onCreate
  288. if (mForcedLoginToCreateFirstAccount) {
  289. initDelayedTilAccountAvailabe();
  290. mForcedLoginToCreateFirstAccount = false;
  291. }
  292. // Listen for sync messages
  293. IntentFilter syncIntentFilter = new IntentFilter(FileSyncService.SYNC_MESSAGE);
  294. mSyncBroadcastReceiver = new SyncBroadcastReceiver();
  295. registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);
  296. // Listen for upload messages
  297. IntentFilter uploadIntentFilter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE);
  298. mUploadFinishReceiver = new UploadFinishReceiver();
  299. registerReceiver(mUploadFinishReceiver, uploadIntentFilter);
  300. // Listen for download messages
  301. IntentFilter downloadIntentFilter = new IntentFilter(FileDownloader.DOWNLOAD_FINISH_MESSAGE);
  302. mDownloadFinishReceiver = new DownloadFinishReceiver();
  303. registerReceiver(mDownloadFinishReceiver, downloadIntentFilter);
  304. // Storage manager initialization
  305. mStorageManager = new FileDataStorageManager(
  306. AccountUtils.getCurrentOwnCloudAccount(this),
  307. getContentResolver());
  308. // File list fragments
  309. mFileList = (FileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
  310. // Figure out what directory to list.
  311. // Priority: Intent (here), savedInstanceState (onCreate), root dir (dir is null)
  312. if(getIntent().hasExtra(FileDetailFragment.EXTRA_FILE)){
  313. mCurrentDir = (OCFile) getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE);
  314. if(mCurrentDir != null && !mCurrentDir.isDirectory()){
  315. mCurrentDir = mStorageManager.getFileById(mCurrentDir.getParentId());
  316. }
  317. // Clear intent extra, so rotating the screen will not return us to this directory
  318. getIntent().removeExtra(FileDetailFragment.EXTRA_FILE);
  319. }
  320. if (mCurrentDir == null)
  321. mCurrentDir = mStorageManager.getFileByPath("/");
  322. // Drop-Down navigation and file list restore
  323. mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
  324. // Given the case we have a file to display:
  325. if(mCurrentDir != null){
  326. ArrayList<OCFile> files = new ArrayList<OCFile>();
  327. OCFile currFile = mCurrentDir;
  328. while(currFile != null){
  329. files.add(currFile);
  330. currFile = mStorageManager.getFileById(currFile.getParentId());
  331. }
  332. // Insert in mDirs
  333. mDirs = new String[files.size()];
  334. for(int i = files.size() - 1; i >= 0; i--){
  335. mDirs[i] = files.get(i).getFileName();
  336. }
  337. }
  338. if (mDirs != null) {
  339. for (String s : mDirs)
  340. mDirectories.add(s);
  341. } else {
  342. mDirectories.add(OCFile.PATH_SEPARATOR);
  343. }
  344. // Actionbar setup
  345. ActionBar action_bar = getSupportActionBar();
  346. action_bar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
  347. action_bar.setDisplayShowTitleEnabled(false);
  348. action_bar.setListNavigationCallbacks(mDirectories, this);
  349. if(mCurrentDir != null && mCurrentDir.getParentId() != 0){
  350. action_bar.setDisplayHomeAsUpEnabled(true);
  351. } else {
  352. action_bar.setDisplayHomeAsUpEnabled(false);
  353. }
  354. // List dir here
  355. mFileList.listDirectory(mCurrentDir);
  356. }
  357. Log.i(getClass().toString(), "onResume() end");
  358. }
  359. @Override
  360. protected void onPause() {
  361. Log.i(getClass().toString(), "onPause() start");
  362. super.onPause();
  363. if (mSyncBroadcastReceiver != null) {
  364. unregisterReceiver(mSyncBroadcastReceiver);
  365. mSyncBroadcastReceiver = null;
  366. }
  367. if (mUploadFinishReceiver != null) {
  368. unregisterReceiver(mUploadFinishReceiver);
  369. mUploadFinishReceiver = null;
  370. }
  371. if (mDownloadFinishReceiver != null) {
  372. unregisterReceiver(mDownloadFinishReceiver);
  373. mDownloadFinishReceiver = null;
  374. }
  375. getIntent().putExtra(FileDetailFragment.EXTRA_FILE, mCurrentDir);
  376. Log.i(getClass().toString(), "onPause() end");
  377. }
  378. @Override
  379. protected Dialog onCreateDialog(int id) {
  380. Dialog dialog = null;
  381. AlertDialog.Builder builder;
  382. switch (id) {
  383. case DIALOG_SETUP_ACCOUNT:
  384. builder = new AlertDialog.Builder(this);
  385. builder.setTitle(R.string.main_tit_accsetup);
  386. builder.setMessage(R.string.main_wrn_accsetup);
  387. builder.setCancelable(false);
  388. builder.setPositiveButton(android.R.string.ok, this);
  389. builder.setNegativeButton(android.R.string.cancel, this);
  390. dialog = builder.create();
  391. break;
  392. case DIALOG_ABOUT_APP: {
  393. builder = new AlertDialog.Builder(this);
  394. builder.setTitle(getString(R.string.about_title));
  395. PackageInfo pkg;
  396. try {
  397. pkg = getPackageManager().getPackageInfo(getPackageName(), 0);
  398. builder.setMessage("ownCloud android client\n\nversion: " + pkg.versionName );
  399. builder.setIcon(android.R.drawable.ic_menu_info_details);
  400. dialog = builder.create();
  401. } catch (NameNotFoundException e) {
  402. builder = null;
  403. dialog = null;
  404. Log.e(TAG, "Error while showing about dialog", e);
  405. }
  406. break;
  407. }
  408. case DIALOG_CREATE_DIR: {
  409. builder = new Builder(this);
  410. final EditText dirNameInput = new EditText(getBaseContext());
  411. final Account a = AccountUtils.getCurrentOwnCloudAccount(this);
  412. builder.setView(dirNameInput);
  413. builder.setTitle(R.string.uploader_info_dirname);
  414. int typed_color = getResources().getColor(R.color.setup_text_typed);
  415. dirNameInput.setTextColor(typed_color);
  416. builder.setPositiveButton(android.R.string.ok,
  417. new OnClickListener() {
  418. public void onClick(DialogInterface dialog, int which) {
  419. String directoryName = dirNameInput.getText().toString();
  420. if (directoryName.trim().length() == 0) {
  421. dialog.cancel();
  422. return;
  423. }
  424. // Figure out the path where the dir needs to be created
  425. String path;
  426. if (mCurrentDir == null) {
  427. // this is just a patch; we should ensure that mCurrentDir never is null
  428. if (!mStorageManager.fileExists(OCFile.PATH_SEPARATOR)) {
  429. OCFile file = new OCFile(OCFile.PATH_SEPARATOR);
  430. mStorageManager.saveFile(file);
  431. }
  432. mCurrentDir = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);
  433. }
  434. path = FileDisplayActivity.this.mCurrentDir.getRemotePath();
  435. // Create directory
  436. path += directoryName + OCFile.PATH_SEPARATOR;
  437. Thread thread = new Thread(new DirectoryCreator(path, a, new Handler()));
  438. thread.start();
  439. dialog.dismiss();
  440. showDialog(DIALOG_SHORT_WAIT);
  441. }
  442. });
  443. builder.setNegativeButton(R.string.common_cancel,
  444. new OnClickListener() {
  445. public void onClick(DialogInterface dialog, int which) {
  446. dialog.cancel();
  447. }
  448. });
  449. dialog = builder.create();
  450. break;
  451. }
  452. case DIALOG_SHORT_WAIT: {
  453. ProgressDialog working_dialog = new ProgressDialog(this);
  454. working_dialog.setMessage(getResources().getString(
  455. R.string.wait_a_moment));
  456. working_dialog.setIndeterminate(true);
  457. working_dialog.setCancelable(false);
  458. dialog = working_dialog;
  459. break;
  460. }
  461. default:
  462. dialog = null;
  463. }
  464. return dialog;
  465. }
  466. /**
  467. * Responds to the "There are no ownCloud Accounts setup" dialog
  468. * TODO: Dialog is 100% useless -> Remove
  469. */
  470. @Override
  471. public void onClick(DialogInterface dialog, int which) {
  472. // In any case - we won't need it anymore
  473. dialog.dismiss();
  474. switch (which) {
  475. case DialogInterface.BUTTON_POSITIVE:
  476. Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
  477. intent.putExtra("authorities",
  478. new String[] { AccountAuthenticator.AUTH_TOKEN_TYPE });
  479. startActivity(intent);
  480. break;
  481. case DialogInterface.BUTTON_NEGATIVE:
  482. finish();
  483. }
  484. }
  485. /**
  486. * Translates a content URI of an image to a physical path
  487. * on the disk
  488. * @param uri The URI to resolve
  489. * @return The path to the image or null if it could not be found
  490. */
  491. public String getPath(Uri uri) {
  492. String[] projection = { MediaStore.Images.Media.DATA };
  493. Cursor cursor = managedQuery(uri, projection, null, null, null);
  494. if (cursor != null) {
  495. int column_index = cursor
  496. .getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
  497. cursor.moveToFirst();
  498. return cursor.getString(column_index);
  499. }
  500. return null;
  501. }
  502. /**
  503. * Pushes a directory to the drop down list
  504. * @param directory to push
  505. * @throws IllegalArgumentException If the {@link OCFile#isDirectory()} returns false.
  506. */
  507. public void pushDirname(OCFile directory) {
  508. if(!directory.isDirectory()){
  509. throw new IllegalArgumentException("Only directories may be pushed!");
  510. }
  511. mDirectories.insert(directory.getFileName(), 0);
  512. mCurrentDir = directory;
  513. }
  514. /**
  515. * Pops a directory name from the drop down list
  516. * @return True, unless the stack is empty
  517. */
  518. public boolean popDirname() {
  519. mDirectories.remove(mDirectories.getItem(0));
  520. return !mDirectories.isEmpty();
  521. }
  522. private class DirectoryCreator implements Runnable {
  523. private String mTargetPath;
  524. private Account mAccount;
  525. private AccountManager mAm;
  526. private Handler mHandler;
  527. public DirectoryCreator(String targetPath, Account account, Handler handler) {
  528. mTargetPath = targetPath;
  529. mAccount = account;
  530. mAm = (AccountManager) getSystemService(ACCOUNT_SERVICE);
  531. mHandler = handler;
  532. }
  533. @Override
  534. public void run() {
  535. WebdavClient wdc = new WebdavClient(mAccount, getApplicationContext());
  536. String username = mAccount.name.substring(0,
  537. mAccount.name.lastIndexOf('@'));
  538. String password = mAm.getPassword(mAccount);
  539. wdc.setCredentials(username, password);
  540. wdc.allowSelfsignedCertificates();
  541. boolean created = wdc.createDirectory(mTargetPath);
  542. if (created) {
  543. mHandler.post(new Runnable() {
  544. @Override
  545. public void run() {
  546. dismissDialog(DIALOG_SHORT_WAIT);
  547. // Save new directory in local database
  548. OCFile newDir = new OCFile(mTargetPath);
  549. newDir.setMimetype("DIR");
  550. newDir.setParentId(mCurrentDir.getFileId());
  551. mStorageManager.saveFile(newDir);
  552. // Display the new folder right away
  553. mFileList.listDirectory(mCurrentDir);
  554. }
  555. });
  556. } else {
  557. mHandler.post(new Runnable() {
  558. @Override
  559. public void run() {
  560. dismissDialog(DIALOG_SHORT_WAIT);
  561. try {
  562. Toast msg = Toast.makeText(FileDisplayActivity.this, R.string.create_dir_fail_msg, Toast.LENGTH_LONG);
  563. msg.show();
  564. } catch (NotFoundException e) {
  565. Log.e(TAG, "Error while trying to show fail message " , e);
  566. }
  567. }
  568. });
  569. }
  570. }
  571. }
  572. // Custom array adapter to override text colors
  573. private class CustomArrayAdapter<T> extends ArrayAdapter<T> {
  574. public CustomArrayAdapter(FileDisplayActivity ctx, int view) {
  575. super(ctx, view);
  576. }
  577. public View getView(int position, View convertView, ViewGroup parent) {
  578. View v = super.getView(position, convertView, parent);
  579. ((TextView) v).setTextColor(getResources().getColorStateList(
  580. android.R.color.white));
  581. return v;
  582. }
  583. public View getDropDownView(int position, View convertView,
  584. ViewGroup parent) {
  585. View v = super.getDropDownView(position, convertView, parent);
  586. ((TextView) v).setTextColor(getResources().getColorStateList(
  587. android.R.color.white));
  588. return v;
  589. }
  590. }
  591. private class SyncBroadcastReceiver extends BroadcastReceiver {
  592. /**
  593. * {@link BroadcastReceiver} to enable syncing feedback in UI
  594. */
  595. @Override
  596. public void onReceive(Context context, Intent intent) {
  597. boolean inProgress = intent.getBooleanExtra(
  598. FileSyncService.IN_PROGRESS, false);
  599. String accountName = intent
  600. .getStringExtra(FileSyncService.ACCOUNT_NAME);
  601. Log.d("FileDisplay", "sync of account " + accountName
  602. + " is in_progress: " + inProgress);
  603. if (accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name)) {
  604. String synchFolderRemotePath = intent.getStringExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH);
  605. boolean fillBlankRoot = false;
  606. if (mCurrentDir == null) {
  607. mCurrentDir = mStorageManager.getFileByPath("/");
  608. fillBlankRoot = (mCurrentDir != null);
  609. }
  610. if ((synchFolderRemotePath != null && mCurrentDir != null && (mCurrentDir.getRemotePath().equals(synchFolderRemotePath)))
  611. || fillBlankRoot ) {
  612. if (!fillBlankRoot)
  613. mCurrentDir = getStorageManager().getFileByPath(synchFolderRemotePath);
  614. FileListFragment fileListFragment = (FileListFragment) getSupportFragmentManager()
  615. .findFragmentById(R.id.fileList);
  616. if (fileListFragment != null) {
  617. fileListFragment.listDirectory(mCurrentDir);
  618. }
  619. }
  620. setSupportProgressBarIndeterminateVisibility(inProgress);
  621. }
  622. }
  623. }
  624. private class UploadFinishReceiver extends BroadcastReceiver {
  625. /**
  626. * Once the file upload has finished -> update view
  627. * @author David A. Velasco
  628. * {@link BroadcastReceiver} to enable upload feedback in UI
  629. */
  630. @Override
  631. public void onReceive(Context context, Intent intent) {
  632. long parentDirId = intent.getLongExtra(FileUploader.EXTRA_PARENT_DIR_ID, -1);
  633. OCFile parentDir = mStorageManager.getFileById(parentDirId);
  634. String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME);
  635. if (accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name) &&
  636. parentDir != null &&
  637. ( (mCurrentDir == null && parentDir.getFileName().equals("/")) ||
  638. parentDir.equals(mCurrentDir)
  639. )
  640. ) {
  641. FileListFragment fileListFragment = (FileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
  642. if (fileListFragment != null) {
  643. fileListFragment.listDirectory();
  644. }
  645. }
  646. }
  647. }
  648. /**
  649. * Once the file download has finished -> update view
  650. */
  651. private class DownloadFinishReceiver extends BroadcastReceiver {
  652. @Override
  653. public void onReceive(Context context, Intent intent) {
  654. String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
  655. String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME);
  656. if (accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name) &&
  657. mCurrentDir != null && mCurrentDir.getFileId() == mStorageManager.getFileByPath(downloadedRemotePath).getParentId()) {
  658. FileListFragment fileListFragment = (FileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
  659. if (fileListFragment != null) {
  660. fileListFragment.listDirectory();
  661. }
  662. }
  663. }
  664. }
  665. @Override
  666. public void onClick(View v) {
  667. if (v.getId() == R.id.setup_account) {
  668. Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT);
  669. intent.putExtra(android.provider.Settings.EXTRA_AUTHORITIES, new String[] { AccountAuthenticator.AUTH_TOKEN_TYPE });
  670. startActivity(intent);
  671. mForcedLoginToCreateFirstAccount = true;
  672. }
  673. }
  674. /**
  675. * {@inheritDoc}
  676. */
  677. @Override
  678. public DataStorageManager getStorageManager() {
  679. return mStorageManager;
  680. }
  681. /**
  682. * {@inheritDoc}
  683. */
  684. @Override
  685. public void onDirectoryClick(OCFile directory) {
  686. pushDirname(directory);
  687. ActionBar actionBar = getSupportActionBar();
  688. actionBar.setDisplayHomeAsUpEnabled(true);
  689. if (mDualPane) {
  690. // Resets the FileDetailsFragment on Tablets so that it always displays
  691. FileDetailFragment fileDetails = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
  692. if (fileDetails != null && !fileDetails.isEmpty()) {
  693. FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
  694. transaction.remove(fileDetails);
  695. transaction.add(R.id.file_details_container, new FileDetailFragment(null, null));
  696. transaction.commit();
  697. }
  698. }
  699. }
  700. /**
  701. * {@inheritDoc}
  702. */
  703. @Override
  704. public void onFileClick(OCFile file) {
  705. // If we are on a large device -> update fragment
  706. if (mDualPane) {
  707. // buttons in the details view are problematic when trying to reuse an existing fragment; create always a new one solves some of them, BUT no all; downloads are 'dangerous'
  708. FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
  709. transaction.replace(R.id.file_details_container, new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
  710. transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
  711. transaction.commit();
  712. } else { // small or medium screen device -> new Activity
  713. Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
  714. showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
  715. showDetailsIntent.putExtra(FileDownloader.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
  716. startActivity(showDetailsIntent);
  717. }
  718. }
  719. /**
  720. * {@inheritDoc}
  721. */
  722. @Override
  723. public void onFileStateChanged() {
  724. FileListFragment fileListFragment = (FileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
  725. if (fileListFragment != null) {
  726. fileListFragment.listDirectory();
  727. }
  728. }
  729. /**
  730. * Operations in this method should be preferably performed in onCreate to have a lighter onResume method.
  731. *
  732. * But we need to delay them to onResume for the first start of the application, when no account exists and the login activity must be shown; and
  733. * put instead the ugly view that shows the 'Setup' button to restart the login activity.
  734. *
  735. * In other way, if the users cancels or presses BACK in the login page that first time (users can be cruel sometimes) would show a blank view (the
  736. * FragmentList view empty).
  737. *
  738. * This is temporal, until we found out how to get a result in this activity after launching the ADD_ACCOUNT Intent with startActivityForResult (not trivial)
  739. */
  740. private void initDelayedTilAccountAvailabe() {
  741. setContentView(mLayoutView);
  742. mDualPane = (findViewById(R.id.file_details_container) != null);
  743. if (mDualPane && getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG) == null) {
  744. FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
  745. transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null)); // empty FileDetailFragment
  746. transaction.commit();
  747. }
  748. setSupportProgressBarIndeterminateVisibility(false);
  749. }
  750. /**
  751. * Launch an intent to request the PIN code to the user before letting him use the app
  752. */
  753. private void requestPinCode() {
  754. boolean pinStart = false;
  755. SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
  756. pinStart = appPrefs.getBoolean("set_pincode", false);
  757. if (pinStart) {
  758. Intent i = new Intent(getApplicationContext(), PinCodeActivity.class);
  759. i.putExtra(PinCodeActivity.EXTRA_ACTIVITY, "FileDisplayActivity");
  760. startActivity(i);
  761. }
  762. }
  763. }