FileDisplayActivity.java 37 KB

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