FileDetailFragment.java 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114
  1. /* ownCloud Android client application
  2. * Copyright (C) 2011 Bartek Przybylski
  3. * Copyright (C) 2012-2013 ownCloud Inc.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License version 2,
  7. * as published by the Free Software Foundation.
  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.fragment;
  19. import java.io.File;
  20. import java.lang.ref.WeakReference;
  21. import java.util.ArrayList;
  22. import java.util.List;
  23. import android.accounts.Account;
  24. import android.accounts.AccountManager;
  25. import android.app.Activity;
  26. import android.content.ActivityNotFoundException;
  27. import android.content.BroadcastReceiver;
  28. import android.content.Context;
  29. import android.content.Intent;
  30. import android.content.IntentFilter;
  31. import android.net.Uri;
  32. import android.os.Bundle;
  33. import android.os.Handler;
  34. import android.support.v4.app.FragmentTransaction;
  35. import android.view.LayoutInflater;
  36. import android.view.View;
  37. import android.view.View.OnClickListener;
  38. import android.view.ViewGroup;
  39. import android.webkit.MimeTypeMap;
  40. import android.widget.CheckBox;
  41. import android.widget.ImageView;
  42. import android.widget.ProgressBar;
  43. import android.widget.TextView;
  44. import android.widget.Toast;
  45. import com.actionbarsherlock.app.SherlockFragment;
  46. import com.actionbarsherlock.view.Menu;
  47. import com.actionbarsherlock.view.MenuInflater;
  48. import com.actionbarsherlock.view.MenuItem;
  49. import com.owncloud.android.DisplayUtils;
  50. import com.owncloud.android.Log_OC;
  51. import com.owncloud.android.datamodel.FileDataStorageManager;
  52. import com.owncloud.android.datamodel.OCFile;
  53. import com.owncloud.android.files.services.FileObserverService;
  54. import com.owncloud.android.files.services.FileUploader;
  55. import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
  56. import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
  57. import com.owncloud.android.network.OwnCloudClientUtils;
  58. import com.owncloud.android.operations.OnRemoteOperationListener;
  59. import com.owncloud.android.operations.RemoteOperation;
  60. import com.owncloud.android.operations.RemoteOperationResult;
  61. import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
  62. import com.owncloud.android.operations.RemoveFileOperation;
  63. import com.owncloud.android.operations.RenameFileOperation;
  64. import com.owncloud.android.operations.SynchronizeFileOperation;
  65. import com.owncloud.android.ui.activity.ConflictsResolveActivity;
  66. import com.owncloud.android.ui.activity.FileDetailActivity;
  67. import com.owncloud.android.ui.activity.FileDisplayActivity;
  68. import com.owncloud.android.ui.dialog.EditNameDialog;
  69. import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
  70. import com.owncloud.android.R;
  71. import eu.alefzero.webdav.OnDatatransferProgressListener;
  72. import eu.alefzero.webdav.WebdavClient;
  73. import eu.alefzero.webdav.WebdavUtils;
  74. /**
  75. * This Fragment is used to display the details about a file.
  76. *
  77. * @author Bartek Przybylski
  78. * @author David A. Velasco
  79. */
  80. public class FileDetailFragment extends SherlockFragment implements
  81. OnClickListener,
  82. ConfirmationDialogFragment.ConfirmationDialogFragmentListener, OnRemoteOperationListener, EditNameDialogListener,
  83. FileFragment {
  84. public static final String EXTRA_FILE = "FILE";
  85. public static final String EXTRA_ACCOUNT = "ACCOUNT";
  86. private FileFragment.ContainerActivity mContainerActivity;
  87. private int mLayout;
  88. private View mView;
  89. private OCFile mFile;
  90. private Account mAccount;
  91. private FileDataStorageManager mStorageManager;
  92. private UploadFinishReceiver mUploadFinishReceiver;
  93. public ProgressListener mProgressListener;
  94. private Handler mHandler;
  95. private RemoteOperation mLastRemoteOperation;
  96. private static final String TAG = FileDetailFragment.class.getSimpleName();
  97. public static final String FTAG = "FileDetails";
  98. public static final String FTAG_CONFIRMATION = "REMOVE_CONFIRMATION_FRAGMENT";
  99. /**
  100. * Creates an empty details fragment.
  101. *
  102. * It's necessary to keep a public constructor without parameters; the system uses it when tries to reinstantiate a fragment automatically.
  103. */
  104. public FileDetailFragment() {
  105. mFile = null;
  106. mAccount = null;
  107. mStorageManager = null;
  108. mLayout = R.layout.file_details_empty;
  109. mProgressListener = null;
  110. }
  111. /**
  112. * Creates a details fragment.
  113. *
  114. * When 'fileToDetail' or 'ocAccount' are null, creates a dummy layout (to use when a file wasn't tapped before).
  115. *
  116. * @param fileToDetail An {@link OCFile} to show in the fragment
  117. * @param ocAccount An ownCloud account; needed to start downloads
  118. */
  119. public FileDetailFragment(OCFile fileToDetail, Account ocAccount) {
  120. mFile = fileToDetail;
  121. mAccount = ocAccount;
  122. mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment
  123. mLayout = R.layout.file_details_empty;
  124. mProgressListener = null;
  125. }
  126. @Override
  127. public void onCreate(Bundle savedInstanceState) {
  128. super.onCreate(savedInstanceState);
  129. mHandler = new Handler();
  130. setHasOptionsMenu(true);
  131. }
  132. @Override
  133. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  134. Bundle savedInstanceState) {
  135. super.onCreateView(inflater, container, savedInstanceState);
  136. if (savedInstanceState != null) {
  137. mFile = savedInstanceState.getParcelable(FileDetailFragment.EXTRA_FILE);
  138. mAccount = savedInstanceState.getParcelable(FileDetailFragment.EXTRA_ACCOUNT);
  139. }
  140. if(mFile != null && mAccount != null) {
  141. mLayout = R.layout.file_details_fragment;
  142. }
  143. View view = null;
  144. view = inflater.inflate(mLayout, container, false);
  145. mView = view;
  146. if (mLayout == R.layout.file_details_fragment) {
  147. mView.findViewById(R.id.fdKeepInSync).setOnClickListener(this);
  148. ProgressBar progressBar = (ProgressBar)mView.findViewById(R.id.fdProgressBar);
  149. mProgressListener = new ProgressListener(progressBar);
  150. mView.findViewById(R.id.fdCancelBtn).setOnClickListener(this);
  151. }
  152. updateFileDetails(false, false);
  153. return view;
  154. }
  155. /**
  156. * {@inheritDoc}
  157. */
  158. @Override
  159. public void onAttach(Activity activity) {
  160. super.onAttach(activity);
  161. try {
  162. mContainerActivity = (ContainerActivity) activity;
  163. } catch (ClassCastException e) {
  164. throw new ClassCastException(activity.toString() + " must implement " + FileDetailFragment.ContainerActivity.class.getSimpleName());
  165. }
  166. }
  167. /**
  168. * {@inheritDoc}
  169. */
  170. @Override
  171. public void onActivityCreated(Bundle savedInstanceState) {
  172. super.onActivityCreated(savedInstanceState);
  173. if (mAccount != null) {
  174. mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());
  175. }
  176. }
  177. @Override
  178. public void onSaveInstanceState(Bundle outState) {
  179. super.onSaveInstanceState(outState);
  180. outState.putParcelable(FileDetailFragment.EXTRA_FILE, mFile);
  181. outState.putParcelable(FileDetailFragment.EXTRA_ACCOUNT, mAccount);
  182. }
  183. @Override
  184. public void onStart() {
  185. super.onStart();
  186. listenForTransferProgress();
  187. }
  188. @Override
  189. public void onResume() {
  190. super.onResume();
  191. mUploadFinishReceiver = new UploadFinishReceiver();
  192. IntentFilter filter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE);
  193. getActivity().registerReceiver(mUploadFinishReceiver, filter);
  194. }
  195. @Override
  196. public void onPause() {
  197. super.onPause();
  198. if (mUploadFinishReceiver != null) {
  199. getActivity().unregisterReceiver(mUploadFinishReceiver);
  200. mUploadFinishReceiver = null;
  201. }
  202. }
  203. @Override
  204. public void onStop() {
  205. super.onStop();
  206. leaveTransferProgress();
  207. }
  208. @Override
  209. public View getView() {
  210. return super.getView() == null ? mView : super.getView();
  211. }
  212. /**
  213. * {@inheritDoc}
  214. */
  215. @Override
  216. public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
  217. super.onCreateOptionsMenu(menu, inflater);
  218. inflater.inflate(R.menu.file_actions_menu, menu);
  219. MenuItem item = menu.findItem(R.id.action_see_details);
  220. if (item != null) {
  221. item.setVisible(false);
  222. item.setEnabled(false);
  223. }
  224. }
  225. /**
  226. * {@inheritDoc}
  227. */
  228. @Override
  229. public void onPrepareOptionsMenu (Menu menu) {
  230. super.onPrepareOptionsMenu(menu);
  231. List<Integer> toHide = new ArrayList<Integer>();
  232. List<Integer> toShow = new ArrayList<Integer>();
  233. FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
  234. boolean downloading = downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile);
  235. FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
  236. boolean uploading = uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile);
  237. if (downloading || uploading) {
  238. toHide.add(R.id.action_download_file);
  239. toHide.add(R.id.action_rename_file);
  240. toHide.add(R.id.action_remove_file);
  241. toHide.add(R.id.action_open_file_with);
  242. if (!downloading) {
  243. toHide.add(R.id.action_cancel_download);
  244. toShow.add(R.id.action_cancel_upload);
  245. } else {
  246. toHide.add(R.id.action_cancel_upload);
  247. toShow.add(R.id.action_cancel_download);
  248. }
  249. } else if (mFile != null && mFile.isDown()) {
  250. toHide.add(R.id.action_download_file);
  251. toHide.add(R.id.action_cancel_download);
  252. toHide.add(R.id.action_cancel_upload);
  253. toShow.add(R.id.action_rename_file);
  254. toShow.add(R.id.action_remove_file);
  255. toShow.add(R.id.action_open_file_with);
  256. toShow.add(R.id.action_sync_file);
  257. } else if (mFile != null) {
  258. toHide.add(R.id.action_open_file_with);
  259. toHide.add(R.id.action_cancel_download);
  260. toHide.add(R.id.action_cancel_upload);
  261. toHide.add(R.id.action_sync_file);
  262. toShow.add(R.id.action_rename_file);
  263. toShow.add(R.id.action_remove_file);
  264. toShow.add(R.id.action_download_file);
  265. } else {
  266. toHide.add(R.id.action_open_file_with);
  267. toHide.add(R.id.action_cancel_download);
  268. toHide.add(R.id.action_cancel_upload);
  269. toHide.add(R.id.action_sync_file);
  270. toHide.add(R.id.action_download_file);
  271. toHide.add(R.id.action_rename_file);
  272. toHide.add(R.id.action_remove_file);
  273. }
  274. MenuItem item = null;
  275. for (int i : toHide) {
  276. item = menu.findItem(i);
  277. if (item != null) {
  278. item.setVisible(false);
  279. item.setEnabled(false);
  280. }
  281. }
  282. for (int i : toShow) {
  283. item = menu.findItem(i);
  284. if (item != null) {
  285. item.setVisible(true);
  286. item.setEnabled(true);
  287. }
  288. }
  289. }
  290. /**
  291. * {@inheritDoc}
  292. */
  293. @Override
  294. public boolean onOptionsItemSelected(MenuItem item) {
  295. switch (item.getItemId()) {
  296. case R.id.action_open_file_with: {
  297. openFile();
  298. return true;
  299. }
  300. case R.id.action_remove_file: {
  301. removeFile();
  302. return true;
  303. }
  304. case R.id.action_rename_file: {
  305. renameFile();
  306. return true;
  307. }
  308. case R.id.action_download_file:
  309. case R.id.action_cancel_download:
  310. case R.id.action_cancel_upload:
  311. case R.id.action_sync_file: {
  312. synchronizeFile();
  313. return true;
  314. }
  315. default:
  316. return false;
  317. }
  318. }
  319. @Override
  320. public void onClick(View v) {
  321. switch (v.getId()) {
  322. case R.id.fdKeepInSync: {
  323. toggleKeepInSync();
  324. break;
  325. }
  326. case R.id.fdCancelBtn: {
  327. synchronizeFile();
  328. break;
  329. }
  330. default:
  331. Log_OC.e(TAG, "Incorrect view clicked!");
  332. }
  333. }
  334. private void toggleKeepInSync() {
  335. CheckBox cb = (CheckBox) getView().findViewById(R.id.fdKeepInSync);
  336. mFile.setKeepInSync(cb.isChecked());
  337. mStorageManager.saveFile(mFile);
  338. /// register the OCFile instance in the observer service to monitor local updates;
  339. /// if necessary, the file is download
  340. Intent intent = new Intent(getActivity().getApplicationContext(),
  341. FileObserverService.class);
  342. intent.putExtra(FileObserverService.KEY_FILE_CMD,
  343. (cb.isChecked()?
  344. FileObserverService.CMD_ADD_OBSERVED_FILE:
  345. FileObserverService.CMD_DEL_OBSERVED_FILE));
  346. intent.putExtra(FileObserverService.KEY_CMD_ARG_FILE, mFile);
  347. intent.putExtra(FileObserverService.KEY_CMD_ARG_ACCOUNT, mAccount);
  348. getActivity().startService(intent);
  349. if (mFile.keepInSync()) {
  350. synchronizeFile(); // force an immediate synchronization
  351. }
  352. }
  353. private void removeFile() {
  354. ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
  355. R.string.confirmation_remove_alert,
  356. new String[]{mFile.getFileName()},
  357. mFile.isDown() ? R.string.confirmation_remove_remote_and_local : R.string.confirmation_remove_remote,
  358. mFile.isDown() ? R.string.confirmation_remove_local : -1,
  359. R.string.common_cancel);
  360. confDialog.setOnConfirmationListener(this);
  361. confDialog.show(getFragmentManager(), FTAG_CONFIRMATION);
  362. }
  363. private void renameFile() {
  364. String fileName = mFile.getFileName();
  365. int extensionStart = mFile.isDirectory() ? -1 : fileName.lastIndexOf(".");
  366. int selectionEnd = (extensionStart >= 0) ? extensionStart : fileName.length();
  367. EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), fileName, 0, selectionEnd, this);
  368. dialog.show(getFragmentManager(), "nameeditdialog");
  369. }
  370. private void synchronizeFile() {
  371. FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
  372. FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
  373. if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile)) {
  374. downloaderBinder.cancel(mAccount, mFile);
  375. if (mFile.isDown()) {
  376. setButtonsForDown();
  377. } else {
  378. setButtonsForRemote();
  379. }
  380. } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile)) {
  381. uploaderBinder.cancel(mAccount, mFile);
  382. if (!mFile.fileExists()) {
  383. // TODO make something better
  384. if (getActivity() instanceof FileDisplayActivity) {
  385. // double pane
  386. FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction();
  387. transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null), FTAG); // empty FileDetailFragment
  388. transaction.commit();
  389. mContainerActivity.onFileStateChanged();
  390. } else {
  391. getActivity().finish();
  392. }
  393. } else if (mFile.isDown()) {
  394. setButtonsForDown();
  395. } else {
  396. setButtonsForRemote();
  397. }
  398. } else {
  399. mLastRemoteOperation = new SynchronizeFileOperation(mFile, null, mStorageManager, mAccount, true, false, getActivity());
  400. mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
  401. // update ui
  402. boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;
  403. getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);
  404. }
  405. }
  406. /**
  407. * Opens mFile.
  408. */
  409. private void openFile() {
  410. String storagePath = mFile.getStoragePath();
  411. String encodedStoragePath = WebdavUtils.encodePath(storagePath);
  412. try {
  413. Intent i = new Intent(Intent.ACTION_VIEW);
  414. i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mFile.getMimetype());
  415. i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
  416. startActivity(i);
  417. } catch (Throwable t) {
  418. Log_OC.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + mFile.getMimetype());
  419. boolean toastIt = true;
  420. String mimeType = "";
  421. try {
  422. Intent i = new Intent(Intent.ACTION_VIEW);
  423. mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1));
  424. if (mimeType == null || !mimeType.equals(mFile.getMimetype())) {
  425. if (mimeType != null) {
  426. i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mimeType);
  427. } else {
  428. // desperate try
  429. i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), "*/*");
  430. }
  431. i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
  432. startActivity(i);
  433. toastIt = false;
  434. }
  435. } catch (IndexOutOfBoundsException e) {
  436. Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath);
  437. } catch (ActivityNotFoundException e) {
  438. Log_OC.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension");
  439. } catch (Throwable th) {
  440. Log_OC.e(TAG, "Unexpected problem when opening: " + storagePath, th);
  441. } finally {
  442. if (toastIt) {
  443. Toast.makeText(getActivity(), "There is no application to handle file " + mFile.getFileName(), Toast.LENGTH_SHORT).show();
  444. }
  445. }
  446. }
  447. }
  448. @Override
  449. public void onConfirmation(String callerTag) {
  450. if (callerTag.equals(FTAG_CONFIRMATION)) {
  451. if (mStorageManager.getFileById(mFile.getFileId()) != null) {
  452. mLastRemoteOperation = new RemoveFileOperation( mFile,
  453. true,
  454. mStorageManager);
  455. mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
  456. boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;
  457. getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);
  458. }
  459. }
  460. }
  461. @Override
  462. public void onNeutral(String callerTag) {
  463. File f = null;
  464. if (mFile.isDown() && (f = new File(mFile.getStoragePath())).exists()) {
  465. f.delete();
  466. mFile.setStoragePath(null);
  467. mStorageManager.saveFile(mFile);
  468. updateFileDetails(mFile, mAccount);
  469. }
  470. }
  471. @Override
  472. public void onCancel(String callerTag) {
  473. Log_OC.d(TAG, "REMOVAL CANCELED");
  474. }
  475. /**
  476. * Check if the fragment was created with an empty layout. An empty fragment can't show file details, must be replaced.
  477. *
  478. * @return True when the fragment was created with the empty layout.
  479. */
  480. public boolean isEmpty() {
  481. return (mLayout == R.layout.file_details_empty || mFile == null || mAccount == null);
  482. }
  483. /**
  484. * {@inheritDoc}
  485. */
  486. public OCFile getFile(){
  487. return mFile;
  488. }
  489. /**
  490. * Use this method to signal this Activity that it shall update its view.
  491. *
  492. * @param file : An {@link OCFile}
  493. */
  494. public void updateFileDetails(OCFile file, Account ocAccount) {
  495. mFile = file;
  496. if (ocAccount != null && (
  497. mStorageManager == null ||
  498. (mAccount != null && !mAccount.equals(ocAccount))
  499. )) {
  500. mStorageManager = new FileDataStorageManager(ocAccount, getActivity().getApplicationContext().getContentResolver());
  501. }
  502. mAccount = ocAccount;
  503. updateFileDetails(false, false);
  504. }
  505. /**
  506. * Updates the view with all relevant details about that file.
  507. *
  508. * TODO Remove parameter when the transferring state of files is kept in database.
  509. *
  510. * TODO REFACTORING! this method called 5 times before every time the fragment is shown!
  511. *
  512. * @param transferring Flag signaling if the file should be considered as downloading or uploading,
  513. * although {@link FileDownloaderBinder#isDownloading(Account, OCFile)} and
  514. * {@link FileUploaderBinder#isUploading(Account, OCFile)} return false.
  515. *
  516. * @param refresh If 'true', try to refresh the hold file from the database
  517. */
  518. public void updateFileDetails(boolean transferring, boolean refresh) {
  519. if (readyToShow()) {
  520. if (refresh && mStorageManager != null) {
  521. mFile = mStorageManager.getFileByPath(mFile.getRemotePath());
  522. }
  523. // set file details
  524. setFilename(mFile.getFileName());
  525. setFiletype(mFile.getMimetype());
  526. setFilesize(mFile.getFileLength());
  527. if(ocVersionSupportsTimeCreated()){
  528. setTimeCreated(mFile.getCreationTimestamp());
  529. }
  530. setTimeModified(mFile.getModificationTimestamp());
  531. CheckBox cb = (CheckBox)getView().findViewById(R.id.fdKeepInSync);
  532. cb.setChecked(mFile.keepInSync());
  533. // configure UI for depending upon local state of the file
  534. //if (FileDownloader.isDownloading(mAccount, mFile.getRemotePath()) || FileUploader.isUploading(mAccount, mFile.getRemotePath())) {
  535. FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
  536. FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
  537. if (transferring || (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile)) || (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile))) {
  538. setButtonsForTransferring();
  539. } else if (mFile.isDown()) {
  540. setButtonsForDown();
  541. } else {
  542. // TODO load default preview image; when the local file is removed, the preview remains there
  543. setButtonsForRemote();
  544. }
  545. }
  546. getView().invalidate();
  547. }
  548. /**
  549. * Checks if the fragment is ready to show details of a OCFile
  550. *
  551. * @return 'True' when the fragment is ready to show details of a file
  552. */
  553. private boolean readyToShow() {
  554. return (mFile != null && mAccount != null && mLayout == R.layout.file_details_fragment);
  555. }
  556. /**
  557. * Updates the filename in view
  558. * @param filename to set
  559. */
  560. private void setFilename(String filename) {
  561. TextView tv = (TextView) getView().findViewById(R.id.fdFilename);
  562. if (tv != null)
  563. tv.setText(filename);
  564. }
  565. /**
  566. * Updates the MIME type in view
  567. * @param mimetype to set
  568. */
  569. private void setFiletype(String mimetype) {
  570. TextView tv = (TextView) getView().findViewById(R.id.fdType);
  571. if (tv != null) {
  572. String printableMimetype = DisplayUtils.convertMIMEtoPrettyPrint(mimetype);;
  573. tv.setText(printableMimetype);
  574. }
  575. ImageView iv = (ImageView) getView().findViewById(R.id.fdIcon);
  576. if (iv != null) {
  577. iv.setImageResource(DisplayUtils.getResourceId(mimetype));
  578. }
  579. }
  580. /**
  581. * Updates the file size in view
  582. * @param filesize in bytes to set
  583. */
  584. private void setFilesize(long filesize) {
  585. TextView tv = (TextView) getView().findViewById(R.id.fdSize);
  586. if (tv != null)
  587. tv.setText(DisplayUtils.bytesToHumanReadable(filesize));
  588. }
  589. /**
  590. * Updates the time that the file was created in view
  591. * @param milliseconds Unix time to set
  592. */
  593. private void setTimeCreated(long milliseconds){
  594. TextView tv = (TextView) getView().findViewById(R.id.fdCreated);
  595. TextView tvLabel = (TextView) getView().findViewById(R.id.fdCreatedLabel);
  596. if(tv != null){
  597. tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds));
  598. tv.setVisibility(View.VISIBLE);
  599. tvLabel.setVisibility(View.VISIBLE);
  600. }
  601. }
  602. /**
  603. * Updates the time that the file was last modified
  604. * @param milliseconds Unix time to set
  605. */
  606. private void setTimeModified(long milliseconds){
  607. TextView tv = (TextView) getView().findViewById(R.id.fdModified);
  608. if(tv != null){
  609. tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds));
  610. }
  611. }
  612. /**
  613. * Enables or disables buttons for a file being downloaded
  614. */
  615. private void setButtonsForTransferring() {
  616. if (!isEmpty()) {
  617. // let's protect the user from himself ;)
  618. getView().findViewById(R.id.fdKeepInSync).setEnabled(false);
  619. // show the progress bar for the transfer
  620. getView().findViewById(R.id.fdProgressBlock).setVisibility(View.VISIBLE);
  621. TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
  622. progressText.setVisibility(View.VISIBLE);
  623. FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
  624. FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
  625. if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile)) {
  626. progressText.setText(R.string.downloader_download_in_progress_ticker);
  627. } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile)) {
  628. progressText.setText(R.string.uploader_upload_in_progress_ticker);
  629. }
  630. }
  631. }
  632. /**
  633. * Enables or disables buttons for a file locally available
  634. */
  635. private void setButtonsForDown() {
  636. if (!isEmpty()) {
  637. getView().findViewById(R.id.fdKeepInSync).setEnabled(true);
  638. // hides the progress bar
  639. getView().findViewById(R.id.fdProgressBlock).setVisibility(View.GONE);
  640. TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
  641. progressText.setVisibility(View.GONE);
  642. }
  643. }
  644. /**
  645. * Enables or disables buttons for a file not locally available
  646. */
  647. private void setButtonsForRemote() {
  648. if (!isEmpty()) {
  649. getView().findViewById(R.id.fdKeepInSync).setEnabled(true);
  650. // hides the progress bar
  651. getView().findViewById(R.id.fdProgressBlock).setVisibility(View.GONE);
  652. TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
  653. progressText.setVisibility(View.GONE);
  654. }
  655. }
  656. /**
  657. * In ownCloud 3.X.X and 4.X.X there is a bug that SabreDAV does not return
  658. * the time that the file was created. There is a chance that this will
  659. * be fixed in future versions. Use this method to check if this version of
  660. * ownCloud has this fix.
  661. * @return True, if ownCloud the ownCloud version is supporting creation time
  662. */
  663. private boolean ocVersionSupportsTimeCreated(){
  664. /*if(mAccount != null){
  665. AccountManager accManager = (AccountManager) getActivity().getSystemService(Context.ACCOUNT_SERVICE);
  666. OwnCloudVersion ocVersion = new OwnCloudVersion(accManager
  667. .getUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION));
  668. if(ocVersion.compareTo(new OwnCloudVersion(0x030000)) < 0) {
  669. return true;
  670. }
  671. }*/
  672. return false;
  673. }
  674. /**
  675. * Once the file upload has finished -> update view
  676. *
  677. * Being notified about the finish of an upload is necessary for the next sequence:
  678. * 1. Upload a big file.
  679. * 2. Force a synchronization; if it finished before the upload, the file in transfer will be included in the local database and in the file list
  680. * of its containing folder; the the server includes it in the PROPFIND requests although it's not fully upload.
  681. * 3. Click the file in the list to see its details.
  682. * 4. Wait for the upload finishes; at this moment, the details view must be refreshed to enable the action buttons.
  683. */
  684. private class UploadFinishReceiver extends BroadcastReceiver {
  685. @Override
  686. public void onReceive(Context context, Intent intent) {
  687. String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME);
  688. if (!isEmpty() && accountName.equals(mAccount.name)) {
  689. boolean uploadWasFine = intent.getBooleanExtra(FileUploader.EXTRA_UPLOAD_RESULT, false);
  690. String uploadRemotePath = intent.getStringExtra(FileUploader.EXTRA_REMOTE_PATH);
  691. boolean renamedInUpload = mFile.getRemotePath().equals(intent.getStringExtra(FileUploader.EXTRA_OLD_REMOTE_PATH));
  692. if (mFile.getRemotePath().equals(uploadRemotePath) ||
  693. renamedInUpload) {
  694. if (uploadWasFine) {
  695. mFile = mStorageManager.getFileByPath(uploadRemotePath);
  696. }
  697. if (renamedInUpload) {
  698. String newName = (new File(uploadRemotePath)).getName();
  699. Toast msg = Toast.makeText(getActivity().getApplicationContext(), String.format(getString(R.string.filedetails_renamed_in_upload_msg), newName), Toast.LENGTH_LONG);
  700. msg.show();
  701. }
  702. getSherlockActivity().removeStickyBroadcast(intent); // not the best place to do this; a small refactorization of BroadcastReceivers should be done
  703. updateFileDetails(false, false); // it updates the buttons; must be called although !uploadWasFine; interrupted uploads still leave an incomplete file in the server
  704. }
  705. }
  706. }
  707. }
  708. public void onDismiss(EditNameDialog dialog) {
  709. if (dialog.getResult()) {
  710. String newFilename = dialog.getNewFilename();
  711. Log_OC.d(TAG, "name edit dialog dismissed with new name " + newFilename);
  712. mLastRemoteOperation = new RenameFileOperation( mFile,
  713. mAccount,
  714. newFilename,
  715. new FileDataStorageManager(mAccount, getActivity().getContentResolver()));
  716. mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
  717. boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;
  718. getActivity().showDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);
  719. }
  720. }
  721. /**
  722. * {@inheritDoc}
  723. */
  724. @Override
  725. public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
  726. if (operation.equals(mLastRemoteOperation)) {
  727. if (operation instanceof RemoveFileOperation) {
  728. onRemoveFileOperationFinish((RemoveFileOperation)operation, result);
  729. } else if (operation instanceof RenameFileOperation) {
  730. onRenameFileOperationFinish((RenameFileOperation)operation, result);
  731. } else if (operation instanceof SynchronizeFileOperation) {
  732. onSynchronizeFileOperationFinish((SynchronizeFileOperation)operation, result);
  733. }
  734. }
  735. }
  736. private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {
  737. boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;
  738. getActivity().dismissDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);
  739. if (result.isSuccess()) {
  740. Toast msg = Toast.makeText(getActivity().getApplicationContext(), R.string.remove_success_msg, Toast.LENGTH_LONG);
  741. msg.show();
  742. if (inDisplayActivity) {
  743. // double pane
  744. FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction();
  745. transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null)); // empty FileDetailFragment
  746. transaction.commit();
  747. mContainerActivity.onFileStateChanged();
  748. } else {
  749. getActivity().finish();
  750. }
  751. } else {
  752. Toast msg = Toast.makeText(getActivity(), R.string.remove_fail_msg, Toast.LENGTH_LONG);
  753. msg.show();
  754. if (result.isSslRecoverableException()) {
  755. // TODO show the SSL warning dialog
  756. }
  757. }
  758. }
  759. private void onRenameFileOperationFinish(RenameFileOperation operation, RemoteOperationResult result) {
  760. boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;
  761. getActivity().dismissDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);
  762. if (result.isSuccess()) {
  763. updateFileDetails(((RenameFileOperation)operation).getFile(), mAccount);
  764. mContainerActivity.onFileStateChanged();
  765. } else {
  766. if (result.getCode().equals(ResultCode.INVALID_LOCAL_FILE_NAME)) {
  767. Toast msg = Toast.makeText(getActivity(), R.string.rename_local_fail_msg, Toast.LENGTH_LONG);
  768. msg.show();
  769. // TODO throw again the new rename dialog
  770. } else {
  771. Toast msg = Toast.makeText(getActivity(), R.string.rename_server_fail_msg, Toast.LENGTH_LONG);
  772. msg.show();
  773. if (result.isSslRecoverableException()) {
  774. // TODO show the SSL warning dialog
  775. }
  776. }
  777. }
  778. }
  779. private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation, RemoteOperationResult result) {
  780. boolean inDisplayActivity = getActivity() instanceof FileDisplayActivity;
  781. getActivity().dismissDialog((inDisplayActivity)? FileDisplayActivity.DIALOG_SHORT_WAIT : FileDetailActivity.DIALOG_SHORT_WAIT);
  782. if (!result.isSuccess()) {
  783. if (result.getCode() == ResultCode.SYNC_CONFLICT) {
  784. Intent i = new Intent(getActivity(), ConflictsResolveActivity.class);
  785. i.putExtra(ConflictsResolveActivity.EXTRA_FILE, mFile);
  786. i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, mAccount);
  787. startActivity(i);
  788. } else {
  789. Toast msg = Toast.makeText(getActivity(), R.string.sync_file_fail_msg, Toast.LENGTH_LONG);
  790. msg.show();
  791. }
  792. if (mFile.isDown()) {
  793. setButtonsForDown();
  794. } else {
  795. setButtonsForRemote();
  796. }
  797. } else {
  798. if (operation.transferWasRequested()) {
  799. setButtonsForTransferring();
  800. mContainerActivity.onFileStateChanged(); // this is not working; FileDownloader won't do NOTHING at all until this method finishes, so
  801. // checking the service to see if the file is downloading results in FALSE
  802. } else {
  803. Toast msg = Toast.makeText(getActivity(), R.string.sync_file_nothing_to_do_msg, Toast.LENGTH_LONG);
  804. msg.show();
  805. if (mFile.isDown()) {
  806. setButtonsForDown();
  807. } else {
  808. setButtonsForRemote();
  809. }
  810. }
  811. }
  812. }
  813. public void listenForTransferProgress() {
  814. if (mProgressListener != null) {
  815. if (mContainerActivity.getFileDownloaderBinder() != null) {
  816. mContainerActivity.getFileDownloaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, mFile);
  817. }
  818. if (mContainerActivity.getFileUploaderBinder() != null) {
  819. mContainerActivity.getFileUploaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, mFile);
  820. }
  821. }
  822. }
  823. public void leaveTransferProgress() {
  824. if (mProgressListener != null) {
  825. if (mContainerActivity.getFileDownloaderBinder() != null) {
  826. mContainerActivity.getFileDownloaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, mFile);
  827. }
  828. if (mContainerActivity.getFileUploaderBinder() != null) {
  829. mContainerActivity.getFileUploaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, mFile);
  830. }
  831. }
  832. }
  833. /**
  834. * Helper class responsible for updating the progress bar shown for file uploading or downloading
  835. *
  836. * @author David A. Velasco
  837. */
  838. private class ProgressListener implements OnDatatransferProgressListener {
  839. int mLastPercent = 0;
  840. WeakReference<ProgressBar> mProgressBar = null;
  841. ProgressListener(ProgressBar progressBar) {
  842. mProgressBar = new WeakReference<ProgressBar>(progressBar);
  843. }
  844. @Override
  845. public void onTransferProgress(long progressRate) {
  846. // old method, nothing here
  847. };
  848. @Override
  849. public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filename) {
  850. int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));
  851. if (percent != mLastPercent) {
  852. ProgressBar pb = mProgressBar.get();
  853. if (pb != null) {
  854. pb.setProgress(percent);
  855. pb.postInvalidate();
  856. }
  857. }
  858. mLastPercent = percent;
  859. }
  860. };
  861. /*
  862. // this is a temporary class for sharing purposes, it need to be replaced in transfer service
  863. @SuppressWarnings("unused")
  864. private class ShareRunnable implements Runnable {
  865. private String mPath;
  866. public ShareRunnable(String path) {
  867. mPath = path;
  868. }
  869. public void run() {
  870. AccountManager am = AccountManager.get(getActivity());
  871. Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity());
  872. OwnCloudVersion ocv = new OwnCloudVersion(am.getUserData(account, AccountAuthenticator.KEY_OC_VERSION));
  873. String url = am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + AccountUtils.getWebdavPath(ocv);
  874. Log_OC.d("share", "sharing for version " + ocv.toString());
  875. if (ocv.compareTo(new OwnCloudVersion(0x040000)) >= 0) {
  876. String APPS_PATH = "/apps/files_sharing/";
  877. String SHARE_PATH = "ajax/share.php";
  878. String SHARED_PATH = "/apps/files_sharing/get.php?token=";
  879. final String WEBDAV_SCRIPT = "webdav.php";
  880. final String WEBDAV_FILES_LOCATION = "/files/";
  881. WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(account, getActivity().getApplicationContext());
  882. HttpConnectionManagerParams params = new HttpConnectionManagerParams();
  883. params.setMaxConnectionsPerHost(wc.getHostConfiguration(), 5);
  884. //wc.getParams().setParameter("http.protocol.single-cookie-header", true);
  885. //wc.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
  886. PostMethod post = new PostMethod(am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + APPS_PATH + SHARE_PATH);
  887. post.addRequestHeader("Content-type","application/x-www-form-urlencoded; charset=UTF-8" );
  888. post.addRequestHeader("Referer", am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL));
  889. List<NameValuePair> formparams = new ArrayList<NameValuePair>();
  890. Log_OC.d("share", mPath+"");
  891. formparams.add(new BasicNameValuePair("sources",mPath));
  892. formparams.add(new BasicNameValuePair("uid_shared_with", "public"));
  893. formparams.add(new BasicNameValuePair("permissions", "0"));
  894. post.setRequestEntity(new StringRequestEntity(URLEncodedUtils.format(formparams, HTTP.UTF_8)));
  895. int status;
  896. try {
  897. PropFindMethod find = new PropFindMethod(url+"/");
  898. find.addRequestHeader("Referer", am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL));
  899. Log_OC.d("sharer", ""+ url+"/");
  900. for (org.apache.commons.httpclient.Header a : find.getRequestHeaders()) {
  901. Log_OC.d("sharer-h", a.getName() + ":"+a.getValue());
  902. }
  903. int status2 = wc.executeMethod(find);
  904. Log_OC.d("sharer", "propstatus "+status2);
  905. GetMethod get = new GetMethod(am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + "/");
  906. get.addRequestHeader("Referer", am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL));
  907. status2 = wc.executeMethod(get);
  908. Log_OC.d("sharer", "getstatus "+status2);
  909. Log_OC.d("sharer", "" + get.getResponseBodyAsString());
  910. for (org.apache.commons.httpclient.Header a : get.getResponseHeaders()) {
  911. Log_OC.d("sharer", a.getName() + ":"+a.getValue());
  912. }
  913. status = wc.executeMethod(post);
  914. for (org.apache.commons.httpclient.Header a : post.getRequestHeaders()) {
  915. Log_OC.d("sharer-h", a.getName() + ":"+a.getValue());
  916. }
  917. for (org.apache.commons.httpclient.Header a : post.getResponseHeaders()) {
  918. Log_OC.d("sharer", a.getName() + ":"+a.getValue());
  919. }
  920. String resp = post.getResponseBodyAsString();
  921. Log_OC.d("share", ""+post.getURI().toString());
  922. Log_OC.d("share", "returned status " + status);
  923. Log_OC.d("share", " " +resp);
  924. if(status != HttpStatus.SC_OK ||resp == null || resp.equals("") || resp.startsWith("false")) {
  925. return;
  926. }
  927. JSONObject jsonObject = new JSONObject (resp);
  928. String jsonStatus = jsonObject.getString("status");
  929. if(!jsonStatus.equals("success")) throw new Exception("Error while sharing file status != success");
  930. String token = jsonObject.getString("data");
  931. String uri = am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + SHARED_PATH + token;
  932. Log_OC.d("Actions:shareFile ok", "url: " + uri);
  933. } catch (Exception e) {
  934. e.printStackTrace();
  935. }
  936. } else if (ocv.compareTo(new OwnCloudVersion(0x030000)) >= 0) {
  937. }
  938. }
  939. }
  940. */
  941. }