Removed unneeded casts to FileDisplayActivity
[pub/Android/ownCloud.git] / src / com / owncloud / android / ui / fragment / FileDetailFragment.java
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
20 import java.lang.ref.WeakReference;
21
22 import android.accounts.Account;
23 import android.content.Intent;
24 import android.os.Bundle;
25 import android.view.LayoutInflater;
26 import android.view.View;
27 import android.view.View.OnClickListener;
28 import android.view.ViewGroup;
29 import android.widget.CheckBox;
30 import android.widget.ImageView;
31 import android.widget.ProgressBar;
32 import android.widget.TextView;
33
34 import com.actionbarsherlock.view.Menu;
35 import com.actionbarsherlock.view.MenuInflater;
36 import com.actionbarsherlock.view.MenuItem;
37 import com.owncloud.android.R;
38 import com.owncloud.android.datamodel.FileDataStorageManager;
39 import com.owncloud.android.datamodel.OCFile;
40 import com.owncloud.android.files.FileMenuFilter;
41 import com.owncloud.android.files.services.FileObserverService;
42 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
43 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
44 import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
45 import com.owncloud.android.ui.activity.FileActivity;
46 import com.owncloud.android.ui.activity.FileDisplayActivity;
47 import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
48 import com.owncloud.android.ui.dialog.EditNameDialog;
49 import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
50 import com.owncloud.android.utils.DisplayUtils;
51 import com.owncloud.android.utils.Log_OC;
52
53
54 /**
55 * This Fragment is used to display the details about a file.
56 *
57 * @author Bartek Przybylski
58 * @author David A. Velasco
59 */
60 public class FileDetailFragment extends FileFragment implements
61 OnClickListener,
62 ConfirmationDialogFragment.ConfirmationDialogFragmentListener, EditNameDialogListener {
63
64 private int mLayout;
65 private View mView;
66 private Account mAccount;
67
68 public ProgressListener mProgressListener;
69
70 private static final String TAG = FileDetailFragment.class.getSimpleName();
71 public static final String FTAG_CONFIRMATION = "REMOVE_CONFIRMATION_FRAGMENT";
72
73
74 /**
75 * Creates an empty details fragment.
76 *
77 * It's necessary to keep a public constructor without parameters; the system uses it when tries to reinstantiate a fragment automatically.
78 */
79 public FileDetailFragment() {
80 super();
81 mAccount = null;
82 mLayout = R.layout.file_details_empty;
83 mProgressListener = null;
84 }
85
86 /**
87 * Creates a details fragment.
88 *
89 * When 'fileToDetail' or 'ocAccount' are null, creates a dummy layout (to use when a file wasn't tapped before).
90 *
91 * @param fileToDetail An {@link OCFile} to show in the fragment
92 * @param ocAccount An ownCloud account; needed to start downloads
93 */
94 public FileDetailFragment(OCFile fileToDetail, Account ocAccount) {
95 super(fileToDetail);
96 mAccount = ocAccount;
97 mLayout = R.layout.file_details_empty;
98 mProgressListener = null;
99 }
100
101
102 @Override
103 public void onCreate(Bundle savedInstanceState) {
104 super.onCreate(savedInstanceState);
105 setHasOptionsMenu(true);
106 }
107
108
109 @Override
110 public View onCreateView(LayoutInflater inflater, ViewGroup container,
111 Bundle savedInstanceState) {
112 //super.onCreateView(inflater, container, savedInstanceState);
113
114 if (savedInstanceState != null) {
115 setFile((OCFile)savedInstanceState.getParcelable(FileActivity.EXTRA_FILE));
116 mAccount = savedInstanceState.getParcelable(FileActivity.EXTRA_ACCOUNT);
117 }
118
119 if(getFile() != null && mAccount != null) {
120 mLayout = R.layout.file_details_fragment;
121 }
122
123 View view = null;
124 view = inflater.inflate(mLayout, null);
125 mView = view;
126
127 if (mLayout == R.layout.file_details_fragment) {
128 mView.findViewById(R.id.fdKeepInSync).setOnClickListener(this);
129 ProgressBar progressBar = (ProgressBar)mView.findViewById(R.id.fdProgressBar);
130 mProgressListener = new ProgressListener(progressBar);
131 mView.findViewById(R.id.fdCancelBtn).setOnClickListener(this);
132 }
133
134 updateFileDetails(false, false);
135 return view;
136 }
137
138 @Override
139 public void onSaveInstanceState(Bundle outState) {
140 super.onSaveInstanceState(outState);
141 outState.putParcelable(FileActivity.EXTRA_FILE, getFile());
142 outState.putParcelable(FileActivity.EXTRA_ACCOUNT, mAccount);
143 }
144
145 @Override
146 public void onStart() {
147 super.onStart();
148 listenForTransferProgress();
149 }
150
151 @Override
152 public void onStop() {
153 super.onStop();
154 leaveTransferProgress();
155 }
156
157
158 @Override
159 public View getView() {
160 return super.getView() == null ? mView : super.getView();
161 }
162
163
164 /**
165 * {@inheritDoc}
166 */
167 @Override
168 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
169 super.onCreateOptionsMenu(menu, inflater);
170 inflater.inflate(R.menu.file_actions_menu, menu);
171
172 /*
173 TODO Maybe should stay here? It's context (fragment) specific
174
175 MenuItem item = menu.findItem(R.id.action_see_details);
176 if (item != null) {
177 item.setVisible(false);
178 item.setEnabled(false);
179 }
180
181 // Send file
182 item = menu.findItem(R.id.action_send_file);
183 boolean sendEnabled = getString(R.string.send_files_to_other_apps).equalsIgnoreCase("on");
184 if (item != null) {
185 if (sendEnabled) {
186 item.setVisible(true);
187 item.setEnabled(true);
188 } else {
189 item.setVisible(false);
190 item.setEnabled(false);
191
192 }
193 }
194 */
195 }
196
197
198 /**
199 * {@inheritDoc}
200 */
201 @Override
202 public void onPrepareOptionsMenu (Menu menu) {
203 super.onPrepareOptionsMenu(menu);
204
205 FileMenuFilter mf = new FileMenuFilter();
206 mf.setFile(getFile());
207 mf.setComponentGetter(mContainerActivity);
208 mf.setAccount(mContainerActivity.getStorageManager().getAccount());
209 mf.setContext(getSherlockActivity());
210 mf.setFragment(this);
211 mf.filter(menu);
212 }
213
214
215 /**
216 * {@inheritDoc}
217 */
218 @Override
219 public boolean onOptionsItemSelected(MenuItem item) {
220 switch (item.getItemId()) {
221 case R.id.action_share_file: {
222 mContainerActivity.getFileOperationsHelper().shareFileWithLink(getFile());
223 return true;
224 }
225 case R.id.action_unshare_file: {
226 mContainerActivity.getFileOperationsHelper().unshareFileWithLink(getFile());
227 return true;
228 }
229 case R.id.action_open_file_with: {
230 mContainerActivity.getFileOperationsHelper().openFile(getFile());
231 return true;
232 }
233 case R.id.action_remove_file: {
234 showDialogToRemoveFile();
235 return true;
236 }
237 case R.id.action_rename_file: {
238 showDialogToRenameFile();
239 return true;
240 }
241 case R.id.action_cancel_download:
242 case R.id.action_cancel_upload: {
243 ((FileDisplayActivity)mContainerActivity).cancelTransference(getFile());
244 return true;
245 }
246 case R.id.action_download_file:
247 case R.id.action_sync_file: {
248 mContainerActivity.getFileOperationsHelper().syncFile(getFile());
249 return true;
250 }
251 case R.id.action_send_file: {
252 // Obtain the file
253 if (!getFile().isDown()) { // Download the file
254 Log_OC.d(TAG, getFile().getRemotePath() + " : File must be downloaded");
255 ((FileDisplayActivity)mContainerActivity).startDownloadForSending(getFile());
256
257 } else {
258 mContainerActivity.getFileOperationsHelper().sendDownloadedFile(getFile());
259 }
260 return true;
261 }
262 default:
263 return false;
264 }
265 }
266
267 @Override
268 public void onClick(View v) {
269 switch (v.getId()) {
270 case R.id.fdKeepInSync: {
271 toggleKeepInSync();
272 break;
273 }
274 case R.id.fdCancelBtn: {
275 ((FileDisplayActivity)mContainerActivity).cancelTransference(getFile());
276 break;
277 }
278 default:
279 Log_OC.e(TAG, "Incorrect view clicked!");
280 }
281 }
282
283
284 private void toggleKeepInSync() {
285 CheckBox cb = (CheckBox) getView().findViewById(R.id.fdKeepInSync);
286 OCFile file = getFile();
287 file.setKeepInSync(cb.isChecked());
288 mContainerActivity.getStorageManager().saveFile(file);
289
290 /// register the OCFile instance in the observer service to monitor local updates;
291 /// if necessary, the file is download
292 Intent intent = new Intent(getActivity().getApplicationContext(),
293 FileObserverService.class);
294 intent.putExtra(FileObserverService.KEY_FILE_CMD,
295 (cb.isChecked()?
296 FileObserverService.CMD_ADD_OBSERVED_FILE:
297 FileObserverService.CMD_DEL_OBSERVED_FILE));
298 intent.putExtra(FileObserverService.KEY_CMD_ARG_FILE, file);
299 intent.putExtra(FileObserverService.KEY_CMD_ARG_ACCOUNT, mAccount);
300 getActivity().startService(intent);
301
302 if (file.keepInSync()) {
303 mContainerActivity.getFileOperationsHelper().syncFile(getFile());
304 }
305 }
306
307 private void showDialogToRemoveFile() {
308 OCFile file = getFile();
309 ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
310 R.string.confirmation_remove_alert,
311 new String[]{file.getFileName()},
312 file.isDown() ? R.string.confirmation_remove_remote_and_local : R.string.confirmation_remove_remote,
313 file.isDown() ? R.string.confirmation_remove_local : -1,
314 R.string.common_cancel);
315 confDialog.setOnConfirmationListener(this);
316 confDialog.show(getFragmentManager(), FTAG_CONFIRMATION);
317 }
318
319
320 private void showDialogToRenameFile() {
321 OCFile file = getFile();
322 String fileName = file.getFileName();
323 int extensionStart = file.isFolder() ? -1 : fileName.lastIndexOf(".");
324 int selectionEnd = (extensionStart >= 0) ? extensionStart : fileName.length();
325 EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), fileName, 0, selectionEnd, this);
326 dialog.show(getFragmentManager(), "nameeditdialog");
327 }
328
329
330 @Override
331 public void onConfirmation(String callerTag) {
332 OCFile file = getFile();
333 if (callerTag.equals(FTAG_CONFIRMATION)) {
334 if (mContainerActivity.getStorageManager().getFileById(file.getFileId()) != null) {
335 mContainerActivity.getFileOperationsHelper().removeFile(file, true);
336 }
337 }
338 }
339
340 @Override
341 public void onNeutral(String callerTag) {
342 OCFile file = getFile();
343 mContainerActivity.getStorageManager().removeFile(file, false, true); // TODO perform in background task / new thread
344 if (file.getStoragePath() != null) {
345 file.setStoragePath(null);
346 updateFileDetails(file, mAccount);
347 }
348 }
349
350 @Override
351 public void onCancel(String callerTag) {
352 Log_OC.d(TAG, "REMOVAL CANCELED");
353 }
354
355
356 /**
357 * Check if the fragment was created with an empty layout. An empty fragment can't show file details, must be replaced.
358 *
359 * @return True when the fragment was created with the empty layout.
360 */
361 public boolean isEmpty() {
362 return (mLayout == R.layout.file_details_empty || getFile() == null || mAccount == null);
363 }
364
365
366 /**
367 * Use this method to signal this Activity that it shall update its view.
368 *
369 * @param file : An {@link OCFile}
370 */
371 public void updateFileDetails(OCFile file, Account ocAccount) {
372 setFile(file);
373 mAccount = ocAccount;
374 updateFileDetails(false, false);
375 }
376
377 /**
378 * Updates the view with all relevant details about that file.
379 *
380 * TODO Remove parameter when the transferring state of files is kept in database.
381 *
382 * @param transferring Flag signaling if the file should be considered as downloading or uploading,
383 * although {@link FileDownloaderBinder#isDownloading(Account, OCFile)} and
384 * {@link FileUploaderBinder#isUploading(Account, OCFile)} return false.
385 *
386 * @param refresh If 'true', try to refresh the whole file from the database
387 */
388 public void updateFileDetails(boolean transferring, boolean refresh) {
389 if (readyToShow()) {
390 FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
391 if (refresh && storageManager != null) {
392 setFile(storageManager.getFileByPath(getFile().getRemotePath()));
393 }
394 OCFile file = getFile();
395
396 // set file details
397 setFilename(file.getFileName());
398 setFiletype(file.getMimetype());
399 setFilesize(file.getFileLength());
400 if(ocVersionSupportsTimeCreated()){
401 setTimeCreated(file.getCreationTimestamp());
402 }
403
404 setTimeModified(file.getModificationTimestamp());
405
406 CheckBox cb = (CheckBox)getView().findViewById(R.id.fdKeepInSync);
407 cb.setChecked(file.keepInSync());
408
409 // configure UI for depending upon local state of the file
410 FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
411 FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
412 if (transferring || (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) || (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file))) {
413 setButtonsForTransferring();
414
415 } else if (file.isDown()) {
416
417 setButtonsForDown();
418
419 } else {
420 // TODO load default preview image; when the local file is removed, the preview remains there
421 setButtonsForRemote();
422 }
423 }
424 getView().invalidate();
425 }
426
427 /**
428 * Checks if the fragment is ready to show details of a OCFile
429 *
430 * @return 'True' when the fragment is ready to show details of a file
431 */
432 private boolean readyToShow() {
433 return (getFile() != null && mAccount != null && mLayout == R.layout.file_details_fragment);
434 }
435
436
437 /**
438 * Updates the filename in view
439 * @param filename to set
440 */
441 private void setFilename(String filename) {
442 TextView tv = (TextView) getView().findViewById(R.id.fdFilename);
443 if (tv != null)
444 tv.setText(filename);
445 }
446
447 /**
448 * Updates the MIME type in view
449 * @param mimetype to set
450 */
451 private void setFiletype(String mimetype) {
452 TextView tv = (TextView) getView().findViewById(R.id.fdType);
453 if (tv != null) {
454 String printableMimetype = DisplayUtils.convertMIMEtoPrettyPrint(mimetype);;
455 tv.setText(printableMimetype);
456 }
457 ImageView iv = (ImageView) getView().findViewById(R.id.fdIcon);
458 if (iv != null) {
459 iv.setImageResource(DisplayUtils.getResourceId(mimetype));
460 }
461 }
462
463 /**
464 * Updates the file size in view
465 * @param filesize in bytes to set
466 */
467 private void setFilesize(long filesize) {
468 TextView tv = (TextView) getView().findViewById(R.id.fdSize);
469 if (tv != null)
470 tv.setText(DisplayUtils.bytesToHumanReadable(filesize));
471 }
472
473 /**
474 * Updates the time that the file was created in view
475 * @param milliseconds Unix time to set
476 */
477 private void setTimeCreated(long milliseconds){
478 TextView tv = (TextView) getView().findViewById(R.id.fdCreated);
479 TextView tvLabel = (TextView) getView().findViewById(R.id.fdCreatedLabel);
480 if(tv != null){
481 tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds));
482 tv.setVisibility(View.VISIBLE);
483 tvLabel.setVisibility(View.VISIBLE);
484 }
485 }
486
487 /**
488 * Updates the time that the file was last modified
489 * @param milliseconds Unix time to set
490 */
491 private void setTimeModified(long milliseconds){
492 TextView tv = (TextView) getView().findViewById(R.id.fdModified);
493 if(tv != null){
494 tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds));
495 }
496 }
497
498 /**
499 * Enables or disables buttons for a file being downloaded
500 */
501 private void setButtonsForTransferring() {
502 if (!isEmpty()) {
503 // let's protect the user from himself ;)
504 getView().findViewById(R.id.fdKeepInSync).setEnabled(false);
505
506 // show the progress bar for the transfer
507 getView().findViewById(R.id.fdProgressBlock).setVisibility(View.VISIBLE);
508 TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
509 progressText.setVisibility(View.VISIBLE);
510 FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
511 FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
512 if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, getFile())) {
513 progressText.setText(R.string.downloader_download_in_progress_ticker);
514 } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, getFile())) {
515 progressText.setText(R.string.uploader_upload_in_progress_ticker);
516 }
517 }
518 }
519
520 /**
521 * Enables or disables buttons for a file locally available
522 */
523 private void setButtonsForDown() {
524 if (!isEmpty()) {
525 getView().findViewById(R.id.fdKeepInSync).setEnabled(true);
526
527 // hides the progress bar
528 getView().findViewById(R.id.fdProgressBlock).setVisibility(View.GONE);
529 TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
530 progressText.setVisibility(View.GONE);
531 }
532 }
533
534 /**
535 * Enables or disables buttons for a file not locally available
536 */
537 private void setButtonsForRemote() {
538 if (!isEmpty()) {
539 getView().findViewById(R.id.fdKeepInSync).setEnabled(true);
540
541 // hides the progress bar
542 getView().findViewById(R.id.fdProgressBlock).setVisibility(View.GONE);
543 TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
544 progressText.setVisibility(View.GONE);
545 }
546 }
547
548
549 /**
550 * In ownCloud 3.X.X and 4.X.X there is a bug that SabreDAV does not return
551 * the time that the file was created. There is a chance that this will
552 * be fixed in future versions. Use this method to check if this version of
553 * ownCloud has this fix.
554 * @return True, if ownCloud the ownCloud version is supporting creation time
555 */
556 private boolean ocVersionSupportsTimeCreated(){
557 /*if(mAccount != null){
558 AccountManager accManager = (AccountManager) getActivity().getSystemService(Context.ACCOUNT_SERVICE);
559 OwnCloudVersion ocVersion = new OwnCloudVersion(accManager
560 .getUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION));
561 if(ocVersion.compareTo(new OwnCloudVersion(0x030000)) < 0) {
562 return true;
563 }
564 }*/
565 return false;
566 }
567
568
569 public void onDismiss(EditNameDialog dialog) {
570 if (dialog.getResult()) {
571 String newFilename = dialog.getNewFilename();
572 Log_OC.d(TAG, "name edit dialog dismissed with new name " + newFilename);
573 mContainerActivity.getFileOperationsHelper().renameFile(getFile(), newFilename);
574 }
575 }
576
577
578 public void listenForTransferProgress() {
579 if (mProgressListener != null) {
580 if (mContainerActivity.getFileDownloaderBinder() != null) {
581 mContainerActivity.getFileDownloaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, getFile());
582 }
583 if (mContainerActivity.getFileUploaderBinder() != null) {
584 mContainerActivity.getFileUploaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, getFile());
585 }
586 }
587 }
588
589
590 public void leaveTransferProgress() {
591 if (mProgressListener != null) {
592 if (mContainerActivity.getFileDownloaderBinder() != null) {
593 mContainerActivity.getFileDownloaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, getFile());
594 }
595 if (mContainerActivity.getFileUploaderBinder() != null) {
596 mContainerActivity.getFileUploaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, getFile());
597 }
598 }
599 }
600
601
602
603 /**
604 * Helper class responsible for updating the progress bar shown for file uploading or downloading
605 *
606 * @author David A. Velasco
607 */
608 private class ProgressListener implements OnDatatransferProgressListener {
609 int mLastPercent = 0;
610 WeakReference<ProgressBar> mProgressBar = null;
611
612 ProgressListener(ProgressBar progressBar) {
613 mProgressBar = new WeakReference<ProgressBar>(progressBar);
614 }
615
616 @Override
617 public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filename) {
618 int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));
619 if (percent != mLastPercent) {
620 ProgressBar pb = mProgressBar.get();
621 if (pb != null) {
622 pb.setProgress(percent);
623 pb.postInvalidate();
624 }
625 }
626 mLastPercent = percent;
627 }
628
629 };
630
631 }