FileDetailFragment.java 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921
  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.app.Activity;
  25. import android.content.BroadcastReceiver;
  26. import android.content.Context;
  27. import android.content.Intent;
  28. import android.content.IntentFilter;
  29. import android.os.Bundle;
  30. import android.os.Handler;
  31. import android.view.LayoutInflater;
  32. import android.view.View;
  33. import android.view.View.OnClickListener;
  34. import android.view.ViewGroup;
  35. import android.widget.CheckBox;
  36. import android.widget.ImageView;
  37. import android.widget.ProgressBar;
  38. import android.widget.TextView;
  39. import android.widget.Toast;
  40. import com.actionbarsherlock.view.Menu;
  41. import com.actionbarsherlock.view.MenuInflater;
  42. import com.actionbarsherlock.view.MenuItem;
  43. import com.owncloud.android.R;
  44. import com.owncloud.android.datamodel.FileDataStorageManager;
  45. import com.owncloud.android.datamodel.OCFile;
  46. import com.owncloud.android.files.services.FileObserverService;
  47. import com.owncloud.android.files.services.FileUploader;
  48. import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
  49. import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
  50. import com.owncloud.android.oc_framework.network.webdav.OnDatatransferProgressListener;
  51. import com.owncloud.android.oc_framework.operations.OnRemoteOperationListener;
  52. import com.owncloud.android.oc_framework.operations.RemoteOperation;
  53. import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
  54. import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode;
  55. import com.owncloud.android.operations.RemoveFileOperation;
  56. import com.owncloud.android.operations.RenameFileOperation;
  57. import com.owncloud.android.operations.SynchronizeFileOperation;
  58. import com.owncloud.android.ui.activity.ConflictsResolveActivity;
  59. import com.owncloud.android.ui.activity.FileActivity;
  60. import com.owncloud.android.ui.activity.FileDisplayActivity;
  61. import com.owncloud.android.ui.dialog.EditNameDialog;
  62. import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
  63. import com.owncloud.android.ui.preview.PreviewImageFragment;
  64. import com.owncloud.android.utils.DisplayUtils;
  65. import com.owncloud.android.utils.Log_OC;
  66. /**
  67. * This Fragment is used to display the details about a file.
  68. *
  69. * @author Bartek Przybylski
  70. * @author David A. Velasco
  71. */
  72. public class FileDetailFragment extends FileFragment implements
  73. OnClickListener,
  74. ConfirmationDialogFragment.ConfirmationDialogFragmentListener, OnRemoteOperationListener, EditNameDialogListener {
  75. private FileFragment.ContainerActivity mContainerActivity;
  76. private int mLayout;
  77. private View mView;
  78. private Account mAccount;
  79. private FileDataStorageManager mStorageManager;
  80. private UploadFinishReceiver mUploadFinishReceiver;
  81. public ProgressListener mProgressListener;
  82. private Handler mHandler;
  83. private RemoteOperation mLastRemoteOperation;
  84. private static final String TAG = FileDetailFragment.class.getSimpleName();
  85. public static final String FTAG_CONFIRMATION = "REMOVE_CONFIRMATION_FRAGMENT";
  86. /**
  87. * Creates an empty details fragment.
  88. *
  89. * It's necessary to keep a public constructor without parameters; the system uses it when tries to reinstantiate a fragment automatically.
  90. */
  91. public FileDetailFragment() {
  92. super();
  93. mAccount = null;
  94. mStorageManager = null;
  95. mLayout = R.layout.file_details_empty;
  96. mProgressListener = null;
  97. }
  98. /**
  99. * Creates a details fragment.
  100. *
  101. * When 'fileToDetail' or 'ocAccount' are null, creates a dummy layout (to use when a file wasn't tapped before).
  102. *
  103. * @param fileToDetail An {@link OCFile} to show in the fragment
  104. * @param ocAccount An ownCloud account; needed to start downloads
  105. */
  106. public FileDetailFragment(OCFile fileToDetail, Account ocAccount) {
  107. super(fileToDetail);
  108. mAccount = ocAccount;
  109. mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment
  110. mLayout = R.layout.file_details_empty;
  111. mProgressListener = null;
  112. }
  113. @Override
  114. public void onCreate(Bundle savedInstanceState) {
  115. super.onCreate(savedInstanceState);
  116. mHandler = new Handler();
  117. setHasOptionsMenu(true);
  118. }
  119. @Override
  120. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  121. Bundle savedInstanceState) {
  122. //super.onCreateView(inflater, container, savedInstanceState);
  123. if (savedInstanceState != null) {
  124. setFile((OCFile)savedInstanceState.getParcelable(FileActivity.EXTRA_FILE));
  125. mAccount = savedInstanceState.getParcelable(FileActivity.EXTRA_ACCOUNT);
  126. }
  127. if(getFile() != null && mAccount != null) {
  128. mLayout = R.layout.file_details_fragment;
  129. }
  130. View view = null;
  131. //view = inflater.inflate(mLayout, container, false);
  132. view = inflater.inflate(mLayout, null);
  133. mView = view;
  134. if (mLayout == R.layout.file_details_fragment) {
  135. mView.findViewById(R.id.fdKeepInSync).setOnClickListener(this);
  136. ProgressBar progressBar = (ProgressBar)mView.findViewById(R.id.fdProgressBar);
  137. mProgressListener = new ProgressListener(progressBar);
  138. mView.findViewById(R.id.fdCancelBtn).setOnClickListener(this);
  139. }
  140. updateFileDetails(false, false);
  141. return view;
  142. }
  143. /**
  144. * {@inheritDoc}
  145. */
  146. @Override
  147. public void onAttach(Activity activity) {
  148. super.onAttach(activity);
  149. try {
  150. mContainerActivity = (ContainerActivity) activity;
  151. } catch (ClassCastException e) {
  152. throw new ClassCastException(activity.toString() + " must implement " + FileDetailFragment.ContainerActivity.class.getSimpleName());
  153. }
  154. }
  155. /**
  156. * {@inheritDoc}
  157. */
  158. @Override
  159. public void onActivityCreated(Bundle savedInstanceState) {
  160. super.onActivityCreated(savedInstanceState);
  161. if (mAccount != null) {
  162. mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());
  163. }
  164. }
  165. @Override
  166. public void onSaveInstanceState(Bundle outState) {
  167. super.onSaveInstanceState(outState);
  168. outState.putParcelable(FileActivity.EXTRA_FILE, getFile());
  169. outState.putParcelable(FileActivity.EXTRA_ACCOUNT, mAccount);
  170. }
  171. @Override
  172. public void onStart() {
  173. super.onStart();
  174. listenForTransferProgress();
  175. }
  176. @Override
  177. public void onResume() {
  178. super.onResume();
  179. mUploadFinishReceiver = new UploadFinishReceiver();
  180. IntentFilter filter = new IntentFilter(FileUploader.getUploadFinishMessage());
  181. getActivity().registerReceiver(mUploadFinishReceiver, filter);
  182. }
  183. @Override
  184. public void onPause() {
  185. super.onPause();
  186. if (mUploadFinishReceiver != null) {
  187. getActivity().unregisterReceiver(mUploadFinishReceiver);
  188. mUploadFinishReceiver = null;
  189. }
  190. }
  191. @Override
  192. public void onStop() {
  193. super.onStop();
  194. leaveTransferProgress();
  195. }
  196. @Override
  197. public View getView() {
  198. return super.getView() == null ? mView : super.getView();
  199. }
  200. /**
  201. * {@inheritDoc}
  202. */
  203. @Override
  204. public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
  205. super.onCreateOptionsMenu(menu, inflater);
  206. inflater.inflate(R.menu.file_actions_menu, menu);
  207. MenuItem item = menu.findItem(R.id.action_see_details);
  208. if (item != null) {
  209. item.setVisible(false);
  210. item.setEnabled(false);
  211. }
  212. }
  213. /**
  214. * {@inheritDoc}
  215. */
  216. @Override
  217. public void onPrepareOptionsMenu (Menu menu) {
  218. super.onPrepareOptionsMenu(menu);
  219. List<Integer> toHide = new ArrayList<Integer>();
  220. List<Integer> toShow = new ArrayList<Integer>();
  221. OCFile file = getFile();
  222. FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
  223. boolean downloading = downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file);
  224. FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
  225. boolean uploading = uploaderBinder != null && uploaderBinder.isUploading(mAccount, getFile());
  226. if (downloading || uploading) {
  227. toHide.add(R.id.action_download_file);
  228. toHide.add(R.id.action_rename_file);
  229. toHide.add(R.id.action_remove_file);
  230. toHide.add(R.id.action_open_file_with);
  231. if (!downloading) {
  232. toHide.add(R.id.action_cancel_download);
  233. toShow.add(R.id.action_cancel_upload);
  234. } else {
  235. toHide.add(R.id.action_cancel_upload);
  236. toShow.add(R.id.action_cancel_download);
  237. }
  238. } else if (file != null && file.isDown()) {
  239. toHide.add(R.id.action_download_file);
  240. toHide.add(R.id.action_cancel_download);
  241. toHide.add(R.id.action_cancel_upload);
  242. toShow.add(R.id.action_rename_file);
  243. toShow.add(R.id.action_remove_file);
  244. toShow.add(R.id.action_open_file_with);
  245. toShow.add(R.id.action_sync_file);
  246. } else if (file != null) {
  247. toHide.add(R.id.action_open_file_with);
  248. toHide.add(R.id.action_cancel_download);
  249. toHide.add(R.id.action_cancel_upload);
  250. toHide.add(R.id.action_sync_file);
  251. toShow.add(R.id.action_rename_file);
  252. toShow.add(R.id.action_remove_file);
  253. toShow.add(R.id.action_download_file);
  254. } else {
  255. toHide.add(R.id.action_open_file_with);
  256. toHide.add(R.id.action_cancel_download);
  257. toHide.add(R.id.action_cancel_upload);
  258. toHide.add(R.id.action_sync_file);
  259. toHide.add(R.id.action_download_file);
  260. toHide.add(R.id.action_rename_file);
  261. toHide.add(R.id.action_remove_file);
  262. }
  263. MenuItem item = null;
  264. for (int i : toHide) {
  265. item = menu.findItem(i);
  266. if (item != null) {
  267. item.setVisible(false);
  268. item.setEnabled(false);
  269. }
  270. }
  271. for (int i : toShow) {
  272. item = menu.findItem(i);
  273. if (item != null) {
  274. item.setVisible(true);
  275. item.setEnabled(true);
  276. }
  277. }
  278. }
  279. /**
  280. * {@inheritDoc}
  281. */
  282. @Override
  283. public boolean onOptionsItemSelected(MenuItem item) {
  284. switch (item.getItemId()) {
  285. case R.id.action_open_file_with: {
  286. mContainerActivity.openFile(getFile());
  287. return true;
  288. }
  289. case R.id.action_remove_file: {
  290. removeFile();
  291. return true;
  292. }
  293. case R.id.action_rename_file: {
  294. renameFile();
  295. return true;
  296. }
  297. case R.id.action_download_file:
  298. case R.id.action_cancel_download:
  299. case R.id.action_cancel_upload:
  300. case R.id.action_sync_file: {
  301. synchronizeFile();
  302. return true;
  303. }
  304. default:
  305. return false;
  306. }
  307. }
  308. @Override
  309. public void onClick(View v) {
  310. switch (v.getId()) {
  311. case R.id.fdKeepInSync: {
  312. toggleKeepInSync();
  313. break;
  314. }
  315. case R.id.fdCancelBtn: {
  316. synchronizeFile();
  317. break;
  318. }
  319. default:
  320. Log_OC.e(TAG, "Incorrect view clicked!");
  321. }
  322. }
  323. private void toggleKeepInSync() {
  324. CheckBox cb = (CheckBox) getView().findViewById(R.id.fdKeepInSync);
  325. OCFile file = getFile();
  326. file.setKeepInSync(cb.isChecked());
  327. mStorageManager.saveFile(file);
  328. /// register the OCFile instance in the observer service to monitor local updates;
  329. /// if necessary, the file is download
  330. Intent intent = new Intent(getActivity().getApplicationContext(),
  331. FileObserverService.class);
  332. intent.putExtra(FileObserverService.KEY_FILE_CMD,
  333. (cb.isChecked()?
  334. FileObserverService.CMD_ADD_OBSERVED_FILE:
  335. FileObserverService.CMD_DEL_OBSERVED_FILE));
  336. intent.putExtra(FileObserverService.KEY_CMD_ARG_FILE, file);
  337. intent.putExtra(FileObserverService.KEY_CMD_ARG_ACCOUNT, mAccount);
  338. getActivity().startService(intent);
  339. if (file.keepInSync()) {
  340. synchronizeFile(); // force an immediate synchronization
  341. }
  342. }
  343. private void removeFile() {
  344. OCFile file = getFile();
  345. ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
  346. R.string.confirmation_remove_alert,
  347. new String[]{file.getFileName()},
  348. file.isDown() ? R.string.confirmation_remove_remote_and_local : R.string.confirmation_remove_remote,
  349. file.isDown() ? R.string.confirmation_remove_local : -1,
  350. R.string.common_cancel);
  351. confDialog.setOnConfirmationListener(this);
  352. confDialog.show(getFragmentManager(), FTAG_CONFIRMATION);
  353. }
  354. private void renameFile() {
  355. OCFile file = getFile();
  356. String fileName = file.getFileName();
  357. int extensionStart = file.isFolder() ? -1 : fileName.lastIndexOf(".");
  358. int selectionEnd = (extensionStart >= 0) ? extensionStart : fileName.length();
  359. EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), fileName, 0, selectionEnd, this);
  360. dialog.show(getFragmentManager(), "nameeditdialog");
  361. }
  362. private void synchronizeFile() {
  363. OCFile file = getFile();
  364. FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
  365. FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
  366. if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) {
  367. downloaderBinder.cancel(mAccount, file);
  368. if (file.isDown()) {
  369. setButtonsForDown();
  370. } else {
  371. setButtonsForRemote();
  372. }
  373. } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file)) {
  374. uploaderBinder.cancel(mAccount, file);
  375. if (!file.fileExists()) {
  376. // TODO make something better
  377. ((FileDisplayActivity)getActivity()).cleanSecondFragment();
  378. } else if (file.isDown()) {
  379. setButtonsForDown();
  380. } else {
  381. setButtonsForRemote();
  382. }
  383. } else {
  384. mLastRemoteOperation = new SynchronizeFileOperation(file, null, mStorageManager, mAccount, true, getActivity());
  385. mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
  386. // update ui
  387. ((FileDisplayActivity) getActivity()).showLoadingDialog();
  388. }
  389. }
  390. @Override
  391. public void onConfirmation(String callerTag) {
  392. OCFile file = getFile();
  393. if (callerTag.equals(FTAG_CONFIRMATION)) {
  394. if (mStorageManager.getFileById(file.getFileId()) != null) {
  395. mLastRemoteOperation = new RemoveFileOperation( file,
  396. true,
  397. mStorageManager);
  398. mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
  399. ((FileDisplayActivity) getActivity()).showLoadingDialog();
  400. }
  401. }
  402. }
  403. @Override
  404. public void onNeutral(String callerTag) {
  405. OCFile file = getFile();
  406. mStorageManager.removeFile(file, false, true); // TODO perform in background task / new thread
  407. if (file.getStoragePath() != null) {
  408. file.setStoragePath(null);
  409. updateFileDetails(file, mAccount);
  410. }
  411. }
  412. @Override
  413. public void onCancel(String callerTag) {
  414. Log_OC.d(TAG, "REMOVAL CANCELED");
  415. }
  416. /**
  417. * Check if the fragment was created with an empty layout. An empty fragment can't show file details, must be replaced.
  418. *
  419. * @return True when the fragment was created with the empty layout.
  420. */
  421. public boolean isEmpty() {
  422. return (mLayout == R.layout.file_details_empty || getFile() == null || mAccount == null);
  423. }
  424. /**
  425. * Use this method to signal this Activity that it shall update its view.
  426. *
  427. * @param file : An {@link OCFile}
  428. */
  429. public void updateFileDetails(OCFile file, Account ocAccount) {
  430. setFile(file);
  431. if (ocAccount != null && (
  432. mStorageManager == null ||
  433. (mAccount != null && !mAccount.equals(ocAccount))
  434. )) {
  435. mStorageManager = new FileDataStorageManager(ocAccount, getActivity().getApplicationContext().getContentResolver());
  436. }
  437. mAccount = ocAccount;
  438. updateFileDetails(false, false);
  439. }
  440. /**
  441. * Updates the view with all relevant details about that file.
  442. *
  443. * TODO Remove parameter when the transferring state of files is kept in database.
  444. *
  445. * TODO REFACTORING! this method called 5 times before every time the fragment is shown!
  446. *
  447. * @param transferring Flag signaling if the file should be considered as downloading or uploading,
  448. * although {@link FileDownloaderBinder#isDownloading(Account, OCFile)} and
  449. * {@link FileUploaderBinder#isUploading(Account, OCFile)} return false.
  450. *
  451. * @param refresh If 'true', try to refresh the hold file from the database
  452. */
  453. public void updateFileDetails(boolean transferring, boolean refresh) {
  454. if (readyToShow()) {
  455. if (refresh && mStorageManager != null) {
  456. setFile(mStorageManager.getFileByPath(getFile().getRemotePath()));
  457. }
  458. OCFile file = getFile();
  459. // set file details
  460. setFilename(file.getFileName());
  461. setFiletype(file.getMimetype());
  462. setFilesize(file.getFileLength());
  463. if(ocVersionSupportsTimeCreated()){
  464. setTimeCreated(file.getCreationTimestamp());
  465. }
  466. setTimeModified(file.getModificationTimestamp());
  467. CheckBox cb = (CheckBox)getView().findViewById(R.id.fdKeepInSync);
  468. cb.setChecked(file.keepInSync());
  469. // configure UI for depending upon local state of the file
  470. //if (FileDownloader.isDownloading(mAccount, mFile.getRemotePath()) || FileUploader.isUploading(mAccount, mFile.getRemotePath())) {
  471. FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
  472. FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
  473. if (transferring || (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) || (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file))) {
  474. setButtonsForTransferring();
  475. } else if (file.isDown()) {
  476. setButtonsForDown();
  477. } else {
  478. // TODO load default preview image; when the local file is removed, the preview remains there
  479. setButtonsForRemote();
  480. }
  481. }
  482. getView().invalidate();
  483. }
  484. /**
  485. * Checks if the fragment is ready to show details of a OCFile
  486. *
  487. * @return 'True' when the fragment is ready to show details of a file
  488. */
  489. private boolean readyToShow() {
  490. return (getFile() != null && mAccount != null && mLayout == R.layout.file_details_fragment);
  491. }
  492. /**
  493. * Updates the filename in view
  494. * @param filename to set
  495. */
  496. private void setFilename(String filename) {
  497. TextView tv = (TextView) getView().findViewById(R.id.fdFilename);
  498. if (tv != null)
  499. tv.setText(filename);
  500. }
  501. /**
  502. * Updates the MIME type in view
  503. * @param mimetype to set
  504. */
  505. private void setFiletype(String mimetype) {
  506. TextView tv = (TextView) getView().findViewById(R.id.fdType);
  507. if (tv != null) {
  508. String printableMimetype = DisplayUtils.convertMIMEtoPrettyPrint(mimetype);;
  509. tv.setText(printableMimetype);
  510. }
  511. ImageView iv = (ImageView) getView().findViewById(R.id.fdIcon);
  512. if (iv != null) {
  513. iv.setImageResource(DisplayUtils.getResourceId(mimetype));
  514. }
  515. }
  516. /**
  517. * Updates the file size in view
  518. * @param filesize in bytes to set
  519. */
  520. private void setFilesize(long filesize) {
  521. TextView tv = (TextView) getView().findViewById(R.id.fdSize);
  522. if (tv != null)
  523. tv.setText(DisplayUtils.bytesToHumanReadable(filesize));
  524. }
  525. /**
  526. * Updates the time that the file was created in view
  527. * @param milliseconds Unix time to set
  528. */
  529. private void setTimeCreated(long milliseconds){
  530. TextView tv = (TextView) getView().findViewById(R.id.fdCreated);
  531. TextView tvLabel = (TextView) getView().findViewById(R.id.fdCreatedLabel);
  532. if(tv != null){
  533. tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds));
  534. tv.setVisibility(View.VISIBLE);
  535. tvLabel.setVisibility(View.VISIBLE);
  536. }
  537. }
  538. /**
  539. * Updates the time that the file was last modified
  540. * @param milliseconds Unix time to set
  541. */
  542. private void setTimeModified(long milliseconds){
  543. TextView tv = (TextView) getView().findViewById(R.id.fdModified);
  544. if(tv != null){
  545. tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds));
  546. }
  547. }
  548. /**
  549. * Enables or disables buttons for a file being downloaded
  550. */
  551. private void setButtonsForTransferring() {
  552. if (!isEmpty()) {
  553. // let's protect the user from himself ;)
  554. getView().findViewById(R.id.fdKeepInSync).setEnabled(false);
  555. // show the progress bar for the transfer
  556. getView().findViewById(R.id.fdProgressBlock).setVisibility(View.VISIBLE);
  557. TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
  558. progressText.setVisibility(View.VISIBLE);
  559. FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
  560. FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
  561. if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, getFile())) {
  562. progressText.setText(R.string.downloader_download_in_progress_ticker);
  563. } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, getFile())) {
  564. progressText.setText(R.string.uploader_upload_in_progress_ticker);
  565. }
  566. }
  567. }
  568. /**
  569. * Enables or disables buttons for a file locally available
  570. */
  571. private void setButtonsForDown() {
  572. if (!isEmpty()) {
  573. getView().findViewById(R.id.fdKeepInSync).setEnabled(true);
  574. // hides the progress bar
  575. getView().findViewById(R.id.fdProgressBlock).setVisibility(View.GONE);
  576. TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
  577. progressText.setVisibility(View.GONE);
  578. }
  579. }
  580. /**
  581. * Enables or disables buttons for a file not locally available
  582. */
  583. private void setButtonsForRemote() {
  584. if (!isEmpty()) {
  585. getView().findViewById(R.id.fdKeepInSync).setEnabled(true);
  586. // hides the progress bar
  587. getView().findViewById(R.id.fdProgressBlock).setVisibility(View.GONE);
  588. TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
  589. progressText.setVisibility(View.GONE);
  590. }
  591. }
  592. /**
  593. * In ownCloud 3.X.X and 4.X.X there is a bug that SabreDAV does not return
  594. * the time that the file was created. There is a chance that this will
  595. * be fixed in future versions. Use this method to check if this version of
  596. * ownCloud has this fix.
  597. * @return True, if ownCloud the ownCloud version is supporting creation time
  598. */
  599. private boolean ocVersionSupportsTimeCreated(){
  600. /*if(mAccount != null){
  601. AccountManager accManager = (AccountManager) getActivity().getSystemService(Context.ACCOUNT_SERVICE);
  602. OwnCloudVersion ocVersion = new OwnCloudVersion(accManager
  603. .getUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION));
  604. if(ocVersion.compareTo(new OwnCloudVersion(0x030000)) < 0) {
  605. return true;
  606. }
  607. }*/
  608. return false;
  609. }
  610. /**
  611. * Once the file upload has finished -> update view
  612. *
  613. * Being notified about the finish of an upload is necessary for the next sequence:
  614. * 1. Upload a big file.
  615. * 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
  616. * of its containing folder; the the server includes it in the PROPFIND requests although it's not fully upload.
  617. * 3. Click the file in the list to see its details.
  618. * 4. Wait for the upload finishes; at this moment, the details view must be refreshed to enable the action buttons.
  619. */
  620. private class UploadFinishReceiver extends BroadcastReceiver {
  621. @Override
  622. public void onReceive(Context context, Intent intent) {
  623. String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME);
  624. if (!isEmpty() && accountName.equals(mAccount.name)) {
  625. boolean uploadWasFine = intent.getBooleanExtra(FileUploader.EXTRA_UPLOAD_RESULT, false);
  626. String uploadRemotePath = intent.getStringExtra(FileUploader.EXTRA_REMOTE_PATH);
  627. boolean renamedInUpload = getFile().getRemotePath().equals(intent.getStringExtra(FileUploader.EXTRA_OLD_REMOTE_PATH));
  628. if (getFile().getRemotePath().equals(uploadRemotePath) ||
  629. renamedInUpload) {
  630. if (uploadWasFine) {
  631. setFile(mStorageManager.getFileByPath(uploadRemotePath));
  632. }
  633. if (renamedInUpload) {
  634. String newName = (new File(uploadRemotePath)).getName();
  635. Toast msg = Toast.makeText(getActivity().getApplicationContext(), String.format(getString(R.string.filedetails_renamed_in_upload_msg), newName), Toast.LENGTH_LONG);
  636. msg.show();
  637. }
  638. getSherlockActivity().removeStickyBroadcast(intent); // not the best place to do this; a small refactorization of BroadcastReceivers should be done
  639. updateFileDetails(false, false); // it updates the buttons; must be called although !uploadWasFine; interrupted uploads still leave an incomplete file in the server
  640. // Force the preview if the file is an image
  641. if (uploadWasFine && PreviewImageFragment.canBePreviewed(getFile())) {
  642. ((FileDisplayActivity) mContainerActivity).startImagePreview(getFile());
  643. }
  644. }
  645. }
  646. }
  647. }
  648. public void onDismiss(EditNameDialog dialog) {
  649. if (dialog.getResult()) {
  650. String newFilename = dialog.getNewFilename();
  651. Log_OC.d(TAG, "name edit dialog dismissed with new name " + newFilename);
  652. mLastRemoteOperation = new RenameFileOperation( getFile(),
  653. mAccount,
  654. newFilename,
  655. new FileDataStorageManager(mAccount, getActivity().getContentResolver()));
  656. mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
  657. ((FileDisplayActivity) getActivity()).showLoadingDialog();
  658. }
  659. }
  660. /**
  661. * {@inheritDoc}
  662. */
  663. @Override
  664. public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
  665. if (operation.equals(mLastRemoteOperation)) {
  666. if (operation instanceof RemoveFileOperation) {
  667. onRemoveFileOperationFinish((RemoveFileOperation)operation, result);
  668. } else if (operation instanceof RenameFileOperation) {
  669. onRenameFileOperationFinish((RenameFileOperation)operation, result);
  670. } else if (operation instanceof SynchronizeFileOperation) {
  671. onSynchronizeFileOperationFinish((SynchronizeFileOperation)operation, result);
  672. }
  673. }
  674. }
  675. private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {
  676. ((FileDisplayActivity) getActivity()).dismissLoadingDialog();
  677. if (result.isSuccess()) {
  678. Toast msg = Toast.makeText(getActivity().getApplicationContext(), R.string.remove_success_msg, Toast.LENGTH_LONG);
  679. msg.show();
  680. ((FileDisplayActivity)getActivity()).cleanSecondFragment();
  681. } else {
  682. Toast msg = Toast.makeText(getActivity(), R.string.remove_fail_msg, Toast.LENGTH_LONG);
  683. msg.show();
  684. if (result.isSslRecoverableException()) {
  685. // TODO show the SSL warning dialog
  686. }
  687. }
  688. }
  689. private void onRenameFileOperationFinish(RenameFileOperation operation, RemoteOperationResult result) {
  690. ((FileDisplayActivity) getActivity()).dismissLoadingDialog();
  691. if (result.isSuccess()) {
  692. updateFileDetails(((RenameFileOperation)operation).getFile(), mAccount);
  693. mContainerActivity.onFileStateChanged();
  694. } else {
  695. if (result.getCode().equals(ResultCode.INVALID_LOCAL_FILE_NAME)) {
  696. Toast msg = Toast.makeText(getActivity(), R.string.rename_local_fail_msg, Toast.LENGTH_LONG);
  697. msg.show();
  698. // TODO throw again the new rename dialog
  699. } if (result.getCode().equals(ResultCode.INVALID_CHARACTER_IN_NAME)) {
  700. Toast msg = Toast.makeText(getActivity(), R.string.filename_forbidden_characters, Toast.LENGTH_LONG);
  701. msg.show();
  702. } else {
  703. Toast msg = Toast.makeText(getActivity(), R.string.rename_server_fail_msg, Toast.LENGTH_LONG);
  704. msg.show();
  705. if (result.isSslRecoverableException()) {
  706. // TODO show the SSL warning dialog
  707. }
  708. }
  709. }
  710. }
  711. private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation, RemoteOperationResult result) {
  712. ((FileDisplayActivity) getActivity()).dismissLoadingDialog();
  713. OCFile file = getFile();
  714. if (!result.isSuccess()) {
  715. if (result.getCode() == ResultCode.SYNC_CONFLICT) {
  716. Intent i = new Intent(getActivity(), ConflictsResolveActivity.class);
  717. i.putExtra(ConflictsResolveActivity.EXTRA_FILE, file);
  718. i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, mAccount);
  719. startActivity(i);
  720. }
  721. if (file.isDown()) {
  722. setButtonsForDown();
  723. } else {
  724. setButtonsForRemote();
  725. }
  726. } else {
  727. if (operation.transferWasRequested()) {
  728. setButtonsForTransferring();
  729. mContainerActivity.onFileStateChanged(); // this is not working; FileDownloader won't do NOTHING at all until this method finishes, so
  730. // checking the service to see if the file is downloading results in FALSE
  731. } else {
  732. Toast msg = Toast.makeText(getActivity(), R.string.sync_file_nothing_to_do_msg, Toast.LENGTH_LONG);
  733. msg.show();
  734. if (file.isDown()) {
  735. setButtonsForDown();
  736. } else {
  737. setButtonsForRemote();
  738. }
  739. }
  740. }
  741. }
  742. public void listenForTransferProgress() {
  743. if (mProgressListener != null) {
  744. if (mContainerActivity.getFileDownloaderBinder() != null) {
  745. mContainerActivity.getFileDownloaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, getFile());
  746. }
  747. if (mContainerActivity.getFileUploaderBinder() != null) {
  748. mContainerActivity.getFileUploaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, getFile());
  749. }
  750. }
  751. }
  752. public void leaveTransferProgress() {
  753. if (mProgressListener != null) {
  754. if (mContainerActivity.getFileDownloaderBinder() != null) {
  755. mContainerActivity.getFileDownloaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, getFile());
  756. }
  757. if (mContainerActivity.getFileUploaderBinder() != null) {
  758. mContainerActivity.getFileUploaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, getFile());
  759. }
  760. }
  761. }
  762. /**
  763. * Helper class responsible for updating the progress bar shown for file uploading or downloading
  764. *
  765. * @author David A. Velasco
  766. */
  767. private class ProgressListener implements OnDatatransferProgressListener {
  768. int mLastPercent = 0;
  769. WeakReference<ProgressBar> mProgressBar = null;
  770. ProgressListener(ProgressBar progressBar) {
  771. mProgressBar = new WeakReference<ProgressBar>(progressBar);
  772. }
  773. @Override
  774. public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filename) {
  775. int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));
  776. if (percent != mLastPercent) {
  777. ProgressBar pb = mProgressBar.get();
  778. if (pb != null) {
  779. pb.setProgress(percent);
  780. pb.postInvalidate();
  781. }
  782. }
  783. mLastPercent = percent;
  784. }
  785. };
  786. }